NetBSD-5.0.2/sys/arch/i386/bios/vesafb.c

Compare this file to the similar file:
Show the results in this format:

/* $NetBSD: vesafb.c,v 1.31 2008/07/09 20:41:02 joerg Exp $ */

/*-
 * Copyright (c) 2006 Jared D. McNeill <jmcneill@invisible.ca>
 * All rights reserved.
 *
 * Hardware scrolling added in 2007 by Reinoud Zandijk <reinoud@NetBSD.org>
 *
 * 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. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *        This product includes software developed by the NetBSD
 *        Foundation, Inc. and its contributors.
 * 4. Neither the name of The NetBSD Foundation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */


#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: vesafb.c,v 1.31 2008/07/09 20:41:02 joerg Exp $");

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

#include <uvm/uvm_extern.h>

#include <machine/frame.h>
#include <machine/kvm86.h>
#include <machine/bus.h>

#if notyet
#include <machine/bioscall.h>
#endif

#include "opt_vesafb.h"

#include <arch/i386/bios/vesabios.h>
#include <arch/i386/bios/vesabiosreg.h>
#include <arch/i386/bios/vesafbvar.h>

#ifdef VESAFB_DISABLE_TEXT
#include <sys/reboot.h>
#define DISABLESPLASH (boothowto & (RB_SINGLE | RB_USERCONF | RB_ASKNAME | \
	    AB_VERBOSE | AB_DEBUG) )
#endif

MALLOC_DEFINE(M_VESAFB, "vesafb", "vesafb shadow framebuffer");

static int vesafb_match(device_t, cfdata_t, void *);
static void vesafb_attach(device_t, device_t, void *);

struct wsscreen_descr vesafb_stdscreen = {
	"fb",
	0, 0,
	NULL,
	8, 16,
	0,	/* capabilities */
	NULL,	/* modecookie */
};

static int	vesafb_ioctl(void *, void *, u_long, void *, int,
		    struct lwp *);
static paddr_t	vesafb_mmap(void *, void *, off_t, int);
static void	vesafb_show_screen_cb(struct vcons_screen *);
static void	vesafb_init_screen(void *, struct vcons_screen *,
					int, long *);

static void	vesafb_init(struct vesafb_softc *, int);

static int	vesafb_svideo(struct vesafb_softc *, int);
static int	vesafb_gvideo(struct vesafb_softc *, u_int *);
static int	vesafb_putcmap(struct vesafb_softc *,
				    struct wsdisplay_cmap *);
static int	vesafb_getcmap(struct vesafb_softc *,
				    struct wsdisplay_cmap *);

static void	vesafb_set_palette(struct vesafb_softc *, int, struct paletteentry);

static void	vesafb_display_on(device_t);
static void	vesafb_display_standby(device_t);
static void	vesafb_display_suspend(device_t);
static void	vesafb_display_off(device_t);
static void	vesafb_display_reduced(device_t);

struct wsdisplay_accessops vesafb_accessops = {
	vesafb_ioctl,
	vesafb_mmap,
	NULL,	/* alloc_screen */
	NULL,	/* free_screen */
	NULL,	/* show_screen */
	NULL,	/* load_font */
	NULL,	/* pollc */
	NULL,	/* scroll */
};

static struct vcons_screen vesafb_console_screen;

const struct wsscreen_descr *_vesafb_scrlist[] = {
	&vesafb_stdscreen,
};

struct wsscreen_list vesafb_screenlist = {
	sizeof(_vesafb_scrlist) / sizeof(struct wsscreen_descr *),
	_vesafb_scrlist
};

CFATTACH_DECL_NEW(vesafb, sizeof(struct vesafb_softc),
    vesafb_match, vesafb_attach, NULL, NULL);

static int
vesafb_match(device_t parent, cfdata_t match, void *aux)
{
	struct vesabiosdev_attach_args *vaa = aux;

	if (strcmp(vaa->vbaa_type, "raster"))
		return (0);

	return (1);
}

