Implement hardware scrolling in vesafb by utilising the whole frame buffer

memory and scrolling trough it. If the hardware is not capable it will
disable hardware scrolling.

For 640x480 at 8 bpp the speedup is around 4 times, at 1280x1280 at 32 bpp
the speedup is around 9.4 times.

Checked and OK'd by Jared McNeill. Thanks go to Jared and Michael Lorenz
for their tips and vcons knowledge!
This commit is contained in:
reinoud 2007-03-24 00:07:17 +00:00
parent e6e6c988b3
commit ef34b019ed
5 changed files with 192 additions and 26 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: vesabios.c,v 1.23 2007/02/20 00:09:57 xtraeme Exp $ */
/* $NetBSD: vesabios.c,v 1.24 2007/03/24 00:07:17 reinoud Exp $ */
/*
* Copyright (c) 2002, 2004
@ -27,7 +27,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: vesabios.c,v 1.23 2007/02/20 00:09:57 xtraeme Exp $");
__KERNEL_RCSID(0, "$NetBSD: vesabios.c,v 1.24 2007/03/24 00:07:17 reinoud Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -186,6 +186,8 @@ vesabios_attach(struct device *parent, struct device *dev,
aprint_normal(": version %d.%d",
vi->VbeVersion >> 8, vi->VbeVersion & 0xff);
vbaa.vbaa_vbeversion = vi->VbeVersion;
res = kvm86_bios_read(FAR2FLATPTR(vi->OemVendorNamePtr),
name, sizeof(name));
if (res > 0) {
@ -259,6 +261,12 @@ vesabios_attach(struct device *parent, struct device *dev,
if (mi->ModeAttributes & 0x80) {
/* flat buffer */
rastermodes[nrastermodes++] = modes[i];
#ifdef VESABIOSVERBOSE
aprint_verbose("%s: memory window "
"granularity %d Kb, window size %d Kb\n",
dev->dv_xname,
mi->WinGranularity/1024, mi->WinSize/1024);
#endif
}
} else {
/* text */

View File

@ -1,4 +1,4 @@
/* $NetBSD: vesabios.h,v 1.6 2006/06/20 20:30:22 drochner Exp $ */
/* $NetBSD: vesabios.h,v 1.7 2007/03/24 00:07:17 reinoud Exp $ */
/*
* Copyright (c) 2002, 2005
@ -32,4 +32,5 @@ struct vesabiosdev_attach_args {
const char *vbaa_type;
int *vbaa_modes;
int vbaa_nmodes;
int vbaa_vbeversion;
};

View File

@ -1,4 +1,4 @@
/* $NetBSD: vesabiosreg.h,v 1.5 2006/06/20 20:30:22 drochner Exp $ */
/* $NetBSD: vesabiosreg.h,v 1.6 2007/03/24 00:07:17 reinoud Exp $ */
/*
* Written by M. Drochner
@ -29,13 +29,8 @@ struct modeinfoblock {
uint8_t DirectColorModeInfo;
/* Mandatory information for VBE 2.0 and above */
uint32_t PhysBasePtr;
#ifdef VBE_2_0
uint32_t OffScreenMemOffset;
uint16_t OffScreenMemSize;
uint8_t Reserved2[206];
#else
uint32_t Reserved2;
uint16_t Reserved3;
uint32_t OffScreenMemOffset; /* reserved in VBE 3.0 and above */
uint16_t OffScreenMemSize; /* reserved in VBE 3.0 and above */
/* Mandatory information for VBE 3.0 and above */
uint16_t LinBytesPerScanLine;
@ -47,7 +42,6 @@ struct modeinfoblock {
uint8_t LinRsvdMaskSize, LinRsvdFieldPosition;
uint32_t MaxPixelClock;
uint8_t Reserved4[189];
#endif
} __attribute__ ((packed));
struct paletteentry {

View File

@ -1,9 +1,11 @@
/* $NetBSD: vesafb.c,v 1.20 2007/03/04 05:59:56 christos Exp $ */
/* $NetBSD: vesafb.c,v 1.21 2007/03/24 00:07:17 reinoud 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:
@ -35,7 +37,7 @@
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: vesafb.c,v 1.20 2007/03/04 05:59:56 christos Exp $");
__KERNEL_RCSID(0, "$NetBSD: vesafb.c,v 1.21 2007/03/24 00:07:17 reinoud Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -75,7 +77,7 @@ struct wsscreen_descr vesafb_stdscreen = {
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 *);
@ -218,10 +220,32 @@ vesafb_attach(struct device *parent, struct device *dev, void *aux)
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("%s: fb %dx%dx%d @0x%x\n", sc->sc_dev.dv_xname,
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;
sc->sc_screensize = mi->YResolution * mi->BytesPerScanLine;
sc->sc_fbsize = sc->sc_scrollscreens * sc->sc_screensize;
aprint_normal("%s: %d Kb memory reported, %d screens possible\n",
sc->sc_dev.dv_xname,
sc->sc_fbsize / 1024,
sc->sc_scrollscreens);
if (sc->sc_scrollscreens == 1)
aprint_normal("%s: one screen, so hardware scrolling not "
"possible\n", sc->sc_dev.dv_xname);
if (sc->sc_pm) {
aprint_normal("%s: VBE/PM %d.%d", sc->sc_dev.dv_xname,
(sc->sc_pmver >> 4), sc->sc_pmver & 0xf);
@ -237,25 +261,35 @@ vesafb_attach(struct device *parent, struct device *dev, void *aux)
}
res = _x86_memio_map(X86_BUS_SPACE_MEM, mi->PhysBasePtr,
mi->YResolution * mi->BytesPerScanLine,
sc->sc_fbsize, /* was sc_screensize */
BUS_SPACE_MAP_LINEAR, &h);
if (res) {
aprint_error("%s: framebuffer mapping failed\n",
sc->sc_dev.dv_xname);
goto out;
}
sc->sc_bits = bus_space_vaddr(X86_BUS_SPACE_MEM, h);
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(mi->YResolution * mi->BytesPerScanLine,
sc->sc_shadowbits = malloc(sc->sc_screensize,
M_VESAFB, M_NOWAIT);
if (sc->sc_shadowbits == NULL) {
aprint_error("%s: unable to allocate %d bytes for shadowfb\n",
sc->sc_dev.dv_xname, mi->YResolution*mi->BytesPerScanLine);
sc->sc_dev.dv_xname, 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);
vesafb_console_screen.scr_flags |= VCONS_SCREEN_IS_STATIC;
@ -422,6 +456,119 @@ vesafb_mmap(void *v, void *vs, off_t offset, int prot)
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("%s: vbecall: res=%d, ax=%x\n",
sc->sc_dev.dv_xname, 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)
@ -437,13 +584,13 @@ vesafb_init_screen(void *c, struct vcons_screen *scr, int existing,
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;
#ifdef VESAFB_SHADOW_FB
ri->ri_bits = sc->sc_shadowbits;
ri->ri_hwbits = sc->sc_bits;
#else
ri->ri_bits = sc->sc_bits;
#endif /* !VESAFB_SHADOW_FB */
/* 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;
@ -454,6 +601,12 @@ vesafb_init_screen(void *c, struct vcons_screen *scr, int existing,
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)
SCREEN_DISABLE_DRAWING(&vesafb_console_screen);

View File

@ -1,4 +1,4 @@
/* $NetBSD: vesafbvar.h,v 1.3 2006/04/24 14:14:38 jmcneill Exp $ */
/* $NetBSD: vesafbvar.h,v 1.4 2007/03/24 00:07:18 reinoud Exp $ */
/*-
* Copyright (c) 2006 Jared D. McNeill <jmcneill@invisible.ca>
@ -85,11 +85,21 @@ struct vesafb_softc {
char *sc_buf;
u_char *sc_bits;
u_char *sc_shadowbits;
u_char *sc_fbstart;
u_char sc_cmap_red[256];
u_char sc_cmap_green[256];
u_char sc_cmap_blue[256];
int sc_wsmode;
int sc_nscreens;
int sc_scrollscreens;
uint32_t sc_screensize;
uint32_t sc_fbsize;
uint32_t sc_fblines;
/* display wide buffer settings for VTs */
uint8_t *sc_displ_bits;
uint8_t *sc_displ_hwbits;
/* delegate rasops function if hardware scrolling */
void (*sc_orig_copyrows)(void *, int, int, int);
int sc_pm;
uint8_t sc_pmver;