956 lines
24 KiB
C
956 lines
24 KiB
C
/* $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, ®s);
|
|
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(®s, 0, sizeof(struct bioscallregs));
|
|
regs.EAX = 0x4f08; /* function code */
|
|
regs.EBX = 0x0600; /* we want a 6-bit palette */
|
|
|
|
kvm86_bioscall_simple(0x10, ®s);
|
|
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(®s, 0, sizeof(struct bioscallregs));
|
|
regs.EAX = 0x4f10;
|
|
regs.EBX = (bh << 8) | 1;
|
|
|
|
s = splsched();
|
|
kvm86_bioscall_simple(0x10, ®s);
|
|
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;
|
|
}
|