static void
vesafb_attach(device_t parent, device_t self, void *aux)
{
	struct vesafb_softc *sc = device_private(self);
	struct vesabiosdev_attach_args *vaa = aux;
	unsigned char *buf;
	struct trapframe tf;
	int res, i, j;
	long defattr;
	struct modeinfoblock *mi;
	struct rasops_info *ri;
	struct wsemuldisplaydev_attach_args aa;
	bus_space_handle_t h;

	sc->sc_dev = self;

	aprint_naive("\n");
	aprint_normal(": VESA frame buffer\n");

	buf = kvm86_bios_addpage(0x2000);
	if (!buf) {
		aprint_error_dev(self, "kvm86_bios_addpage(0x2000) failed\n");
		return;
	}
	sc->sc_buf = buf;

	if (vaa->vbaa_nmodes == 0)
		goto out;

	sc->sc_nscreens = 1; /* XXX console */
	sc->sc_wsmode = WSDISPLAYIO_MODE_EMUL;
	sc->sc_mode = vaa->vbaa_modes[0]; /* XXX */
	sc->sc_pm = 0;
	sc->sc_pmstates = 0;
	sc->sc_isconsole = 0;
	mi = NULL;
	j = 0;

	/* XXX */
	for (i = 0; i < vaa->vbaa_nmodes; i++) {
		memset(&tf, 0, sizeof(struct trapframe));
		tf.tf_eax = 0x4f01; /* function code */
		tf.tf_ecx = vaa->vbaa_modes[i];
		tf.tf_vm86_es = 0;
		tf.tf_edi = 0x2000; /* buf ptr */

		res = kvm86_bioscall(0x10, &tf);
		if (res || (tf.tf_eax & 0xff) != 0x4f) {
			aprint_error_dev(self, "vbecall: res=%d, ax=%x\n",
			    res, tf.tf_eax);
			goto out;
		}
		mi = (struct modeinfoblock *)buf;

		if (mi->XResolution == VESAFB_WIDTH &&
		    mi->YResolution == VESAFB_HEIGHT &&
		    mi->BitsPerPixel == VESAFB_DEPTH) {
			sc->sc_mode = vaa->vbaa_modes[i];
			break;
		}
	}

	if (i == vaa->vbaa_nmodes) {
		aprint_error_dev(self, "no supported mode found\n");
		goto out;
	}

	sc->sc_mi = *mi;

	/* Check for power management support */
	memset(&tf, 0, sizeof(struct trapframe));
	tf.tf_eax = 0x4f10; /* function code */
	tf.tf_ebx = 0;
	tf.tf_edi = 0x2000; /* buf ptr */

	res = kvm86_bioscall(0x10, &tf);
	if (res || (tf.tf_eax & 0xff) != 0x4f)
		sc->sc_pm = 0; /* power management not supported */
	else {
		sc->sc_pm = 1; /* power management is supported */
		sc->sc_pmver = tf.tf_ebx & 0xff;
		sc->sc_pmstates = (tf.tf_ebx >> 8);
	}

	ri = &vesafb_console_screen.scr_ri;
	memset(ri, 0, sizeof(struct rasops_info));

	vcons_init(&sc->sc_vd, sc, &vesafb_stdscreen,
	    &vesafb_accessops);
	sc->sc_vd.init_screen = vesafb_init_screen;
	sc->sc_vd.show_screen_cb = vesafb_show_screen_cb;

	aprint_normal_dev(self, "fb %dx%dx%d @0x%x\n",
	       mi->XResolution, mi->YResolution,
	       mi->BitsPerPixel, mi->PhysBasePtr);

	if (vaa->vbaa_vbeversion >= 0x300) {
		sc->sc_scrollscreens = mi->LinNumberOfImagePages;
	} else {
		sc->sc_scrollscreens = mi->NumberOfImagePages;
	}
	if (sc->sc_scrollscreens == 0)
		sc->sc_scrollscreens = 1;

	/* XXX disable hardware scrolling for now; kvm86_call() can trap */
	sc->sc_scrollscreens = 1;

	sc->sc_screensize = mi->YResolution * mi->BytesPerScanLine;
	sc->sc_fbsize = sc->sc_scrollscreens * sc->sc_screensize;

	aprint_normal_dev(self, "%d Kb memory reported, %d screens possible\n",
	       sc->sc_fbsize / 1024,
	       sc->sc_scrollscreens);

	if (sc->sc_scrollscreens == 1)
		aprint_normal_dev(self, "one screen, so hardware scrolling not "
			"possible\n");

	if (sc->sc_pm) {
		aprint_normal_dev(self, "VBE/PM %d.%d\n",
		    (sc->sc_pmver >> 4), sc->sc_pmver & 0xf);
	}

	res = _x86_memio_map(X86_BUS_SPACE_MEM, mi->PhysBasePtr,
			      sc->sc_fbsize,		/* was sc_screensize */
			      BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE,
			      &h);
	if (res) {
		aprint_error_dev(self, "framebuffer mapping failed\n");
		goto out;
	}
	sc->sc_fbstart = bus_space_vaddr(X86_BUS_SPACE_MEM, h);
	sc->sc_bits = sc->sc_fbstart;

#ifdef VESAFB_SHADOW_FB
	sc->sc_shadowbits = malloc(sc->sc_screensize,
				   M_VESAFB, M_NOWAIT);
	if (sc->sc_shadowbits == NULL) {
		aprint_error_dev(self, "unable to allocate %d bytes for shadowfb\n",
		    sc->sc_screensize);
		/* Not fatal; we'll just have to continue without shadowfb */
	}
#endif

	/* initialise our display wide settings */
#ifdef VESAFB_SHADOW_FB
	sc->sc_displ_bits   = sc->sc_shadowbits;
	sc->sc_displ_hwbits = sc->sc_bits;
#else
	sc->sc_displ_bits   = sc->sc_bits;
	sc->sc_displ_hwbits = NULL;
#endif /* !VESAFB_SHADOW_FB */

	vesafb_init(sc, 1);

	vesafb_console_screen.scr_flags |= VCONS_SCREEN_IS_STATIC;
	vcons_init_screen(&sc->sc_vd, &vesafb_console_screen, 1, &defattr);
#ifdef SPLASHSCREEN
/*
 * If system isn't going to go multiuser, or user has requested to see
 * boot text, don't render splash screen immediately
 */
	if (DISABLESPLASH)	  
#endif
		vcons_redraw_screen(&vesafb_console_screen);

	vesafb_stdscreen.ncols = ri->ri_cols;
	vesafb_stdscreen.nrows = ri->ri_rows;
	vesafb_stdscreen.textops = &ri->ri_ops;
	vesafb_stdscreen.capabilities = ri->ri_caps;
	vesafb_stdscreen.modecookie = NULL;

	wsdisplay_cnattach(&vesafb_stdscreen, ri, 0, 0, defattr);

#ifdef SPLASHSCREEN
	sc->sc_si.si_depth = ri->ri_depth;
	sc->sc_si.si_bits = ri->ri_bits;
	sc->sc_si.si_hwbits = ri->ri_hwbits;
	sc->sc_si.si_width = ri->ri_width;
	sc->sc_si.si_height = ri->ri_height;
	sc->sc_si.si_stride = ri->ri_stride;
	sc->sc_si.si_fillrect = NULL;
 	if (!DISABLESPLASH)
		splash_render(&sc->sc_si, SPLASH_F_CENTER|SPLASH_F_FILL);
#endif

#ifdef SPLASHSCREEN_PROGRESS
	sc->sc_sp.sp_top = (mi->YResolution / 8) * 7;
	sc->sc_sp.sp_width = (mi->XResolution / 4) * 3;
	sc->sc_sp.sp_left = (mi->XResolution - sc->sc_sp.sp_width) / 2;
	sc->sc_sp.sp_height = 20;
	sc->sc_sp.sp_state = -1;
	sc->sc_sp.sp_si = &sc->sc_si;
	splash_progress_init(&sc->sc_sp);
#endif

	aa.console = 1; /* XXX */
	aa.scrdata = &vesafb_screenlist;
	aa.accessops = &vesafb_accessops;
	aa.accesscookie = &sc->sc_vd;

#ifdef VESAFB_DISABLE_TEXT
	if (!DISABLESPLASH)
		SCREEN_DISABLE_DRAWING(&vesafb_console_screen);
#endif /* !VESAFB_DISABLE_TEXT */

	sc->sc_isconsole = 1;

	if (!pmf_device_register(self, NULL, NULL))
		aprint_error_dev(self, "couldn't establish power handler\n");
	else if (!pmf_class_display_register(self))
		aprint_error_dev(self, "couldn't set display class\n");

	if (!pmf_event_register(self, PMFE_DISPLAY_ON, vesafb_display_on, true))
		aprint_error_dev(self, "couldn't register DISPLAY ON event\n");
	if ((sc->sc_pmstates & 1) != 0 &&
	    !pmf_event_register(self, PMFE_DISPLAY_STANDBY,
				vesafb_display_standby, true))
		aprint_error_dev(self,
		    "couldn't register DISPLAY STANDBY event\n");
	if ((sc->sc_pmstates & 2) != 0 &&
	    !pmf_event_register(self, PMFE_DISPLAY_SUSPEND,
				vesafb_display_suspend, true))
		aprint_error_dev(self,
		    "couldn't register DISPLAY SUSPEND event\n");
	if ((sc->sc_pmstates & 4) != 0 &&
	    !pmf_event_register(self, PMFE_DISPLAY_OFF,
				vesafb_display_off, true))
		aprint_error_dev(self,
		    "couldn't register DISPLAY OFF event\n");
	if ((sc->sc_pmstates & 8) != 0 &&
	    !pmf_event_register(self, PMFE_DISPLAY_REDUCED,
				vesafb_display_reduced, true))
		aprint_error_dev(self,
		    "couldn't register DISPLAY REDUCED event\n");

	config_found(self, &aa, wsemuldisplaydevprint);

out:
	return;
}

