text: /*	$OpenBSD: odyssey.c,v 1.11 2014/03/09 10:01:25 miod Exp $ */
  /*
   * Copyright (c) 2009, 2010 Joel Sing <jsing@openbsd.org>
   *
   * Permission to use, copy, modify, and distribute this software for any
   * purpose with or without fee is hereby granted, provided that the above
   * copyright notice and this permission notice appear in all copies.
   *
   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   */
  
  /*
   * Driver for the SGI VPro (aka Odyssey) Graphics Card.
   */
  
  /*
   * The details regarding the design and operation of this hardware, along with
   * the necessary magic numbers, are only available thanks to the reverse
   * engineering work undertaken by Stanislaw Skowronek <skylark@linux-mips.org>.
   */
  
  #include <sys/param.h>
  #include <sys/systm.h>
  #include <sys/kernel.h>
  #include <sys/device.h>
  #include <sys/types.h>
  #include <sys/malloc.h>
  
  #include <machine/autoconf.h>
  
  #include <mips64/arcbios.h>
  
  #include <sgi/dev/gl.h>
  #include <sgi/xbow/odysseyreg.h>
  #include <sgi/xbow/odysseyvar.h>
  #include <sgi/xbow/widget.h>
  #include <sgi/xbow/xbow.h>
  #include <sgi/xbow/xbowdevs.h>
  
  #include <dev/wscons/wsconsio.h>
  #include <dev/wscons/wsdisplayvar.h>
  #include <dev/rasops/rasops.h>
  
  /*
   * Colourmap data.
   */
  struct  odyssey_cmap {
  	u_int8_t cm_red[256];
  	u_int8_t cm_green[256];
  	u_int8_t cm_blue[256];
  };
  
  /*
   * Screen data.
   */
  struct odyssey_screen {
  	struct device *sc;		/* Back pointer. */
  
  	struct rasops_info ri;		/* Screen raster display info. */
  	struct odyssey_cmap cmap;	/* Display colour map. */
  	long attr;			/* Rasops attributes. */
  
  	int width;			/* Width in pixels. */
  	int height;			/* Height in pixels. */
  	int depth;			/* Colour depth in bits. */
  	int linebytes;			/* Bytes per line. */
  };
  
  struct odyssey_softc {
  	struct device		sc_dev;
  
  	struct mips_bus_space	iot_store;
  	bus_space_tag_t		iot;
  	bus_space_handle_t	ioh;
  
  	int console;				/* Is this the console? */
  	int screens;				/* No of screens allocated. */
  
  	struct odyssey_screen	*curscr;	/* Current screen. */
  };
  
  int	odyssey_match(struct device *, void *, void *);
  void	odyssey_attach(struct device *, struct device *, void *);
  
  int	odyssey_is_console(struct xbow_attach_args *);
  
  void	odyssey_cmd_wait(struct odyssey_softc *);
  void	odyssey_data_wait(struct odyssey_softc *);
  void	odyssey_cmd_flush(struct odyssey_softc *, int);
  
  void	odyssey_setup(struct odyssey_softc *);
  void	odyssey_init_screen(struct odyssey_screen *);
  
  /*
   * Colour map handling for indexed modes.
   */
  int	odyssey_getcmap(struct odyssey_cmap *, struct wsdisplay_cmap *);
  int	odyssey_putcmap(struct odyssey_cmap *, struct wsdisplay_cmap *);
  
  /*
   * Hardware acceleration for rasops.
   */
  void	odyssey_rop(struct odyssey_softc *, int, int, int, int, int, int);
  void	odyssey_copyrect(struct odyssey_softc *, int, int, int, int, int, int);
  void	odyssey_fillrect(struct odyssey_softc *, int, int, int, int, u_int);
  int	odyssey_do_cursor(struct rasops_info *);
  int	odyssey_putchar(void *, int, int, u_int, long);
  int	odyssey_copycols(void *, int, int, int, int);
  int	odyssey_erasecols(void *, int, int, int, long);
  int	odyssey_copyrows(void *, int, int, int);
  int	odyssey_eraserows(void *, int, int, long);
  
  u_int32_t ieee754_sp(uint);
  
  /*
   * Interfaces for wscons.
   */
  int 	odyssey_ioctl(void *, u_long, caddr_t, int, struct proc *);
  paddr_t odyssey_mmap(void *, off_t, int);
  int	odyssey_alloc_screen(void *, const struct wsscreen_descr *, void **,
  	    int *, int *, long *);
  void	odyssey_free_screen(void *, void *);
  int	odyssey_show_screen(void *, void *, int, void (*)(void *, int, int),
  	    void *);
  int	odyssey_load_font(void *, void *, struct wsdisplay_font *);
  int	odyssey_list_font(void *, struct wsdisplay_font *);
  
  static struct odyssey_screen odyssey_consdata;
  static struct odyssey_softc odyssey_cons_sc;
  
  struct wsscreen_descr odyssey_stdscreen = {
  	"std",			/* Screen name. */
  };
  
  struct wsdisplay_accessops odyssey_accessops = {
  	.ioctl = odyssey_ioctl,
  	.mmap = odyssey_mmap,
  	.alloc_screen = odyssey_alloc_screen,
  	.free_screen = odyssey_free_screen,
  	.show_screen = odyssey_show_screen,
  	.load_font = odyssey_load_font,
  	.list_font = odyssey_list_font
  };
  
  const struct wsscreen_descr *odyssey_scrlist[] = {
  	&odyssey_stdscreen
  };
  
  struct wsscreen_list odyssey_screenlist = {
  	nitems(odyssey_scrlist), odyssey_scrlist
  };
  
  const struct cfattach odyssey_ca = {
  	sizeof(struct odyssey_softc), odyssey_match, odyssey_attach,
  };
  
  struct cfdriver odyssey_cd = {
  	NULL, "odyssey", DV_DULL,
  };
  
  int
  odyssey_match(struct device *parent, void *match, void *aux)
  {
  	struct xbow_attach_args *xaa = aux;
  
  	if (xaa->xaa_vendor == XBOW_VENDOR_SGI2 &&
  	    xaa->xaa_product == XBOW_PRODUCT_SGI2_ODYSSEY)
  		return 1;
  
  	return 0;
  }
  
  void
  odyssey_attach(struct device *parent, struct device *self, void *aux)
  {
  	struct xbow_attach_args *xaa = aux;
  	struct wsemuldisplaydev_attach_args waa;
  	struct odyssey_softc *sc = (void *)self;
  	struct odyssey_screen *screen;
  
  	if (strncmp(bios_graphics, "alive", 5) != 0) {
  		printf(" device has not been setup by firmware!\n");
  		return;
  	}
  
  	printf(" revision %d\n", xaa->xaa_revision);
  
  	/*
  	 * Create a copy of the bus space tag.
  	 */
  	bcopy(xaa->xaa_iot, &sc->iot_store, sizeof(struct mips_bus_space));
  	sc->iot = &sc->iot_store;
  
  	/* Setup bus space mappings. */
  	if (bus_space_map(sc->iot, ODYSSEY_REG_OFFSET, ODYSSEY_REG_SIZE,
  	    BUS_SPACE_MAP_LINEAR, &sc->ioh)) {
  		printf("failed to map bus space!\n");
  		return;
  	}
  
  	if (odyssey_is_console(xaa)) {
  		/*
  		 * Setup has already been done via odyssey_cnattach().
  		 */
  		screen = &odyssey_consdata;
         		sc->curscr = screen;
  		sc->curscr->sc = (void *)sc;
  		sc->console = 1;
  	} else {
  		/*
  		 * Setup screen data.
  		 */
  		sc->curscr = malloc(sizeof(struct odyssey_screen), M_DEVBUF,
  		    M_NOWAIT);
  		if (sc->curscr == NULL) {
  			printf("failed to allocate screen memory!\n");
  			return;
  		}
  		sc->curscr->sc = (void *)sc;
  		screen = sc->curscr;
  
  		/* Setup hardware and clear screen. */
  		odyssey_setup(sc);
  		odyssey_fillrect(sc, 0, 0, 1280, 1024, 0x000000);
  		odyssey_cmd_flush(sc, 0);
  
  		/* Set screen defaults. */
  		screen->width = 1280;
  		screen->height = 1024;
  		screen->depth = 32;
  		screen->linebytes = screen->width * screen->depth / 8;
  
  		odyssey_init_screen(screen);
  	}
  
  	waa.console = sc->console;
  	waa.scrdata = &odyssey_screenlist;
  	waa.accessops = &odyssey_accessops;
  	waa.accesscookie = screen;
  	waa.defaultscreens = 0;
  	config_found(self, &waa, wsemuldisplaydevprint);
  }
  
  void
  odyssey_init_screen(struct odyssey_screen *screen)
  {
  	u_char *colour;
  	int i;
  
  	/*
  	 * Initialise screen.
  	 */
  
  	/* Initialise rasops. */
  	memset(&screen->ri, 0, sizeof(struct rasops_info));
  
  	screen->ri.ri_flg = RI_CENTER;
  	screen->ri.ri_depth = screen->depth;
  	screen->ri.ri_width = screen->width;
  	screen->ri.ri_height = screen->height;
  	screen->ri.ri_stride = screen->linebytes;
  
  	if (screen->depth == 32) {
  		screen->ri.ri_bpos = 16;
  		screen->ri.ri_bnum = 8;
  		screen->ri.ri_gpos = 8;
  		screen->ri.ri_gnum = 8;
  		screen->ri.ri_rpos = 0;
  		screen->ri.ri_rnum = 8;
  	} else if (screen->depth == 16) {
  		screen->ri.ri_rpos = 10;
  		screen->ri.ri_rnum = 5;
  		screen->ri.ri_gpos = 5;
  		screen->ri.ri_gnum = 5;
  		screen->ri.ri_bpos = 0;
  		screen->ri.ri_bnum = 5;
  	}
  
  	rasops_init(&screen->ri, screen->height / 8, screen->width / 8);
  
  	/*
  	 * Initialise colourmap, if required.
  	 */
  	if (screen->depth == 8) {
  		for (i = 0; i < 16; i++) {
  			colour = (u_char *)&rasops_cmap[i * 3];
  			screen->cmap.cm_red[i] = colour[0];
  			screen->cmap.cm_green[i] = colour[1];
  			screen->cmap.cm_blue[i] = colour[2];
  		}
  		for (i = 240; i < 256; i++) {
  			colour = (u_char *)&rasops_cmap[i * 3];
  			screen->cmap.cm_red[i] = colour[0];
  			screen->cmap.cm_green[i] = colour[1];
  			screen->cmap.cm_blue[i] = colour[2];
  		}
  	}
  
  	screen->ri.ri_hw = screen->sc;
  
  	screen->ri.ri_ops.putchar = odyssey_putchar;
  	screen->ri.ri_do_cursor = odyssey_do_cursor;
  	screen->ri.ri_ops.copyrows = odyssey_copyrows;
  	screen->ri.ri_ops.copycols = odyssey_copycols;
  	screen->ri.ri_ops.eraserows = odyssey_eraserows;
  	screen->ri.ri_ops.erasecols = odyssey_erasecols;
  
  	odyssey_stdscreen.ncols = screen->ri.ri_cols;
  	odyssey_stdscreen.nrows = screen->ri.ri_rows;
  	odyssey_stdscreen.textops = &screen->ri.ri_ops;
  	odyssey_stdscreen.fontwidth = screen->ri.ri_font->fontwidth;
  	odyssey_stdscreen.fontheight = screen->ri.ri_font->fontheight;
  	odyssey_stdscreen.capabilities = screen->ri.ri_caps;
  }
  
  /*
   * Hardware initialisation.
   */
  void
  odyssey_setup(struct odyssey_softc *sc)
  {
  	u_int64_t val;
  	int i;
  
  	/* Initialise Buzz Graphics Engine. */
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x20008003);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x21008010);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x22008000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x23008002);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x2400800c);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x2500800e);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x27008000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x28008000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x290080d6);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x2a0080e0);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x2c0080ea);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x2e008380);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x2f008000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x30008000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x31008000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x32008000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x33008000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x34008000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x35008000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x310081e0);
  	odyssey_cmd_flush(sc, 0);
  
  	/* Initialise Buzz X-Form. */
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x9080bda2);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x3f800000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x3f000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0xbf800000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x4e000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x40400000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x4e000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x4d000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x34008000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x9080bdc8);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x3f800000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x3f000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x3f800000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x3f000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x3f800000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x3f800000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x34008010);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x908091df);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x3f800000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x34008000);
  	odyssey_cmd_flush(sc, 0);
  
  	/* Initialise Buzz Raster. */
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x0001203b);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00001000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00001000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00001000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00001000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x0001084a);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000080);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000080);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00010845);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x000000ff);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x000076ff);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x0001141b);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000001);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00011c16);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x03000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00010404);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00011023);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00ff0ff0);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00ff0ff0);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x000000ff);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00011017);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00002000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000050);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x20004950);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x0001204b);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x004ff3ff);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00ffffff);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00ffffff);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00ffffff);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	odyssey_cmd_flush(sc, 0);
  
  	/*
  	 * Initalise Pixel Blaster & Jammer.
  	 */
  	for (i = 0; i < 32; i++) {
  		if ((i & 0xf) == 0)
  			odyssey_data_wait(sc);
  
  		bus_space_write_8(sc->iot, sc->ioh, ODYSSEY_DATA_FIFO,
  		    ((0x30000001ULL | ((0x2900 + i) << 14)) << 32) |
  		    0x905215a6);
  	}
  
  	odyssey_data_wait(sc);
  	bus_space_write_8(sc->iot, sc->ioh, ODYSSEY_DATA_FIFO,
  	    ((0x30000001ULL | (0x2581 << 14)) << 32) | 0x0);
  
  	/* Gamma ramp. */
  	for (i = 0; i < 0x600; i++) {
  		if ((i & 0xf) == 0)
  			odyssey_data_wait(sc);
  
  		if (i < 0x200)
  			val = i >> 2;
  		else if (i < 0x300)
  			val = ((i - 0x200) >> 1) + 0x80;
  		else
  			val = ((i - 0x300) >> 0) + 0x100;
  
  		val = (val << 20) | (val << 10) | val;
  
  		bus_space_write_8(sc->iot, sc->ioh, ODYSSEY_DATA_FIFO,
  		    ((0x30000001ULL | ((0x1a00 + i) << 14)) << 32) | val);
  	}
  }
  
  void
  odyssey_cmd_wait(struct odyssey_softc *sc)
  {
  	u_int32_t val, timeout = 1000000;
  
  	val = bus_space_read_4(sc->iot, sc->ioh, ODYSSEY_STATUS);
  	while ((val & ODYSSEY_STATUS_CMD_FIFO_LOW) == 0) {
  		delay(1);
  		if (--timeout == 0) {
  			printf("odyssey: timeout waiting for command fifo!\n");
  			return;
  		}
  		val = bus_space_read_4(sc->iot, sc->ioh, ODYSSEY_STATUS);
  	}
  }
  
  void
  odyssey_data_wait(struct odyssey_softc *sc)
  {
  	u_int32_t val, timeout = 1000000;
  
  	val = bus_space_read_4(sc->iot, sc->ioh, ODYSSEY_DBE_STATUS);
  	while ((val & 0x7f) > 0) {
  		delay(1);
  		if (--timeout == 0) {
  			printf("odyssey: timeout waiting for data fifo!\n");
  			return;
  		}
  		val = bus_space_read_4(sc->iot, sc->ioh, ODYSSEY_DBE_STATUS);
  	}
  }
  
  void
  odyssey_cmd_flush(struct odyssey_softc *sc, int quick)
  {
  
  	odyssey_cmd_wait(sc);
  
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00010443);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x000000fa);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00010046);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00010046);
  
  	if (quick)
  		return;
  
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00010019);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00010443);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000096);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00010046);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00010046);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00010046);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00010046);
  
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00010443);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x000000fa);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00010046);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00010046);
  }
  
  /*
   * Interfaces for wscons.
   */
  
  int
  odyssey_ioctl(void *v, u_long cmd, caddr_t data, int flags, struct proc *p)
  {
  	struct odyssey_screen *screen = (struct odyssey_screen *)v;
  	int rc;
  
  	switch (cmd) {
  	case WSDISPLAYIO_GTYPE:
  		*(u_int *)data = WSDISPLAY_TYPE_ODYSSEY;
  		break;
  
  	case WSDISPLAYIO_GINFO:
  	{
  		struct wsdisplay_fbinfo *fb = (struct wsdisplay_fbinfo *)data;
  
  		fb->height = screen->height;
  		fb->width = screen->width;
  		fb->depth = screen->depth;
  		fb->cmsize = screen->depth == 8 ? 256 : 0;
  	}
  		break;
  
  	case WSDISPLAYIO_LINEBYTES:
  		*(u_int *)data = screen->linebytes;
  		break;
  
  	case WSDISPLAYIO_GETCMAP:
  		if (screen->depth == 8) {
  			struct wsdisplay_cmap *cm =
  			    (struct wsdisplay_cmap *)data;
  
  			rc = odyssey_getcmap(&screen->cmap, cm);
  			if (rc != 0)
  				return (rc);
  		}
  		break;
  
  	case WSDISPLAYIO_PUTCMAP:
  		if (screen->depth == 8) {
  			struct wsdisplay_cmap *cm =
  			    (struct wsdisplay_cmap *)data;
  
  			rc = odyssey_putcmap(&screen->cmap, cm);
  			if (rc != 0)
  				return (rc);
  		}
  		break;
  
  	case WSDISPLAYIO_GVIDEO:
  	case WSDISPLAYIO_SVIDEO:
  		/* Handled by the upper layer. */
  		break;
  
  	default:
  		return (-1);
  	}
  
  	return (0);
  }
  
  int
  odyssey_getcmap(struct odyssey_cmap *cm, struct wsdisplay_cmap *rcm)
  {
  	u_int index = rcm->index, count = rcm->count;
  	int rc;
  
  	if (index >= 256 || count > 256 - index)
  		return (EINVAL);
  
  	if ((rc = copyout(&cm->cm_red[index], rcm->red, count)) != 0)
  		return (rc);
  	if ((rc = copyout(&cm->cm_green[index], rcm->green, count)) != 0)
  		return (rc);
  	if ((rc = copyout(&cm->cm_blue[index], rcm->blue, count)) != 0)
  		return (rc);
  
  	return (0);
  }
  
  int
  odyssey_putcmap(struct odyssey_cmap *cm, struct wsdisplay_cmap *rcm)
  {
  	u_int index = rcm->index, count = rcm->count;
  	int rc;
  
  	if (index >= 256 || count > 256 - index)
  		return (EINVAL);
  
  	if ((rc = copyin(rcm->red, &cm->cm_red[index], count)) != 0)
  		return (rc);
  	if ((rc = copyin(rcm->green, &cm->cm_green[index], count)) != 0)
  		return (rc);
  	if ((rc = copyin(rcm->blue, &cm->cm_blue[index], count)) != 0)
  		return (rc);
  
  	return (0);
  }
  
  paddr_t
  odyssey_mmap(void *v, off_t offset, int protection)
  {
  	return (-1);
  }
  
  int
  odyssey_alloc_screen(void *v, const struct wsscreen_descr *type,
      void **cookiep, int *curxp, int *curyp, long *attrp)
  {
  	struct odyssey_screen *screen = (struct odyssey_screen *)v;
  	struct odyssey_softc *sc = (struct odyssey_softc *)screen->sc;
  
  	/* We do not allow multiple consoles at the moment. */
  	if (sc->screens > 0)
  		return (ENOMEM);
  
  	sc->screens++;
  
  	/* Return rasops_info via cookie. */
  	*cookiep = &screen->ri;
  
  	/* Move cursor to top left of screen. */
  	*curxp = 0;
  	*curyp = 0;
  
  	/* Correct screen attributes. */
  	screen->ri.ri_ops.alloc_attr(&screen->ri, 0, 0, 0, attrp);
  	screen->attr = *attrp;
  
  	return (0);
  }
  
  void
  odyssey_free_screen(void *v, void *cookie)
  {
  	/* We do not allow multiple consoles at the moment. */
  }
  
  int
  odyssey_show_screen(void *v, void *cookie, int waitok,
      void (*cb)(void *, int, int), void *cbarg)
  {
  	/* We do not allow multiple consoles at the moment. */
  	return (0);
  }
  
  int
  odyssey_load_font(void *v, void *emulcookie, struct wsdisplay_font *font)
  {
  	struct odyssey_screen *screen = (struct odyssey_screen *)v;
  
  	return rasops_load_font(&screen->ri, emulcookie, font);
  }
  
  int
  odyssey_list_font(void *v, struct wsdisplay_font *font)
  {
  	struct odyssey_screen *screen = (struct odyssey_screen *)v;
  
  	return rasops_list_font(&screen->ri, font);
  }
  
  /*
   * Hardware accelerated functions.
   */
  
  void
  odyssey_rop(struct odyssey_softc *sc, int x, int y, int w, int h, int op, int c)
  {
  	/* Setup raster operation. */
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00010404);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00100000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, OPENGL_LOGIC_OP);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, op);
  	odyssey_cmd_flush(sc, 1);
  
  	odyssey_fillrect(sc, x, y, w, h, c);
  
  	/* Return to copy mode. */
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00010404);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00100000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, OPENGL_LOGIC_OP);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO,
  	    OPENGL_LOGIC_OP_COPY);
  	odyssey_cmd_flush(sc, 1);
  }
  
  void
  odyssey_copyrect(struct odyssey_softc *sc, int sx, int sy, int dx, int dy,
      int w, int h)
  {
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00010658);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00120000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00002031);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00002000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, sx | (sy << 16));
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x80502050);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, w | (h << 16));
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x82223042);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00002000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, dx | (dy << 16));
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x3222204b);
  
  	odyssey_cmd_flush(sc, 1);
  }
  
  void
  odyssey_fillrect(struct odyssey_softc *sc, int x, int y, int w, int h, u_int c)
  {
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, OPENGL_BEGIN);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, OPENGL_QUADS);
  
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, OPENGL_COLOR_3UB);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, c & 0xff);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, (c >> 8) & 0xff);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, (c >> 16) & 0xff);
  
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, OPENGL_VERTEX_2I);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, x);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, y);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, OPENGL_VERTEX_2I);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, x + w);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, y);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, OPENGL_VERTEX_2I);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, x + w);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, y + h);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, OPENGL_VERTEX_2I);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, x);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, y + h);
  
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, OPENGL_END);
  
  	odyssey_cmd_flush(sc, 1);
  }
  
  int
  odyssey_do_cursor(struct rasops_info *ri)
  {
  	struct odyssey_softc *sc = ri->ri_hw;
  	struct odyssey_screen *screen = sc->curscr;
  	int y, x, w, h, fg, bg;
  
  	w = ri->ri_font->fontwidth;
  	h = ri->ri_font->fontheight;
  	x = ri->ri_xorigin + ri->ri_ccol * w;
  	y = ri->ri_yorigin + ri->ri_crow * h;
  
  	ri->ri_ops.unpack_attr(ri, screen->attr, &fg, &bg, NULL);
  
  	odyssey_rop(sc, x, y, w, h, OPENGL_LOGIC_OP_XOR, ri->ri_devcmap[fg]);
  	odyssey_cmd_flush(sc, 0);
  
  	return 0;
  }
  
  int
  odyssey_putchar(void *cookie, int row, int col, u_int uc, long attr)
  {
  	struct rasops_info *ri = cookie;
  	struct odyssey_softc *sc = ri->ri_hw;
  	struct wsdisplay_font *font = ri->ri_font;
  	int bg, fg, ul, i, j, ci, l;
  	uint x, y, w, h;
  	u_int8_t *fontbitmap;
  	u_int chunk;
  
  	w = ri->ri_font->fontwidth;
  	h = ri->ri_font->fontheight;
  	x = ri->ri_xorigin + col * w;
  	y = ri->ri_yorigin + row * h;
  
  	fontbitmap = (u_int8_t *)(font->data + (uc - font->firstchar) *
  	    ri->ri_fontscale);
  	ri->ri_ops.unpack_attr(ri, attr, &fg, &bg, &ul);
  
  	/* Handle spaces with a single fillrect. */
  	if (uc == ' ') {
  		odyssey_fillrect(sc, x, y, w, h, ri->ri_devcmap[bg]);
  		odyssey_cmd_flush(sc, 0);
  		return 0;
  	}
  
  	odyssey_fillrect(sc, x, y, w, h, 0xff0000);
  
  	/* Setup pixel painting. */
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00010405);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00002400);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, OPENGL_COLOR_3UB);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00011453);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000002);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	odyssey_cmd_flush(sc, 0);
  
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x2900812f);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, OPENGL_BEGIN);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x0000000a);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0xcf80a92f);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, ieee754_sp(x));
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, ieee754_sp(y));
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO,
  	    ieee754_sp(x + font->fontwidth));
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO,
  	    ieee754_sp(y + font->fontheight));
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, OPENGL_VERTEX_2I);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00004570);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x0f00104c);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000071);
  
  	for (i = font->fontheight; i != 0; i--) {
  
  		/* Get bitmap for current line. */
  		if (font->fontwidth <= 8)
  			chunk = *fontbitmap;
  		else
  			chunk = *(u_int16_t *)fontbitmap;
  		fontbitmap += font->stride;
  
  		/* Handle underline. */
  		if (ul && i == 1)
  			chunk = 0xffff;
  
  		/* Draw character. */
  		bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO,
  		    0x00004570);
  		bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO,
  		    0x0fd1104c);
  		bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO,
  		    0x00000071);
  
  		w = font->fontwidth;
  		l = 0;
  
  		for (j = 0; j < font->fontwidth; j++) {
  
  			if (l == 0) {
  
  				l = (w > 14 ? 14 : w);
  				w -= 14;
  
  				/* Number of pixels. */
  				bus_space_write_4(sc->iot, sc->ioh,
  				    ODYSSEY_CMD_FIFO, (0x00014011 |
  				    (l << 10)));
  
  			}
  
  			if (font->fontwidth > 8)
  				ci = (chunk & (1 << (16 - j)) ? fg : bg);
  			else
  				ci = (chunk & (1 << (8 - j)) ? fg : bg);
  
  			bus_space_write_4(sc->iot, sc->ioh,
  			    ODYSSEY_CMD_FIFO, ri->ri_devcmap[ci]);
  
  			l--;
  		}
  	}
  
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, OPENGL_END);
  	odyssey_cmd_flush(sc, 1);
  
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x290080d6);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00011453);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00000000);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00010405);
  	bus_space_write_4(sc->iot, sc->ioh, ODYSSEY_CMD_FIFO, 0x00002000);
  	odyssey_cmd_flush(sc, 0);
  
  	return 0;
  }
  
  int
  odyssey_copycols(void *cookie, int row, int src, int dst, int num)
  {
  	struct rasops_info *ri = cookie;
  	struct odyssey_softc *sc = ri->ri_hw;
  	int i;
  
  	if (src < dst) {
  
  		/* We cannot control copy direction, so copy col by col. */
  		for (i = num - 1; i >= 0; i--)
  			odyssey_copyrect(sc,
  			    ri->ri_xorigin + (src + i) * ri->ri_font->fontwidth,
  			    ri->ri_yorigin + row * ri->ri_font->fontheight,
  			    ri->ri_xorigin + (dst + i) * ri->ri_font->fontwidth,
  			    ri->ri_yorigin + row * ri->ri_font->fontheight,
  			    ri->ri_font->fontwidth, ri->ri_font->fontheight);
  
  	} else {
  
  		odyssey_copyrect(sc,
  		    ri->ri_xorigin + src * ri->ri_font->fontwidth,
  		    ri->ri_yorigin + row * ri->ri_font->fontheight,
  		    ri->ri_xorigin + dst * ri->ri_font->fontwidth,
  		    ri->ri_yorigin + row * ri->ri_font->fontheight,
  		    num * ri->ri_font->fontwidth, ri->ri_font->fontheight);
  
  	}
  
  	odyssey_cmd_flush(sc, 0);
  
  	return 0;
  }
  
  int
  odyssey_erasecols(void *cookie, int row, int col, int num, long attr)
  {
  	struct rasops_info *ri = cookie;
  	struct odyssey_softc *sc = ri->ri_hw;
  	int bg, fg;
  
  	ri->ri_ops.unpack_attr(cookie, attr, &fg, &bg, NULL);
  
  	row *= ri->ri_font->fontheight;
  	col *= ri->ri_font->fontwidth;
  	num *= ri->ri_font->fontwidth;
  
  	odyssey_fillrect(sc, ri->ri_xorigin + col, ri->ri_yorigin + row,
  	    num, ri->ri_font->fontheight, ri->ri_devcmap[bg]);
  	odyssey_cmd_flush(sc, 0);
  
  	return 0;
  }
  
  int
  odyssey_copyrows(void *cookie, int src, int dst, int num)
  {
  	struct rasops_info *ri = cookie;
  	struct odyssey_softc *sc = ri->ri_hw;
  	int i;
  
  	if (src < dst) {
  
  		/* We cannot control copy direction, so copy row by row. */
  		for (i = num - 1; i >= 0; i--)
  			odyssey_copyrect(sc, ri->ri_xorigin, ri->ri_yorigin +
  			    (src + i) * ri->ri_font->fontheight,
  			    ri->ri_xorigin, ri->ri_yorigin +
  			    (dst + i) * ri->ri_font->fontheight,
  			    ri->ri_emuwidth, ri->ri_font->fontheight);
  
  	} else {
  
  		odyssey_copyrect(sc, ri->ri_xorigin,
  		    ri->ri_yorigin + src * ri->ri_font->fontheight,
  		    ri->ri_xorigin,
  		    ri->ri_yorigin + dst * ri->ri_font->fontheight,
  		    ri->ri_emuwidth, num * ri->ri_font->fontheight);
  
  	}
  
  	odyssey_cmd_flush(sc, 0);
  
  	return 0;
  }
  
  int
  odyssey_eraserows(void *cookie, int row, int num, long attr)
  {
  	struct rasops_info *ri = cookie;
  	struct odyssey_softc *sc = ri->ri_hw;
  	int x, y, w, bg, fg;
  
  	ri->ri_ops.unpack_attr(cookie, attr, &fg, &bg, NULL);
  
  	if ((num == ri->ri_rows) && ISSET(ri->ri_flg, RI_FULLCLEAR)) {
  		num = ri->ri_height;
  		x = y = 0;
  		w = ri->ri_width;
  	} else {
  		num *= ri->ri_font->fontheight;
  		x = ri->ri_xorigin;
  		y = ri->ri_yorigin + row * ri->ri_font->fontheight;
  		w = ri->ri_emuwidth;
  	}
  
  	odyssey_fillrect(sc, x, y, w, num, ri->ri_devcmap[bg]);
  	odyssey_cmd_flush(sc, 0);
  
  	return 0;
  }
  
  u_int32_t
  ieee754_sp(uint v)
  {
  	u_int8_t exp = 0;
  	int i = 12;	/* 0 <= v < 2048 */
  
  	/*
  	 * Convert a small integer to IEEE754 single precision floating point:
  	 *
  	 * 	Sign - 1 bit
  	 * 	Exponent - 8 bits (with 2^(8-1)-1 = 127 bias)
  	 * 	Fraction - 23 bits
  	 */
  
  	/* Determine shift for fraction. */
  	while (i && (v & (1 << --i)) == 0);
  
  	if (v != 0)
  		exp = 127 + i;
  
  	return (exp << 23) | ((v << (23 - i)) & 0x7fffff);
  }
  
  /*
   * Console support.
   */
  
  int
  odyssey_cnprobe()
  {
  	u_int32_t wid, vendor, product;
  
  	/* Probe for Odyssey graphics card. */
  	if (xbow_widget_id(console_output.nasid, console_output.widget,
  	    &wid) != 0)
  		return 0;
  
  	vendor = WIDGET_ID_VENDOR(wid);
  	product = WIDGET_ID_PRODUCT(wid);
  
  	if (vendor != XBOW_VENDOR_SGI2 || product != XBOW_PRODUCT_SGI2_ODYSSEY)
  		return 0;
  
  	if (strncmp(bios_graphics, "alive", 5) != 0)
  		return 0;
  
  	return 1;
  }
  
  int
  odyssey_cnattach()
  {
  	struct odyssey_softc *sc;
  	struct odyssey_screen *screen;
  	int rc;
  
  	sc = &odyssey_cons_sc;
  	screen = &odyssey_consdata;
  	sc->curscr = screen;
  	sc->curscr->sc = (void *)sc;
  
  	/* Build bus space accessor. */
  	xbow_build_bus_space(&sc->iot_store, console_output.nasid,
  	    console_output.widget);
  	sc->iot = &sc->iot_store;
  
  	/* Setup bus space mappings. */
  	rc = bus_space_map(sc->iot, ODYSSEY_REG_OFFSET, ODYSSEY_REG_SIZE,
  	    BUS_SPACE_MAP_LINEAR, &sc->ioh);
  	if (rc != 0)
  		return rc;
  
  	/* Setup hardware and clear screen. */
  	odyssey_setup(sc);
  	odyssey_fillrect(sc, 0, 0, 1280, 1024, 0x000000);
  	odyssey_cmd_flush(sc, 0);
  
  	/* Set screen defaults. */
  	screen->width = 1280;
  	screen->height = 1024;
  	screen->depth = 32;
  	screen->linebytes = screen->width * screen->depth / 8;
  
  	odyssey_init_screen(screen);
  
  	/*
  	 * Attach wsdisplay.
  	 */
  	screen->ri.ri_ops.alloc_attr(&screen->ri, 0, 0, 0, &screen->attr);
  	wsdisplay_cnattach(&odyssey_stdscreen, &screen->ri, 0, 0, screen->attr);
  
  	return 0;
  }
  
  int
  odyssey_is_console(struct xbow_attach_args *xaa)
  {
  	return xaa->xaa_nasid == console_output.nasid &&
  	    xaa->xaa_widget == console_output.widget;
  }
  