static int
vesafb_ioctl(void *v, void *vs, u_long cmd, void *data,
    int flag, struct lwp *l)
{
	struct vcons_data *vd;
	struct vesafb_softc *sc;
	struct wsdisplay_fbinfo *fb;

	vd = (struct vcons_data *)v;
	sc = (struct vesafb_softc *)vd->cookie;

	switch (cmd) {
	case WSDISPLAYIO_GTYPE:
		*(u_int *)data = WSDISPLAY_TYPE_VESA;
		return 0;
	case WSDISPLAYIO_GINFO:
		if (vd->active != NULL) {
			fb = (struct wsdisplay_fbinfo *)data;
			fb->width = sc->sc_mi.XResolution;
			fb->height = sc->sc_mi.YResolution;
			fb->depth = sc->sc_mi.BitsPerPixel;
			fb->cmsize = 256;
			return 0;
		} else
			return ENODEV;
	case WSDISPLAYIO_GVIDEO:
		if (sc->sc_pm)
			return vesafb_gvideo(sc, (u_int *)data);
		else
			return ENODEV;
	case WSDISPLAYIO_SVIDEO:
		if (sc->sc_pm) {
			u_int on = *(u_int *)data;
			if (on == WSDISPLAYIO_VIDEO_OFF)
				return vesafb_svideo(sc, 4);
			else
				return vesafb_svideo(sc, 0);
		} else
			return ENODEV;
	case WSDISPLAYIO_GETCMAP:
		if (sc->sc_mi.BitsPerPixel == 8)
			return vesafb_getcmap(sc,
			    (struct wsdisplay_cmap *)data);
		else
			return EINVAL;
	case WSDISPLAYIO_PUTCMAP:
		if (sc->sc_mi.BitsPerPixel == 8)
			return vesafb_putcmap(sc,
			    (struct wsdisplay_cmap *)data);
		else
			return EINVAL;
	case WSDISPLAYIO_LINEBYTES:
		*(u_int *)data = sc->sc_mi.BytesPerScanLine;
		return 0;
	case WSDISPLAYIO_SMODE:
		{
			int new_mode = *(int *)data;
			if (new_mode != sc->sc_wsmode) {
				sc->sc_wsmode = new_mode;
				if (new_mode == WSDISPLAYIO_MODE_EMUL) {
					/* vesafb_init(sc, 0); */
					vcons_redraw_screen(vd->active);
				}
			}
		}
		return 0;
	case WSDISPLAYIO_SSPLASH:
#if defined(SPLASHSCREEN)
		if (*(int *)data == 1) {
			SCREEN_DISABLE_DRAWING(&vesafb_console_screen);
			splash_render(&sc->sc_si, SPLASH_F_CENTER|SPLASH_F_FILL);
		} else
			SCREEN_ENABLE_DRAWING(&vesafb_console_screen);
		return 0;
#else
		return ENODEV;
#endif
	case WSDISPLAYIO_SPROGRESS:
#if defined(SPLASHSCREEN) && defined(SPLASHSCREEN_PROGRESS)
		sc->sc_sp.sp_force = 1;
		splash_progress_update(&sc->sc_sp);
		sc->sc_sp.sp_force = 0;
		return 0;
#else
		return ENODEV;
#endif
	}

	return EPASSTHROUGH;
}

static paddr_t
vesafb_mmap(void *v, void *vs, off_t offset, int prot)
{
	struct vcons_data *vd;
	struct vesafb_softc *sc;
	paddr_t pa;

	vd = (struct vcons_data *)v;
	sc = (struct vesafb_softc *)vd->cookie;

	/* XXX */
	if (offset >= 0 && offset <
	    sc->sc_mi.YResolution*sc->sc_mi.BytesPerScanLine) {
		pa = bus_space_mmap(X86_BUS_SPACE_MEM, sc->sc_mi.PhysBasePtr,
		    offset, prot, BUS_SPACE_MAP_LINEAR);
		return pa;
	}

	return -1;
}

/* called back by vcons on screen change; needed for VT display sharing */
static void
vesafb_show_screen_cb(struct vcons_screen *scr)
{
	struct vesafb_softc *sc;
	struct rasops_info *ri;

	sc = (struct vesafb_softc *) scr->scr_cookie;
	ri = &scr->scr_ri;

	/* protect against roque values) */
	if ((sc == NULL) || (ri == NULL))
		return;

	/* set our rasops info to match the display's global state (VTs!) */
	ri->ri_bits   = sc->sc_displ_bits;
	ri->ri_hwbits = sc->sc_displ_hwbits;
}

static void
vv_copyrows(void *id, int srcrow, int dstrow, int nrows)
{
	static int working = 1;
	struct trapframe tf;
	struct rasops_info *ri = id;
	struct vcons_screen *scr = (struct vcons_screen *) ri->ri_hw;
	struct vesafb_softc *sc  = (struct vesafb_softc *) scr->scr_cookie;
	uint32_t displ_offset;
	uint8_t *src, *dst, *hwbits, *cur_hwbits, *hiwater, *lowater;
	int fontheight, offset, linesz, size, height;
	int scrollup, scrolldown, res;

	/* set our rasops info to match the display's global state (VTs!) */
	ri->ri_bits   = sc->sc_displ_bits;
	ri->ri_hwbits = sc->sc_displ_hwbits;

	/* fontheight = ri->ri_font->fontheight; */
	fontheight = 16;

	/* All movements are done in multiples of character heights */
	height = fontheight * nrows;
	size   = height * ri->ri_stride;
	linesz = fontheight * ri->ri_stride;
	offset = (srcrow - dstrow) * linesz;

	/* check if we are full screen scrolling */
	scrollup   = (srcrow + nrows >= ri->ri_rows);
	scrolldown = (dstrow + nrows >= ri->ri_rows);

	if (working && (scrollup || scrolldown))  {
		lowater = sc->sc_fbstart;
		hiwater = lowater + sc->sc_fbsize - sc->sc_screensize;
#ifdef VESAFB_SHADOW_FB
		hwbits = ri->ri_hwbits;
#else
		hwbits = ri->ri_bits;
#endif
		cur_hwbits = hwbits;
		hwbits += offset;
		if (hwbits > hiwater) {
			/* offset is positive */
			memmove(lowater, ri->ri_bits + offset,
				sc->sc_screensize - offset);
			hwbits = lowater;
		}
		if (hwbits < lowater) {
			/* offset is negative */
			memmove(hiwater - offset,
				ri->ri_bits,
				sc->sc_screensize + offset);
			hwbits = hiwater;
		}
		/* program VESA frame buffer start */
		displ_offset = (hwbits - sc->sc_fbstart);
		memset(&tf, 0, sizeof(struct trapframe));
		tf.tf_eax = 0x4f07; /* function code */
		tf.tf_ebx = 0x00;   /* set display immediately */
		tf.tf_ecx = 0;					/* hpixels */
		tf.tf_edx = displ_offset / ri->ri_stride;	/* lineno  */
		tf.tf_vm86_es = 0;

		res = kvm86_bioscall(0x10, &tf);
		if (res || (tf.tf_eax & 0xff) != 0x4f) {
			working = 0;
			aprint_error_dev(sc->sc_dev, "vbecall: res=%d, ax=%x\n",
			    res, tf.tf_eax);
			hwbits = cur_hwbits;
			goto out;
		}
#ifdef VESAFB_SHADOW_FB
		ri->ri_hwbits = hwbits;
		src = ri->ri_bits + srcrow * fontheight * ri->ri_stride;
		dst = ri->ri_bits + dstrow * fontheight * ri->ri_stride;
		memmove(dst, src, size);
#else
		ri->ri_bits = hwbits;
#endif

#if 0
		/* wipe out remains of the screen if nessisary */
		if (ri->ri_emuheight != ri->ri_height)
			vv_eraserows(id, ri->ri_rows, 1, 0);
#endif
		/* remember display's global state (VTs!) */
		sc->sc_displ_bits   = ri->ri_bits;
		sc->sc_displ_hwbits = ri->ri_hwbits;
		return;
	}
out:
	/* just deligate to the origional routine */
	sc->sc_orig_copyrows(id, srcrow, dstrow, nrows);
}

static void
vesafb_init_screen(void *c, struct vcons_screen *scr, int existing,
    long *defattr)
{
	struct vesafb_softc *sc;
	struct rasops_info *ri;
	struct modeinfoblock *mi;

	sc = (struct vesafb_softc *)c;
	mi = &sc->sc_mi;
	ri = &scr->scr_ri;
	ri->ri_flg = RI_CENTER;
	ri->ri_depth = mi->BitsPerPixel;
	ri->ri_width = mi->XResolution;
	ri->ri_height = mi->YResolution;
	ri->ri_emuheight = ri->ri_height;	/* XXX always ? */
	ri->ri_stride = mi->BytesPerScanLine;

	/* set our rasops info to match the display's global state (VTs!) */
	ri->ri_bits   = sc->sc_displ_bits;
	ri->ri_hwbits = sc->sc_displ_hwbits;

	ri->ri_caps = WSSCREEN_WSCOLORS;
	ri->ri_rnum = mi->RedMaskSize;
	ri->ri_gnum = mi->GreenMaskSize;
	ri->ri_bnum = mi->BlueMaskSize;
	ri->ri_rpos = mi->RedFieldPosition;
	ri->ri_gpos = mi->GreenFieldPosition;
	ri->ri_bpos = mi->BlueFieldPosition;

	rasops_init(ri, mi->YResolution / 16, mi->XResolution / 8);

	if (sc->sc_scrollscreens > 1) {
		/* override copyrows but remember old one for delegation */
		sc->sc_orig_copyrows = ri->ri_ops.copyrows;
		ri->ri_ops.copyrows  = vv_copyrows;
	}

#ifdef VESA_DISABLE_TEXT
	if (scr == &vesafb_console_screen && !DISABLESPLASH)
		SCREEN_DISABLE_DRAWING(&vesafb_console_screen);
#endif
}

static void
vesafb_init(struct vesafb_softc *sc, int setmode)
{
	struct bioscallregs regs;
	struct modeinfoblock *mi;
	struct paletteentry pe;
	int i, j, s;

	mi = &sc->sc_mi;

	s = splsched();

	if (setmode) {
		/* set mode */
		regs.EAX = 0x4f02;
		regs.EBX = sc->sc_mode | 0x4000;
		kvm86_bioscall_simple(0x10, &regs);
		if ((regs.EAX & 0xff) != 0x4f) {
			aprint_error_dev(sc->sc_dev, "bioscall failed\n");
			goto out;
		}
	}

	/* setup palette, not required in direct colour mode */
	if (mi->BitsPerPixel == 8) {
		memset(&regs, 0, sizeof(struct bioscallregs));
		regs.EAX = 0x4f08; /* function code */
		regs.EBX = 0x0600; /* we want a 6-bit palette */

		kvm86_bioscall_simple(0x10, &regs);
		if ((regs.EAX & 0xff) != 0x4f) {
			aprint_error_dev(sc->sc_dev, "vbecall failed: ax=%x\n",
			    regs.EAX);
			goto out;
		}

		j = 0;

		/* setup ansi / rasops palette */
		for (i = 0; i < 256; i++) {
#ifndef SPLASHSCREEN
			pe.Blue = rasops_cmap[(i * 3) + 2];
			pe.Green = rasops_cmap[(i * 3) + 1];
			pe.Red = rasops_cmap[(i * 3) + 0];
			pe.Alignment = 0;
			vesafb_set_palette(sc, i, pe);
#else
			if (i >= SPLASH_CMAP_OFFSET &&
			    i < SPLASH_CMAP_OFFSET +
			    SPLASH_CMAP_SIZE) {
				pe.Blue = _splash_header_data_cmap[j][2];
				pe.Green = _splash_header_data_cmap[j][1];
				pe.Red = _splash_header_data_cmap[j][0];
				pe.Alignment = 0;
				j++;
				vesafb_set_palette(sc, i, pe);
			} else {
				pe.Blue = rasops_cmap[(i * 3) + 2];
				pe.Green = rasops_cmap[(i * 3) + 1];
				pe.Red = rasops_cmap[(i * 3) + 0];
				pe.Alignment = 0;
				vesafb_set_palette(sc, i, pe);
			}
#endif

			/* Fill in the softc colourmap arrays */
			sc->sc_cmap_red[i / 3] = rasops_cmap[i + 0];
			sc->sc_cmap_green[i / 3] = rasops_cmap[i + 1];
			sc->sc_cmap_blue[i / 3] = rasops_cmap[i + 2];
		}
	}

out:
	splx(s);
	return;
}

static void
vesafb_display_on(device_t dv)
{
	struct vesafb_softc *sc = device_private(dv);

	if (sc->sc_wsmode == WSDISPLAYIO_MODE_EMUL)
		vesafb_svideo(sc, 0);
}

static void
vesafb_display_standby(device_t dv)
{
	struct vesafb_softc *sc = device_private(dv);

	if (sc->sc_wsmode == WSDISPLAYIO_MODE_EMUL)
	vesafb_svideo(sc, 1);
}

static void
vesafb_display_suspend(device_t dv)
{
	struct vesafb_softc *sc = device_private(dv);

	if (sc->sc_wsmode == WSDISPLAYIO_MODE_EMUL)
		vesafb_svideo(sc, 2);
}

static void
vesafb_display_off(device_t dv)
{
	struct vesafb_softc *sc = device_private(dv);

	if (sc->sc_wsmode == WSDISPLAYIO_MODE_EMUL)
		vesafb_svideo(sc, 4);
}

static void
vesafb_display_reduced(device_t dv)
{
	struct vesafb_softc *sc = device_private(dv);

	if (sc->sc_wsmode == WSDISPLAYIO_MODE_EMUL)
		vesafb_svideo(sc, 8);
}

static void
vesafb_set_palette(struct vesafb_softc *sc, int reg,
    struct paletteentry pe)
{
	struct trapframe tf;
	int res;
	char *buf;

	buf = sc->sc_buf;

	/*
	 * this function takes 8 bit per palette as input, but we're
	 * working in 6 bit mode here
	 */
	pe.Red >>= 2;
	pe.Green >>= 2;
	pe.Blue >>= 2;

	memcpy(buf, &pe, sizeof(struct paletteentry));

	/* set palette */
	memset(&tf, 0, sizeof(struct trapframe));
	tf.tf_eax = 0x4f09; /* function code */
	tf.tf_ebx = 0x0600; /* 6 bit per primary, set format */
	tf.tf_ecx = 1;
	tf.tf_edx = reg;
	tf.tf_vm86_es = 0;
	tf.tf_edi = 0x2000;

	res = kvm86_bioscall(0x10, &tf);
	if (res || (tf.tf_eax & 0xff) != 0x4f)
		aprint_error_dev(sc->sc_dev, "vbecall: res=%d, ax=%x\n",
		    res, tf.tf_eax);

	return;
}

static int
vesafb_svideo(struct vesafb_softc *sc, int bh)
{
#ifdef VESAFB_PM
	struct bioscallregs regs;
	int s;

	/* If the power management state is not supported, bail */
	if ((sc->sc_pmstates & bh) != bh)
		return ENODEV;

	memset(&regs, 0, sizeof(struct bioscallregs));
	regs.EAX = 0x4f10;
	regs.EBX = (bh << 8) | 1;

	s = splsched();
	kvm86_bioscall_simple(0x10, &regs);
	splx(s);

	return 0;
#else
	return ENODEV;
#endif
}

static int
vesafb_gvideo(struct vesafb_softc *sc, u_int *on)
{
#ifdef VESAFB_PM
	struct trapframe tf;
	int res, bh;

	/* get video pm state */
	memset(&tf, 0, sizeof(struct trapframe));
	tf.tf_eax = 0x4f10; /* function code */
	tf.tf_ebx = 2;

	res = kvm86_bioscall(0x10, &tf);
	if (res || (tf.tf_eax & 0xff) != 0x4f) {
		aprint_error_dev(sc->sc_dev, "unable to get power state (0x%04x)\n",
		    (tf.tf_eax & 0xffff));
		return ENODEV;
	}

	bh = (tf.tf_ebx & 0xff00) >> 8;

	switch (bh) {
	case 0: /* 0:on */
		*on = WSDISPLAYIO_VIDEO_ON;
		break;
	default: /* 1:standby, 2:suspend, 4:off, 8:reduced_on */
		*on = WSDISPLAYIO_VIDEO_OFF;
		break;
	}

	return 0;
#else
	return ENODEV;
#endif
}

static int
vesafb_putcmap(struct vesafb_softc *sc,
		    struct wsdisplay_cmap *cm)
{
	struct paletteentry pe;
	u_int idx, cnt;
	u_char r[256], g[256], b[256];
	u_char *rp, *gp, *bp;
	int rv, i;

	idx = cm->index;
	cnt = cm->count;

	if (idx >= 255 || cnt > 256 || idx + cnt > 256)
		return EINVAL;

	rv = copyin(cm->red, &r[idx], cnt);
	if (rv)
		return rv;
	rv = copyin(cm->green, &g[idx], cnt);
	if (rv)
		return rv;
	rv = copyin(cm->blue, &b[idx], cnt);
	if (rv)
		return rv;

	memcpy(&sc->sc_cmap_red[idx], &r[idx], cnt);
	memcpy(&sc->sc_cmap_green[idx], &g[idx], cnt);
	memcpy(&sc->sc_cmap_blue[idx], &b[idx], cnt);

	rp = &sc->sc_cmap_red[idx];
	gp = &sc->sc_cmap_green[idx];
	bp = &sc->sc_cmap_blue[idx];

	for (i = 0; i < cnt; i++) {
		pe.Blue = *bp;
		pe.Green = *gp;
		pe.Red = *rp;
		pe.Alignment = 0;
		vesafb_set_palette(sc, idx, pe);
		idx++;
		rp++, gp++, bp++;
	}

	return 0;
}

static int
vesafb_getcmap(struct vesafb_softc *sc,
		    struct wsdisplay_cmap *cm)
{
	u_int idx, cnt;
	int rv;

	idx = cm->index;
	cnt = cm->count;

	if (idx >= 255 || cnt > 256 || idx + cnt > 256)
		return EINVAL;

	rv = copyout(&sc->sc_cmap_red[idx], cm->red, cnt);
	if (rv)
		return rv;
	rv = copyout(&sc->sc_cmap_green[idx], cm->green, cnt);
	if (rv)
		return rv;
	rv = copyout(&sc->sc_cmap_blue[idx], cm->blue, cnt);
	if (rv)
		return rv;

	return 0;
}

int
vesafb_cnattach(void)
{
	/* XXX i386 calls consinit too early for us to use
	 *     kvm86; assume that we've attached
	 */

	return 0;
}