freebsd-skq/sys/dev/fb/vga.c
Konstantin Belousov 9a52756044 i386: Merge PAE and non-PAE pmaps into same kernel.
Effectively all i386 kernels now have two pmaps compiled in: one
managing PAE pagetables, and another non-PAE. The implementation is
selected at cold time depending on the CPU features. The vm_paddr_t is
always 64bit now. As result, nx bit can be used on all capable CPUs.

Option PAE only affects the bus_addr_t: it is still 32bit for non-PAE
configs, for drivers compatibility. Kernel layout, esp. max kernel
address, low memory PDEs and max user address (same as trampoline
start) are now same for PAE and for non-PAE regardless of the type of
page tables used.

Non-PAE kernel (when using PAE pagetables) can handle physical memory
up to 24G now, larger memory requires re-tuning the KVA consumers and
instead the code caps the maximum at 24G. Unfortunately, a lot of
drivers do not use busdma(9) properly so by default even 4G barrier is
not easy. There are two tunables added: hw.above4g_allow and
hw.above24g_allow, the first one is kept enabled for now to evaluate
the status on HEAD, second is only for dev use.

i386 now creates three freelists if there is any memory above 4G, to
allow proper bounce pages allocation. Also, VM_KMEM_SIZE_SCALE changed
from 3 to 1.

The PAE_TABLES kernel config option is retired.

In collaboarion with: pho
Discussed with:	emaste
Reviewed by:	markj
MFC after:	2 weeks
Sponsored by:	The FreeBSD Foundation
Differential revision:	https://reviews.freebsd.org/D18894
2019-01-30 02:07:13 +00:00

3120 lines
82 KiB
C

/*-
* SPDX-License-Identifier: BSD-3-Clause
*
* Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
* Copyright (c) 1992-1998 Søren Schmidt
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer as
* the first lines of this file unmodified.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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>
__FBSDID("$FreeBSD$");
#include "opt_vga.h"
#include "opt_fb.h"
#ifndef FB_DEBUG
#define FB_DEBUG 0
#endif
#include "opt_syscons.h" /* should be removed in the future, XXX */
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/conf.h>
#include <sys/fcntl.h>
#include <sys/malloc.h>
#include <sys/fbio.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <vm/pmap.h>
#include <machine/md_var.h>
#if defined(__i386__) || defined(__amd64__)
#include <machine/pc/bios.h>
#endif
#include <machine/bus.h>
#include <dev/fb/fbreg.h>
#include <dev/fb/vgareg.h>
#include <isa/isareg.h>
#ifndef VGA_DEBUG
#define VGA_DEBUG 0
#endif
/* XXX machine/pc/bios.h has got too much i386-specific stuff in it */
#ifndef BIOS_PADDRTOVADDR
#define BIOS_PADDRTOVADDR(x) (x)
#endif
int
vga_probe_unit(int unit, video_adapter_t *buf, int flags)
{
video_adapter_t *adp;
video_switch_t *sw;
int error;
sw = vid_get_switch(VGA_DRIVER_NAME);
if (sw == NULL)
return 0;
error = (*sw->probe)(unit, &adp, NULL, flags);
if (error)
return error;
bcopy(adp, buf, sizeof(*buf));
return 0;
}
int
vga_attach_unit(int unit, vga_softc_t *sc, int flags)
{
video_switch_t *sw;
int error;
sw = vid_get_switch(VGA_DRIVER_NAME);
if (sw == NULL)
return ENXIO;
error = (*sw->probe)(unit, &sc->adp, NULL, flags);
if (error)
return error;
return (*sw->init)(unit, sc->adp, flags);
}
/* cdev driver functions */
#ifdef FB_INSTALL_CDEV
int
vga_open(struct cdev *dev, vga_softc_t *sc, int flag, int mode, struct thread *td)
{
if (sc == NULL)
return ENXIO;
if (mode & (O_CREAT | O_APPEND | O_TRUNC))
return ENODEV;
return genfbopen(&sc->gensc, sc->adp, flag, mode, td);
}
int
vga_close(struct cdev *dev, vga_softc_t *sc, int flag, int mode, struct thread *td)
{
return genfbclose(&sc->gensc, sc->adp, flag, mode, td);
}
int
vga_read(struct cdev *dev, vga_softc_t *sc, struct uio *uio, int flag)
{
return genfbread(&sc->gensc, sc->adp, uio, flag);
}
int
vga_write(struct cdev *dev, vga_softc_t *sc, struct uio *uio, int flag)
{
return genfbread(&sc->gensc, sc->adp, uio, flag);
}
int
vga_ioctl(struct cdev *dev, vga_softc_t *sc, u_long cmd, caddr_t arg, int flag,
struct thread *td)
{
return genfbioctl(&sc->gensc, sc->adp, cmd, arg, flag, td);
}
int
vga_mmap(struct cdev *dev, vga_softc_t *sc, vm_ooffset_t offset,
vm_paddr_t *paddr, int prot, vm_memattr_t *memattr)
{
return genfbmmap(&sc->gensc, sc->adp, offset, paddr, prot, memattr);
}
#endif /* FB_INSTALL_CDEV */
/* LOW-LEVEL */
#include <isa/rtc.h>
#ifdef __i386__
#include <dev/fb/vesa.h>
#endif
#define probe_done(adp) ((adp)->va_flags & V_ADP_PROBED)
#define init_done(adp) ((adp)->va_flags & V_ADP_INITIALIZED)
#define config_done(adp) ((adp)->va_flags & V_ADP_REGISTERED)
/* for compatibility with old kernel options */
#ifdef SC_ALT_SEQACCESS
#undef SC_ALT_SEQACCESS
#undef VGA_ALT_SEQACCESS
#define VGA_ALT_SEQACCESS 1
#endif
#ifdef SLOW_VGA
#undef SLOW_VGA
#undef VGA_SLOW_IOACCESS
#define VGA_SLOW_IOACCESS
#endif
/* architecture dependent option */
#if !defined(__i386__) && !defined(__amd64__)
#define VGA_NO_BIOS 1
#endif
/* this should really be in `rtc.h' */
#define RTC_EQUIPMENT 0x14
/* various sizes */
#define V_MODE_MAP_SIZE (M_VGA_CG320 + 1)
#define V_MODE_PARAM_SIZE 64
/* video adapter state buffer */
struct adp_state {
int sig;
#define V_STATE_SIG 0x736f6962
u_char regs[V_MODE_PARAM_SIZE];
};
typedef struct adp_state adp_state_t;
/* video adapter information */
#define DCC_MONO 0
#define DCC_CGA40 1
#define DCC_CGA80 2
#define DCC_EGAMONO 3
#define DCC_EGA40 4
#define DCC_EGA80 5
/*
* NOTE: `va_window' should have a virtual address, but is initialized
* with a physical address in the following table, as verify_adapter()
* will perform address conversion at run-time.
*/
static video_adapter_t adapter_init_value[] = {
/* DCC_MONO */
{ 0, KD_MONO, "mda", 0, 0, 0, IO_MDA, IO_MDASIZE, MONO_CRTC,
MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE,
0, 0, 0, 0, 7, 0, },
/* DCC_CGA40 */
{ 0, KD_CGA, "cga", 0, 0, V_ADP_COLOR, IO_CGA, IO_CGASIZE, COLOR_CRTC,
CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE,
0, 0, 0, 0, 3, 0, },
/* DCC_CGA80 */
{ 0, KD_CGA, "cga", 0, 0, V_ADP_COLOR, IO_CGA, IO_CGASIZE, COLOR_CRTC,
CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE,
0, 0, 0, 0, 3, 0, },
/* DCC_EGAMONO */
{ 0, KD_EGA, "ega", 0, 0, 0, IO_MDA, 48, MONO_CRTC,
EGA_BUF_BASE, EGA_BUF_SIZE, MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE,
0, 0, 0, 0, 7, 0, },
/* DCC_EGA40 */
{ 0, KD_EGA, "ega", 0, 0, V_ADP_COLOR, IO_MDA, 48, COLOR_CRTC,
EGA_BUF_BASE, EGA_BUF_SIZE, CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE,
0, 0, 0, 0, 3, 0, },
/* DCC_EGA80 */
{ 0, KD_EGA, "ega", 0, 0, V_ADP_COLOR, IO_MDA, 48, COLOR_CRTC,
EGA_BUF_BASE, EGA_BUF_SIZE, CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE,
0, 0, 0, 0, 3, 0, },
};
static video_adapter_t biosadapter[2];
static int biosadapters = 0;
/* video driver declarations */
static int vga_configure(int flags);
int (*vga_sub_configure)(int flags);
#if 0
static int vga_nop(void);
#endif
static int vga_error(void);
static vi_probe_t vga_probe;
static vi_init_t vga_init;
static vi_get_info_t vga_get_info;
static vi_query_mode_t vga_query_mode;
static vi_set_mode_t vga_set_mode;
static vi_save_font_t vga_save_font;
static vi_load_font_t vga_load_font;
static vi_show_font_t vga_show_font;
static vi_save_palette_t vga_save_palette;
static vi_load_palette_t vga_load_palette;
static vi_set_border_t vga_set_border;
static vi_save_state_t vga_save_state;
static vi_load_state_t vga_load_state;
static vi_set_win_org_t vga_set_origin;
static vi_read_hw_cursor_t vga_read_hw_cursor;
static vi_set_hw_cursor_t vga_set_hw_cursor;
static vi_set_hw_cursor_shape_t vga_set_hw_cursor_shape;
static vi_blank_display_t vga_blank_display;
static vi_mmap_t vga_mmap_buf;
static vi_ioctl_t vga_dev_ioctl;
#ifndef VGA_NO_MODE_CHANGE
static vi_clear_t vga_clear;
static vi_fill_rect_t vga_fill_rect;
static vi_bitblt_t vga_bitblt;
#else /* VGA_NO_MODE_CHANGE */
#define vga_clear (vi_clear_t *)vga_error
#define vga_fill_rect (vi_fill_rect_t *)vga_error
#define vga_bitblt (vi_bitblt_t *)vga_error
#endif
static vi_diag_t vga_diag;
static video_switch_t vgavidsw = {
vga_probe,
vga_init,
vga_get_info,
vga_query_mode,
vga_set_mode,
vga_save_font,
vga_load_font,
vga_show_font,
vga_save_palette,
vga_load_palette,
vga_set_border,
vga_save_state,
vga_load_state,
vga_set_origin,
vga_read_hw_cursor,
vga_set_hw_cursor,
vga_set_hw_cursor_shape,
vga_blank_display,
vga_mmap_buf,
vga_dev_ioctl,
vga_clear,
vga_fill_rect,
vga_bitblt,
vga_error,
vga_error,
vga_diag,
};
VIDEO_DRIVER(mda, vgavidsw, NULL);
VIDEO_DRIVER(cga, vgavidsw, NULL);
VIDEO_DRIVER(ega, vgavidsw, NULL);
VIDEO_DRIVER(vga, vgavidsw, vga_configure);
/* VGA BIOS standard video modes */
#define EOT (-1)
#define NA (-2)
static video_info_t bios_vmode[] = {
/* CGA */
{ M_B40x25, V_INFO_COLOR, 40, 25, 8, 8, 2, 1,
CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
{ M_C40x25, V_INFO_COLOR, 40, 25, 8, 8, 4, 1,
CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
{ M_B80x25, V_INFO_COLOR, 80, 25, 8, 8, 2, 1,
CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
{ M_C80x25, V_INFO_COLOR, 80, 25, 8, 8, 4, 1,
CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
/* EGA */
{ M_ENH_B40x25, V_INFO_COLOR, 40, 25, 8, 14, 2, 1,
CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
{ M_ENH_C40x25, V_INFO_COLOR, 40, 25, 8, 14, 4, 1,
CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
{ M_ENH_B80x25, V_INFO_COLOR, 80, 25, 8, 14, 2, 1,
CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
{ M_ENH_C80x25, V_INFO_COLOR, 80, 25, 8, 14, 4, 1,
CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
/* VGA */
{ M_VGA_C40x25, V_INFO_COLOR, 40, 25, 8, 16, 4, 1,
CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
{ M_VGA_M80x25, 0, 80, 25, 8, 16, 2, 1,
MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
{ M_VGA_C80x25, V_INFO_COLOR, 80, 25, 8, 16, 4, 1,
CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
/* MDA */
{ M_EGAMONO80x25, 0, 80, 25, 8, 14, 2, 1,
MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
/* EGA */
{ M_ENH_B80x43, V_INFO_COLOR, 80, 43, 8, 8, 2, 1,
CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
{ M_ENH_C80x43, V_INFO_COLOR, 80, 43, 8, 8, 4, 1,
CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
/* VGA */
{ M_VGA_M80x30, 0, 80, 30, 8, 16, 2, 1,
MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
{ M_VGA_C80x30, V_INFO_COLOR, 80, 30, 8, 16, 4, 1,
CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
{ M_VGA_M80x50, 0, 80, 50, 8, 8, 2, 1,
MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
{ M_VGA_C80x50, V_INFO_COLOR, 80, 50, 8, 8, 4, 1,
CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
{ M_VGA_M80x60, 0, 80, 60, 8, 8, 2, 1,
MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
{ M_VGA_C80x60, V_INFO_COLOR, 80, 60, 8, 8, 4, 1,
CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
#ifndef VGA_NO_MODE_CHANGE
#ifdef VGA_WIDTH90
{ M_VGA_M90x25, 0, 90, 25, 8, 16, 2, 1,
MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
{ M_VGA_C90x25, V_INFO_COLOR, 90, 25, 8, 16, 4, 1,
CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
{ M_VGA_M90x30, 0, 90, 30, 8, 16, 2, 1,
MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
{ M_VGA_C90x30, V_INFO_COLOR, 90, 30, 8, 16, 4, 1,
CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
{ M_VGA_M90x43, 0, 90, 43, 8, 8, 2, 1,
MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
{ M_VGA_C90x43, V_INFO_COLOR, 90, 43, 8, 8, 4, 1,
CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
{ M_VGA_M90x50, 0, 90, 50, 8, 8, 2, 1,
MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
{ M_VGA_C90x50, V_INFO_COLOR, 90, 50, 8, 8, 4, 1,
CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
{ M_VGA_M90x60, 0, 90, 60, 8, 8, 2, 1,
MDA_BUF_BASE, MDA_BUF_SIZE, MDA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
{ M_VGA_C90x60, V_INFO_COLOR, 90, 60, 8, 8, 4, 1,
CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_TEXT },
#endif /* VGA_WIDTH90 */
/* CGA */
{ M_BG320, V_INFO_COLOR | V_INFO_GRAPHICS, 320, 200, 8, 8, 2, 1,
CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_CGA },
{ M_CG320, V_INFO_COLOR | V_INFO_GRAPHICS, 320, 200, 8, 8, 2, 1,
CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_CGA },
{ M_BG640, V_INFO_COLOR | V_INFO_GRAPHICS, 640, 200, 8, 8, 1, 1,
CGA_BUF_BASE, CGA_BUF_SIZE, CGA_BUF_SIZE, 0, 0, V_INFO_MM_CGA },
/* EGA */
{ M_CG320_D, V_INFO_COLOR | V_INFO_GRAPHICS, 320, 200, 8, 8, 4, 4,
GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0,
V_INFO_MM_PLANAR },
{ M_CG640_E, V_INFO_COLOR | V_INFO_GRAPHICS, 640, 200, 8, 8, 4, 4,
GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0 ,
V_INFO_MM_PLANAR },
{ M_EGAMONOAPA, V_INFO_GRAPHICS, 640, 350, 8, 14, 4, 4,
GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, 64*1024, 0, 0 ,
V_INFO_MM_PLANAR },
{ M_ENHMONOAPA2,V_INFO_GRAPHICS, 640, 350, 8, 14, 4, 4,
GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0 ,
V_INFO_MM_PLANAR },
{ M_CG640x350, V_INFO_COLOR | V_INFO_GRAPHICS, 640, 350, 8, 14, 2, 2,
GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0 ,
V_INFO_MM_PLANAR },
{ M_ENH_CG640, V_INFO_COLOR | V_INFO_GRAPHICS, 640, 350, 8, 14, 4, 4,
GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0 ,
V_INFO_MM_PLANAR },
/* VGA */
{ M_BG640x480, V_INFO_COLOR | V_INFO_GRAPHICS, 640, 480, 8, 16, 4, 4,
GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0 ,
V_INFO_MM_PLANAR },
{ M_CG640x480, V_INFO_COLOR | V_INFO_GRAPHICS, 640, 480, 8, 16, 4, 4,
GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0 ,
V_INFO_MM_PLANAR },
{ M_VGA_CG320, V_INFO_COLOR | V_INFO_GRAPHICS, 320, 200, 8, 8, 8, 1,
GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0,
V_INFO_MM_PACKED, 1 },
{ M_VGA_MODEX, V_INFO_COLOR | V_INFO_GRAPHICS, 320, 240, 8, 8, 8, 4,
GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0,
V_INFO_MM_VGAX, 1 },
#endif /* VGA_NO_MODE_CHANGE */
{ EOT },
};
static int vga_init_done = FALSE;
#if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE)
static u_char *video_mode_ptr = NULL; /* EGA/VGA */
static u_char *video_mode_ptr2 = NULL; /* CGA/MDA */
#endif
static u_char *mode_map[V_MODE_MAP_SIZE];
static adp_state_t adpstate;
static adp_state_t adpstate2;
static int rows_offset = 1;
/* local macros and functions */
#define BIOS_SADDRTOLADDR(p) ((((p) & 0xffff0000) >> 12) + ((p) & 0x0000ffff))
#if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE)
static void map_mode_table(u_char *map[], u_char *table, int max);
#endif
static void clear_mode_map(video_adapter_t *adp, u_char *map[], int max,
int color);
#if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE)
static int map_mode_num(int mode);
#endif
static int map_gen_mode_num(int type, int color, int mode);
static int map_bios_mode_num(int type, int color, int bios_mode);
static u_char *get_mode_param(int mode);
#ifndef VGA_NO_BIOS
static void fill_adapter_param(int code, video_adapter_t *adp);
#endif
static int verify_adapter(video_adapter_t *adp);
static void update_adapter_info(video_adapter_t *adp, video_info_t *info);
#if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE)
#define COMP_IDENTICAL 0
#define COMP_SIMILAR 1
#define COMP_DIFFERENT 2
static int comp_adpregs(u_char *buf1, u_char *buf2);
#endif
static int probe_adapters(void);
static int set_line_length(video_adapter_t *adp, int pixel);
static int set_display_start(video_adapter_t *adp, int x, int y);
#ifndef VGA_NO_MODE_CHANGE
#ifdef VGA_WIDTH90
static void set_width90(adp_state_t *params);
#endif
#endif /* !VGA_NO_MODE_CHANGE */
#ifndef VGA_NO_FONT_LOADING
#define PARAM_BUFSIZE 6
static void set_font_mode(video_adapter_t *adp, u_char *buf);
static void set_normal_mode(video_adapter_t *adp, u_char *buf);
#endif
#ifndef VGA_NO_MODE_CHANGE
static void filll_io(int val, vm_offset_t d, size_t size);
static void planar_fill(video_adapter_t *adp, int val);
static void packed_fill(video_adapter_t *adp, int val);
static void direct_fill(video_adapter_t *adp, int val);
#ifdef notyet
static void planar_fill_rect(video_adapter_t *adp, int val, int x, int y,
int cx, int cy);
static void packed_fill_rect(video_adapter_t *adp, int val, int x, int y,
int cx, int cy);
static void direct_fill_rect16(video_adapter_t *adp, int val, int x, int y,
int cx, int cy);
static void direct_fill_rect24(video_adapter_t *adp, int val, int x, int y,
int cx, int cy);
static void direct_fill_rect32(video_adapter_t *adp, int val, int x, int y,
int cx, int cy);
#endif /* notyet */
#endif /* !VGA_NO_MODE_CHANGE */
static void dump_buffer(u_char *buf, size_t len);
#define ISMAPPED(pa, width) \
(((pa) <= (u_long)0x1000 - (width)) \
|| ((pa) >= ISA_HOLE_START && (pa) <= 0x100000 - (width)))
#define prologue(adp, flag, err) \
if (!vga_init_done || !((adp)->va_flags & (flag))) \
return (err)
/* a backdoor for the console driver */
static int
vga_configure(int flags)
{
int i;
probe_adapters();
for (i = 0; i < biosadapters; ++i) {
if (!probe_done(&biosadapter[i]))
continue;
biosadapter[i].va_flags |= V_ADP_INITIALIZED;
if (!config_done(&biosadapter[i])) {
if (vid_register(&biosadapter[i]) < 0)
continue;
biosadapter[i].va_flags |= V_ADP_REGISTERED;
}
}
if (vga_sub_configure != NULL)
(*vga_sub_configure)(flags);
return biosadapters;
}
/* local subroutines */
#if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE)
/* construct the mode parameter map */
static void
map_mode_table(u_char *map[], u_char *table, int max)
{
int i;
for(i = 0; i < max; ++i)
map[i] = table + i*V_MODE_PARAM_SIZE;
for(; i < V_MODE_MAP_SIZE; ++i)
map[i] = NULL;
}
#endif /* !VGA_NO_BIOS && !VGA_NO_MODE_CHANGE */
static void
clear_mode_map(video_adapter_t *adp, u_char *map[], int max, int color)
{
video_info_t info;
int i;
/*
* NOTE: we don't touch `bios_vmode[]' because it is shared
* by all adapters.
*/
for(i = 0; i < max; ++i) {
if (vga_get_info(adp, i, &info))
continue;
if ((info.vi_flags & V_INFO_COLOR) != color)
map[i] = NULL;
}
}
#if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE)
/* map the non-standard video mode to a known mode number */
static int
map_mode_num(int mode)
{
static struct {
int from;
int to;
} mode_map[] = {
{ M_ENH_B80x43, M_ENH_B80x25 },
{ M_ENH_C80x43, M_ENH_C80x25 },
{ M_VGA_M80x30, M_VGA_M80x25 },
{ M_VGA_C80x30, M_VGA_C80x25 },
{ M_VGA_M80x50, M_VGA_M80x25 },
{ M_VGA_C80x50, M_VGA_C80x25 },
{ M_VGA_M80x60, M_VGA_M80x25 },
{ M_VGA_C80x60, M_VGA_C80x25 },
#ifdef VGA_WIDTH90
{ M_VGA_M90x25, M_VGA_M80x25 },
{ M_VGA_C90x25, M_VGA_C80x25 },
{ M_VGA_M90x30, M_VGA_M80x25 },
{ M_VGA_C90x30, M_VGA_C80x25 },
{ M_VGA_M90x43, M_VGA_M80x25 },
{ M_VGA_C90x43, M_ENH_C80x25 },
{ M_VGA_M90x50, M_VGA_M80x25 },
{ M_VGA_C90x50, M_VGA_C80x25 },
{ M_VGA_M90x60, M_VGA_M80x25 },
{ M_VGA_C90x60, M_VGA_C80x25 },
#endif
{ M_VGA_MODEX, M_VGA_CG320 },
};
int i;
for (i = 0; i < nitems(mode_map); ++i) {
if (mode_map[i].from == mode)
return mode_map[i].to;
}
return mode;
}
#endif /* !VGA_NO_BIOS && !VGA_NO_MODE_CHANGE */
/* map a generic video mode to a known mode number */
static int
map_gen_mode_num(int type, int color, int mode)
{
static struct {
int from;
int to_color;
int to_mono;
} mode_map[] = {
{ M_TEXT_80x30, M_VGA_C80x30, M_VGA_M80x30, },
{ M_TEXT_80x43, M_ENH_C80x43, M_ENH_B80x43, },
{ M_TEXT_80x50, M_VGA_C80x50, M_VGA_M80x50, },
{ M_TEXT_80x60, M_VGA_C80x60, M_VGA_M80x60, },
};
int i;
if (mode == M_TEXT_80x25) {
switch (type) {
case KD_VGA:
if (color)
return M_VGA_C80x25;
else
return M_VGA_M80x25;
break;
case KD_EGA:
if (color)
return M_ENH_C80x25;
else
return M_EGAMONO80x25;
break;
case KD_CGA:
return M_C80x25;
case KD_MONO:
case KD_HERCULES:
return M_EGAMONO80x25; /* XXX: this name is confusing */
default:
return -1;
}
}
for (i = 0; i < nitems(mode_map); ++i) {
if (mode_map[i].from == mode)
return ((color) ? mode_map[i].to_color : mode_map[i].to_mono);
}
return mode;
}
/* turn the BIOS video number into our video mode number */
static int
map_bios_mode_num(int type, int color, int bios_mode)
{
static int cga_modes[7] = {
M_B40x25, M_C40x25, /* 0, 1 */
M_B80x25, M_C80x25, /* 2, 3 */
M_BG320, M_CG320,
M_BG640,
};
static int ega_modes[17] = {
M_ENH_B40x25, M_ENH_C40x25, /* 0, 1 */
M_ENH_B80x25, M_ENH_C80x25, /* 2, 3 */
M_BG320, M_CG320,
M_BG640,
M_EGAMONO80x25, /* 7 */
8, 9, 10, 11, 12,
M_CG320_D,
M_CG640_E,
M_ENHMONOAPA2, /* XXX: video momery > 64K */
M_ENH_CG640, /* XXX: video momery > 64K */
};
static int vga_modes[20] = {
M_VGA_C40x25, M_VGA_C40x25, /* 0, 1 */
M_VGA_C80x25, M_VGA_C80x25, /* 2, 3 */
M_BG320, M_CG320,
M_BG640,
M_VGA_M80x25, /* 7 */
8, 9, 10, 11, 12,
M_CG320_D,
M_CG640_E,
M_ENHMONOAPA2,
M_ENH_CG640,
M_BG640x480, M_CG640x480,
M_VGA_CG320,
};
switch (type) {
case KD_VGA:
if (bios_mode < nitems(vga_modes))
return vga_modes[bios_mode];
else if (color)
return M_VGA_C80x25;
else
return M_VGA_M80x25;
break;
case KD_EGA:
if (bios_mode < nitems(ega_modes))
return ega_modes[bios_mode];
else if (color)
return M_ENH_C80x25;
else
return M_EGAMONO80x25;
break;
case KD_CGA:
if (bios_mode < nitems(cga_modes))
return cga_modes[bios_mode];
else
return M_C80x25;
break;
case KD_MONO:
case KD_HERCULES:
return M_EGAMONO80x25; /* XXX: this name is confusing */
default:
break;
}
return -1;
}
/* look up a parameter table entry */
static u_char
*get_mode_param(int mode)
{
#if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE)
if (mode >= V_MODE_MAP_SIZE)
mode = map_mode_num(mode);
#endif
if ((mode >= 0) && (mode < V_MODE_MAP_SIZE))
return mode_map[mode];
else
return NULL;
}
#ifndef VGA_NO_BIOS
static void
fill_adapter_param(int code, video_adapter_t *adp)
{
static struct {
int primary;
int secondary;
} dcc[] = {
{ DCC_MONO, DCC_EGA40 /* CGA monitor */ },
{ DCC_MONO, DCC_EGA80 /* CGA monitor */ },
{ DCC_MONO, DCC_EGA80 },
{ DCC_MONO, DCC_EGA80 },
{ DCC_CGA40, DCC_EGAMONO },
{ DCC_CGA80, DCC_EGAMONO },
{ DCC_EGA40 /* CGA monitor */, DCC_MONO},
{ DCC_EGA80 /* CGA monitor */, DCC_MONO},
{ DCC_EGA80, DCC_MONO },
{ DCC_EGA80, DCC_MONO },
{ DCC_EGAMONO, DCC_CGA40 },
{ DCC_EGAMONO, DCC_CGA80 },
};
if ((code < 0) || (code >= nitems(dcc))) {
adp[V_ADP_PRIMARY] = adapter_init_value[DCC_MONO];
adp[V_ADP_SECONDARY] = adapter_init_value[DCC_CGA80];
} else {
adp[V_ADP_PRIMARY] = adapter_init_value[dcc[code].primary];
adp[V_ADP_SECONDARY] = adapter_init_value[dcc[code].secondary];
}
}
#endif /* VGA_NO_BIOS */
static int
verify_adapter(video_adapter_t *adp)
{
vm_offset_t buf;
u_int16_t v;
#if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE)
u_int32_t p;
#endif
buf = BIOS_PADDRTOVADDR(adp->va_window);
v = readw(buf);
writew(buf, 0xA55A);
if (readw(buf) != 0xA55A)
return ENXIO;
writew(buf, v);
switch (adp->va_type) {
case KD_EGA:
outb(adp->va_crtc_addr, 7);
if (inb(adp->va_crtc_addr) == 7) {
adp->va_type = KD_VGA;
adp->va_name = "vga";
adp->va_flags |= V_ADP_STATESAVE | V_ADP_PALETTE;
}
adp->va_flags |= V_ADP_STATELOAD | V_ADP_BORDER;
/* the color adapter may be in the 40x25 mode... XXX */
#if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE)
/* get the BIOS video mode pointer */
p = *(u_int32_t *)BIOS_PADDRTOVADDR(0x4a8);
p = BIOS_SADDRTOLADDR(p);
if (ISMAPPED(p, sizeof(u_int32_t))) {
p = *(u_int32_t *)BIOS_PADDRTOVADDR(p);
p = BIOS_SADDRTOLADDR(p);
if (ISMAPPED(p, V_MODE_PARAM_SIZE))
video_mode_ptr = (u_char *)BIOS_PADDRTOVADDR(p);
}
#endif
break;
case KD_CGA:
adp->va_flags |= V_ADP_COLOR | V_ADP_BORDER;
/* may be in the 40x25 mode... XXX */
#if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE)
/* get the BIOS video mode pointer */
p = *(u_int32_t *)BIOS_PADDRTOVADDR(0x1d*4);
p = BIOS_SADDRTOLADDR(p);
video_mode_ptr2 = (u_char *)BIOS_PADDRTOVADDR(p);
#endif
break;
case KD_MONO:
#if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE)
/* get the BIOS video mode pointer */
p = *(u_int32_t *)BIOS_PADDRTOVADDR(0x1d*4);
p = BIOS_SADDRTOLADDR(p);
video_mode_ptr2 = (u_char *)BIOS_PADDRTOVADDR(p);
#endif
break;
}
return 0;
}
static void
update_adapter_info(video_adapter_t *adp, video_info_t *info)
{
adp->va_flags &= ~V_ADP_COLOR;
adp->va_flags |=
(info->vi_flags & V_INFO_COLOR) ? V_ADP_COLOR : 0;
adp->va_crtc_addr =
(adp->va_flags & V_ADP_COLOR) ? COLOR_CRTC : MONO_CRTC;
adp->va_window = BIOS_PADDRTOVADDR(info->vi_window);
adp->va_window_size = info->vi_window_size;
adp->va_window_gran = info->vi_window_gran;
adp->va_window_orig = 0;
/* XXX */
adp->va_buffer = info->vi_buffer;
adp->va_buffer_size = info->vi_buffer_size;
adp->va_flags &= ~V_ADP_CWIDTH9;
if (info->vi_flags & V_INFO_CWIDTH9)
adp->va_flags |= V_ADP_CWIDTH9;
if (info->vi_mem_model == V_INFO_MM_VGAX) {
adp->va_line_width = info->vi_width/2;
} else if (info->vi_flags & V_INFO_GRAPHICS) {
switch (info->vi_depth/info->vi_planes) {
case 1:
adp->va_line_width = info->vi_width/8;
break;
case 2:
adp->va_line_width = info->vi_width/4;
break;
case 4:
adp->va_line_width = info->vi_width/2;
break;
case 8:
default: /* shouldn't happen */
adp->va_line_width = info->vi_width;
break;
}
} else {
adp->va_line_width = info->vi_width;
}
adp->va_disp_start.x = 0;
adp->va_disp_start.y = 0;
bcopy(info, &adp->va_info, sizeof(adp->va_info));
}
#if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE)
/* compare two parameter table entries */
static int
comp_adpregs(u_char *buf1, u_char *buf2)
{
static struct {
u_char mask;
} params[V_MODE_PARAM_SIZE] = {
{0xff}, {0x00}, {0xff}, /* COLS}, ROWS}, POINTS */
{0x00}, {0x00}, /* page length */
{0xfe}, {0xff}, {0xff}, {0xff}, /* sequencer registers */
{0xf3}, /* misc register */
{0xff}, {0xff}, {0xff}, {0x7f}, {0xff}, /* CRTC */
{0xff}, {0xff}, {0xff}, {0x7f}, {0xff},
{0x00}, {0x00}, {0x00}, {0x00}, {0x00},
{0x00}, {0xff}, {0x7f}, {0xff}, {0xff},
{0x7f}, {0xff}, {0xff}, {0xef}, {0xff},
{0xff}, {0xff}, {0xff}, {0xff}, {0xff}, /* attribute controller regs */
{0xff}, {0xff}, {0xff}, {0xff}, {0xff},
{0xff}, {0xff}, {0xff}, {0xff}, {0xff},
{0xff}, {0xff}, {0xff}, {0xff}, {0xf0},
{0xff}, {0xff}, {0xff}, {0xff}, {0xff}, /* GDC register */
{0xff}, {0xff}, {0xff}, {0xff},
};
int identical = TRUE;
int i;
if ((buf1 == NULL) || (buf2 == NULL))
return COMP_DIFFERENT;
for (i = 0; i < nitems(params); ++i) {
if (params[i].mask == 0) /* don't care */
continue;
if ((buf1[i] & params[i].mask) != (buf2[i] & params[i].mask))
return COMP_DIFFERENT;
if (buf1[i] != buf2[i])
identical = FALSE;
}
return (identical) ? COMP_IDENTICAL : COMP_SIMILAR;
}
#endif /* !VGA_NO_BIOS && !VGA_NO_MODE_CHANGE */
/* probe video adapters and return the number of detected adapters */
static int
probe_adapters(void)
{
video_adapter_t *adp;
video_info_t info;
#if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE)
u_char *mp;
#endif
int height, i, width;
/* do this test only once */
if (vga_init_done)
return biosadapters;
vga_init_done = TRUE;
/*
* Locate display adapters.
* The AT architecture supports up to two adapters. `syscons' allows
* the following combinations of adapters:
* 1) MDA + CGA
* 2) MDA + EGA/VGA color
* 3) CGA + EGA/VGA mono
* Note that `syscons' doesn't bother with MCGA as it is only
* avaiable for low end PS/2 models which has 80286 or earlier CPUs,
* thus, they are not running FreeBSD!
* When there are two adapaters in the system, one becomes `primary'
* and the other `secondary'. The EGA adapter has a set of DIP
* switches on board for this information and the EGA BIOS copies
* it in the BIOS data area BIOSDATA_VIDEOSWITCH (40:88).
* The VGA BIOS has more sophisticated mechanism and has this
* information in BIOSDATA_DCCINDEX (40:8a), but it also maintains
* compatibility with the EGA BIOS by updating BIOSDATA_VIDEOSWITCH.
*/
/*
* Check rtc and BIOS data area.
* XXX: we don't use BIOSDATA_EQUIPMENT, since it is not a dead
* copy of RTC_EQUIPMENT. Bits 4 and 5 of ETC_EQUIPMENT are
* zeros for EGA and VGA. However, the EGA/VGA BIOS sets
* these bits in BIOSDATA_EQUIPMENT according to the monitor
* type detected.
*/
#ifndef VGA_NO_BIOS
if (*(u_int32_t *)BIOS_PADDRTOVADDR(0x4a8)) {
/* EGA/VGA BIOS is present */
fill_adapter_param(readb(BIOS_PADDRTOVADDR(0x488)) & 0x0f,
biosadapter);
} else {
switch ((rtcin(RTC_EQUIPMENT) >> 4) & 3) { /* bit 4 and 5 */
case 0:
/* EGA/VGA: shouldn't be happening */
fill_adapter_param(readb(BIOS_PADDRTOVADDR(0x488)) & 0x0f,
biosadapter);
break;
case 1:
/* CGA 40x25 */
/* FIXME: switch to the 80x25 mode? XXX */
biosadapter[V_ADP_PRIMARY] = adapter_init_value[DCC_CGA40];
biosadapter[V_ADP_SECONDARY] = adapter_init_value[DCC_MONO];
break;
case 2:
/* CGA 80x25 */
biosadapter[V_ADP_PRIMARY] = adapter_init_value[DCC_CGA80];
biosadapter[V_ADP_SECONDARY] = adapter_init_value[DCC_MONO];
break;
case 3:
/* MDA */
biosadapter[V_ADP_PRIMARY] = adapter_init_value[DCC_MONO];
biosadapter[V_ADP_SECONDARY] = adapter_init_value[DCC_CGA80];
break;
}
}
#else
/* assume EGA/VGA? XXX */
biosadapter[V_ADP_PRIMARY] = adapter_init_value[DCC_EGA80];
biosadapter[V_ADP_SECONDARY] = adapter_init_value[DCC_MONO];
#endif /* VGA_NO_BIOS */
biosadapters = 0;
if (verify_adapter(&biosadapter[V_ADP_SECONDARY]) == 0) {
++biosadapters;
biosadapter[V_ADP_SECONDARY].va_flags |= V_ADP_PROBED;
biosadapter[V_ADP_SECONDARY].va_mode =
biosadapter[V_ADP_SECONDARY].va_initial_mode =
map_bios_mode_num(biosadapter[V_ADP_SECONDARY].va_type,
biosadapter[V_ADP_SECONDARY].va_flags
& V_ADP_COLOR,
biosadapter[V_ADP_SECONDARY].va_initial_bios_mode);
} else {
biosadapter[V_ADP_SECONDARY].va_type = -1;
}
if (verify_adapter(&biosadapter[V_ADP_PRIMARY]) == 0) {
++biosadapters;
biosadapter[V_ADP_PRIMARY].va_flags |= V_ADP_PROBED;
#ifndef VGA_NO_BIOS
biosadapter[V_ADP_PRIMARY].va_initial_bios_mode =
readb(BIOS_PADDRTOVADDR(0x449));
#else
biosadapter[V_ADP_PRIMARY].va_initial_bios_mode = 3; /* XXX */
#endif
biosadapter[V_ADP_PRIMARY].va_mode =
biosadapter[V_ADP_PRIMARY].va_initial_mode =
map_bios_mode_num(biosadapter[V_ADP_PRIMARY].va_type,
biosadapter[V_ADP_PRIMARY].va_flags & V_ADP_COLOR,
biosadapter[V_ADP_PRIMARY].va_initial_bios_mode);
} else {
biosadapter[V_ADP_PRIMARY] = biosadapter[V_ADP_SECONDARY];
biosadapter[V_ADP_SECONDARY].va_type = -1;
}
if (biosadapters == 0)
return biosadapters;
biosadapter[V_ADP_PRIMARY].va_unit = V_ADP_PRIMARY;
biosadapter[V_ADP_SECONDARY].va_unit = V_ADP_SECONDARY;
#if 0 /* we don't need these... */
fb_init_struct(&biosadapter[V_ADP_PRIMARY], ...);
fb_init_struct(&biosadapter[V_ADP_SECONDARY], ...);
#endif
#ifdef notyet
/*
* We cannot have two video adapter of the same type; there must be
* only one of color or mono adapter, or one each of them.
*/
if (biosadapters > 1) {
if (!((biosadapter[0].va_flags ^ biosadapter[1].va_flags)
& V_ADP_COLOR))
/* we have two mono or color adapters!! */
return (biosadapters = 0);
}
#endif
/*
* Ensure a zero start address. The registers are w/o
* for old hardware so it's too hard to relocate the active screen
* memory.
* This must be done before vga_save_state() for VGA.
*/
outb(biosadapter[V_ADP_PRIMARY].va_crtc_addr, 12);
outb(biosadapter[V_ADP_PRIMARY].va_crtc_addr + 1, 0);
outb(biosadapter[V_ADP_PRIMARY].va_crtc_addr, 13);
outb(biosadapter[V_ADP_PRIMARY].va_crtc_addr + 1, 0);
/* the video mode parameter table in EGA/VGA BIOS */
/* NOTE: there can be only one EGA/VGA, wheather color or mono,
* recognized by the video BIOS.
*/
if ((biosadapter[V_ADP_PRIMARY].va_type == KD_EGA) ||
(biosadapter[V_ADP_PRIMARY].va_type == KD_VGA)) {
adp = &biosadapter[V_ADP_PRIMARY];
} else if ((biosadapter[V_ADP_SECONDARY].va_type == KD_EGA) ||
(biosadapter[V_ADP_SECONDARY].va_type == KD_VGA)) {
adp = &biosadapter[V_ADP_SECONDARY];
} else {
adp = NULL;
}
bzero(mode_map, sizeof(mode_map));
if (adp != NULL) {
if (adp->va_type == KD_VGA) {
vga_save_state(adp, &adpstate, sizeof(adpstate));
#if defined(VGA_NO_BIOS) || defined(VGA_NO_MODE_CHANGE)
mode_map[adp->va_initial_mode] = adpstate.regs;
rows_offset = 1;
#else /* VGA_NO_BIOS || VGA_NO_MODE_CHANGE */
if (video_mode_ptr == NULL) {
mode_map[adp->va_initial_mode] = adpstate.regs;
rows_offset = 1;
} else {
/* discard the table if we are not familiar with it... */
map_mode_table(mode_map, video_mode_ptr, M_VGA_CG320 + 1);
mp = get_mode_param(adp->va_initial_mode);
if (mp != NULL)
bcopy(mp, adpstate2.regs, sizeof(adpstate2.regs));
switch (comp_adpregs(adpstate.regs, mp)) {
case COMP_IDENTICAL:
/*
* OK, this parameter table looks reasonably familiar
* to us...
*/
/*
* This is a kludge for Toshiba DynaBook SS433
* whose BIOS video mode table entry has the actual #
* of rows at the offset 1; BIOSes from other
* manufacturers store the # of rows - 1 there. XXX
*/
rows_offset = adpstate.regs[1] + 1 - mp[1];
break;
case COMP_SIMILAR:
/*
* Not exactly the same, but similar enough to be
* trusted. However, use the saved register values
* for the initial mode and other modes which are
* based on the initial mode.
*/
mode_map[adp->va_initial_mode] = adpstate.regs;
rows_offset = adpstate.regs[1] + 1 - mp[1];
adpstate.regs[1] -= rows_offset - 1;
break;
case COMP_DIFFERENT:
default:
/*
* Don't use the parameter table in the BIOS, since
* even the BIOS doesn't use it for the initial mode.
* Restrict the tweaked modes to (in practice) 80x50
* from 80x25 with 400 scan lines, since the only safe
* tweak is changing the characters from 8x16 to 8x8.
*/
video_mode_ptr = NULL;
bzero(mode_map, sizeof(mode_map));
mode_map[adp->va_initial_mode] = adpstate.regs;
rows_offset = 1;
width = height = -1;
for (i = 0; i < nitems(bios_vmode); ++i) {
if (bios_vmode[i].vi_mode == adp->va_initial_mode) {
width = bios_vmode[i].vi_width;
height = bios_vmode[i].vi_height;
break;
}
}
for (i = 0; i < nitems(bios_vmode); ++i) {
if (bios_vmode[i].vi_mode != adp->va_initial_mode &&
map_mode_num(bios_vmode[i].vi_mode) ==
adp->va_initial_mode &&
(bios_vmode[i].vi_width != width ||
bios_vmode[i].vi_height != 2 * height)) {
bios_vmode[i].vi_mode = NA;
}
}
break;
}
}
#endif /* VGA_NO_BIOS || VGA_NO_MODE_CHANGE */
#ifndef VGA_NO_MODE_CHANGE
adp->va_flags |= V_ADP_MODECHANGE;
#endif
#ifndef VGA_NO_FONT_LOADING
adp->va_flags |= V_ADP_FONT;
#endif
} else if (adp->va_type == KD_EGA) {
#if defined(VGA_NO_BIOS) || defined(VGA_NO_MODE_CHANGE)
rows_offset = 1;
#else /* VGA_NO_BIOS || VGA_NO_MODE_CHANGE */
if (video_mode_ptr == NULL) {
rows_offset = 1;
} else {
map_mode_table(mode_map, video_mode_ptr, M_ENH_C80x25 + 1);
/* XXX how can one validate the EGA table... */
mp = get_mode_param(adp->va_initial_mode);
if (mp != NULL) {
adp->va_flags |= V_ADP_MODECHANGE;
#ifndef VGA_NO_FONT_LOADING
adp->va_flags |= V_ADP_FONT;
#endif
rows_offset = 1;
} else {
/*
* This is serious. We will not be able to switch video
* modes at all...
*/
video_mode_ptr = NULL;
bzero(mode_map, sizeof(mode_map));
rows_offset = 1;
}
}
#endif /* VGA_NO_BIOS || VGA_NO_MODE_CHANGE */
}
}
/* remove conflicting modes if we have more than one adapter */
if (biosadapters > 0) {
for (i = 0; i < biosadapters; ++i) {
if (!(biosadapter[i].va_flags & V_ADP_MODECHANGE))
continue;
clear_mode_map(&biosadapter[i], mode_map, M_VGA_CG320 + 1,
(biosadapter[i].va_flags & V_ADP_COLOR) ?
V_INFO_COLOR : 0);
if ((biosadapter[i].va_type == KD_VGA)
|| (biosadapter[i].va_type == KD_EGA)) {
biosadapter[i].va_io_base =
(biosadapter[i].va_flags & V_ADP_COLOR) ?
IO_VGA : IO_MDA;
biosadapter[i].va_io_size = 32;
}
}
}
#if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE)
/*
* Attempt to determine the real character width for each mode. 9 wide
* is supposed to be standard for EGA mono mode and most VGA text modes,
* but some hardware doesn't support it, so dynamic configuration is
* needed. Bit 0 in sequencer register 1 is supposed control the width
* (set = 8), but this is unreliable too. Trust that 0 in the sequencer
* bit means 9 wide after verifying that 9 is consistent with some CRTC
* timing. The ratio (Horizontal Total) / (Horizontal Displayed) is
* about 1.2 in all standard 9-wide modes and should be about 9/8 larger
* again in similar 8-wide modes; in practice it is usually about 1.4
* times larger.
*/
for (i = 0; i < nitems(bios_vmode); ++i) {
if (bios_vmode[i].vi_mem_model == V_INFO_MM_TEXT &&
bios_vmode[i].vi_width != 90) {
mp = get_mode_param(map_mode_num(bios_vmode[i].vi_mode));
if (mp != NULL && !(mp[5] & 1) && mp[10] <= mp[11] * 125 / 100)
bios_vmode[i].vi_flags |= V_INFO_CWIDTH9;
}
}
#endif
/* buffer address */
vga_get_info(&biosadapter[V_ADP_PRIMARY],
biosadapter[V_ADP_PRIMARY].va_initial_mode, &info);
info.vi_flags &= ~V_INFO_LINEAR; /* XXX */
update_adapter_info(&biosadapter[V_ADP_PRIMARY], &info);
if (biosadapters > 1) {
vga_get_info(&biosadapter[V_ADP_SECONDARY],
biosadapter[V_ADP_SECONDARY].va_initial_mode, &info);
info.vi_flags &= ~V_INFO_LINEAR; /* XXX */
update_adapter_info(&biosadapter[V_ADP_SECONDARY], &info);
}
/*
* XXX: we should verify the following values for the primary adapter...
* crtc I/O port address: *(u_int16_t *)BIOS_PADDRTOVADDR(0x463);
* color/mono display: (*(u_int8_t *)BIOS_PADDRTOVADDR(0x487) & 0x02)
* ? 0 : V_ADP_COLOR;
* columns: *(u_int8_t *)BIOS_PADDRTOVADDR(0x44a);
* rows: *(u_int8_t *)BIOS_PADDRTOVADDR(0x484);
* font size: *(u_int8_t *)BIOS_PADDRTOVADDR(0x485);
* buffer size: *(u_int16_t *)BIOS_PADDRTOVADDR(0x44c);
*/
return biosadapters;
}
/* set the scan line length in pixel */
static int
set_line_length(video_adapter_t *adp, int pixel)
{
u_char *mp;
int ppw; /* pixels per word */
int bpl; /* bytes per line */
int count;
if ((adp->va_type != KD_VGA) && (adp->va_type != KD_EGA))
return ENODEV;
mp = get_mode_param(adp->va_mode);
if (mp == NULL)
return EINVAL;
switch (adp->va_info.vi_mem_model) {
case V_INFO_MM_PLANAR:
ppw = 16/(adp->va_info.vi_depth/adp->va_info.vi_planes);
count = howmany(pixel, ppw)/2;
bpl = (howmany(pixel, ppw)/2)*4;
break;
case V_INFO_MM_PACKED:
count = (pixel + 7)/8;
bpl = rounddown(pixel + 7, 8);
break;
case V_INFO_MM_TEXT:
count = (pixel + 7)/8; /* columns */
bpl = (pixel + 7)/8; /* columns */
break;
default:
return ENODEV;
}
if (mp[10 + 0x17] & 0x40) /* CRTC mode control reg */
count *= 2; /* byte mode */
outb(adp->va_crtc_addr, 0x13);
outb(adp->va_crtc_addr + 1, count);
adp->va_line_width = bpl;
return 0;
}
static int
set_display_start(video_adapter_t *adp, int x, int y)
{
int off; /* byte offset (graphics mode)/word offset (text mode) */
int poff; /* pixel offset */
int roff; /* row offset */
int ppb; /* pixels per byte */
if ((adp->va_type != KD_VGA) && (adp->va_type != KD_EGA))
x &= ~7;
if (adp->va_info.vi_flags & V_INFO_GRAPHICS) {
ppb = 8/(adp->va_info.vi_depth/adp->va_info.vi_planes);
off = y*adp->va_line_width + x/ppb;
roff = 0;
poff = x%ppb;
} else {
if ((adp->va_type == KD_VGA) || (adp->va_type == KD_EGA)) {
outb(TSIDX, 1);
if (inb(TSREG) & 1)
ppb = 9;
else
ppb = 8;
} else {
ppb = 8;
}
off = y/adp->va_info.vi_cheight*adp->va_line_width + x/ppb;
roff = y%adp->va_info.vi_cheight;
/* FIXME: is this correct? XXX */
if (ppb == 8)
poff = x%ppb;
else
poff = (x + 8)%ppb;
}
/* start address */
outb(adp->va_crtc_addr, 0xc); /* high */
outb(adp->va_crtc_addr + 1, off >> 8);
outb(adp->va_crtc_addr, 0xd); /* low */
outb(adp->va_crtc_addr + 1, off & 0xff);
/* horizontal pel pan */
if ((adp->va_type == KD_VGA) || (adp->va_type == KD_EGA)) {
inb(adp->va_crtc_addr + 6);
outb(ATC, 0x13 | 0x20);
outb(ATC, poff);
inb(adp->va_crtc_addr + 6);
outb(ATC, 0x20);
}
/* preset raw scan */
outb(adp->va_crtc_addr, 8);
outb(adp->va_crtc_addr + 1, roff);
adp->va_disp_start.x = x;
adp->va_disp_start.y = y;
return 0;
}
#ifndef VGA_NO_MODE_CHANGE
#if defined(__i386__) || defined(__amd64__) /* XXX */
static void
fill(int val, void *d, size_t size)
{
u_char *p = d;
while (size-- > 0)
*p++ = val;
}
#endif /* __i386__ */
static void
filll_io(int val, vm_offset_t d, size_t size)
{
while (size-- > 0) {
writel(d, val);
d += sizeof(u_int32_t);
}
}
#endif /* !VGA_NO_MODE_CHANGE */
/* entry points */
#if 0
static int
vga_nop(void)
{
return 0;
}
#endif
static int
vga_error(void)
{
return ENODEV;
}
static int
vga_probe(int unit, video_adapter_t **adpp, void *arg, int flags)
{
probe_adapters();
if (unit >= biosadapters)
return ENXIO;
*adpp = &biosadapter[unit];
return 0;
}
static int
vga_init(int unit, video_adapter_t *adp, int flags)
{
if ((unit >= biosadapters) || (adp == NULL) || !probe_done(adp))
return ENXIO;
if (!init_done(adp)) {
/* nothing to do really... */
adp->va_flags |= V_ADP_INITIALIZED;
}
if (!config_done(adp)) {
if (vid_register(adp) < 0)
return ENXIO;
adp->va_flags |= V_ADP_REGISTERED;
}
if (vga_sub_configure != NULL)
(*vga_sub_configure)(0);
return 0;
}
/*
* get_info():
* Return the video_info structure of the requested video mode.
*
* all adapters
*/
static int
vga_get_info(video_adapter_t *adp, int mode, video_info_t *info)
{
int i;
if (!vga_init_done)
return ENXIO;
mode = map_gen_mode_num(adp->va_type, adp->va_flags & V_ADP_COLOR, mode);
#ifndef VGA_NO_MODE_CHANGE
if (adp->va_flags & V_ADP_MODECHANGE) {
/*
* If the parameter table entry for this mode is not found,
* the mode is not supported...
*/
if (get_mode_param(mode) == NULL)
return EINVAL;
} else
#endif /* VGA_NO_MODE_CHANGE */
{
/*
* Even if we don't support video mode switching on this adapter,
* the information on the initial (thus current) video mode
* should be made available.
*/
if (mode != adp->va_initial_mode)
return EINVAL;
}
for (i = 0; bios_vmode[i].vi_mode != EOT; ++i) {
if (bios_vmode[i].vi_mode == NA)
continue;
if (mode == bios_vmode[i].vi_mode) {
*info = bios_vmode[i];
/* XXX */
info->vi_buffer_size = info->vi_window_size*info->vi_planes;
return 0;
}
}
return EINVAL;
}
/*
* query_mode():
* Find a video mode matching the requested parameters.
* Fields filled with 0 are considered "don't care" fields and
* match any modes.
*
* all adapters
*/
static int
vga_query_mode(video_adapter_t *adp, video_info_t *info)
{
int i;
if (!vga_init_done)
return ENXIO;
for (i = 0; bios_vmode[i].vi_mode != EOT; ++i) {
if (bios_vmode[i].vi_mode == NA)
continue;
if ((info->vi_width != 0)
&& (info->vi_width != bios_vmode[i].vi_width))
continue;
if ((info->vi_height != 0)
&& (info->vi_height != bios_vmode[i].vi_height))
continue;
if ((info->vi_cwidth != 0)
&& (info->vi_cwidth != bios_vmode[i].vi_cwidth))
continue;
if ((info->vi_cheight != 0)
&& (info->vi_cheight != bios_vmode[i].vi_cheight))
continue;
if ((info->vi_depth != 0)
&& (info->vi_depth != bios_vmode[i].vi_depth))
continue;
if ((info->vi_planes != 0)
&& (info->vi_planes != bios_vmode[i].vi_planes))
continue;
/* XXX: should check pixel format, memory model */
if ((info->vi_flags != 0)
&& (info->vi_flags != bios_vmode[i].vi_flags))
continue;
/* verify if this mode is supported on this adapter */
if (vga_get_info(adp, bios_vmode[i].vi_mode, info))
continue;
return 0;
}
return ENODEV;
}
/*
* set_mode():
* Change the video mode.
*
* EGA/VGA
*/
#ifndef VGA_NO_MODE_CHANGE
#ifdef VGA_WIDTH90
static void
set_width90(adp_state_t *params)
{
/*
* Based on code submitted by Kelly Yancey (kbyanc@freedomnet.com)
* and alexv@sui.gda.itesm.mx.
*/
params->regs[5] |= 1; /* toggle 8 pixel wide fonts */
params->regs[10+0x0] = 0x6b;
params->regs[10+0x1] = 0x59;
params->regs[10+0x2] = 0x5a;
params->regs[10+0x3] = 0x8e;
params->regs[10+0x4] = 0x5e;
params->regs[10+0x5] = 0x8a;
params->regs[10+0x13] = 45;
params->regs[35+0x13] = 0;
}
#endif /* VGA_WIDTH90 */
#endif /* !VGA_NO_MODE_CHANGE */
static int
vga_set_mode(video_adapter_t *adp, int mode)
{
#ifndef VGA_NO_MODE_CHANGE
video_info_t info;
adp_state_t params;
prologue(adp, V_ADP_MODECHANGE, ENODEV);
mode = map_gen_mode_num(adp->va_type,
adp->va_flags & V_ADP_COLOR, mode);
if (vga_get_info(adp, mode, &info))
return EINVAL;
#if VGA_DEBUG > 1
printf("vga_set_mode(): setting mode %d\n", mode);
#endif
params.sig = V_STATE_SIG;
bcopy(get_mode_param(mode), params.regs, sizeof(params.regs));
switch (mode) {
#ifdef VGA_WIDTH90
case M_VGA_C90x60: case M_VGA_M90x60:
set_width90(&params);
/* FALLTHROUGH */
#endif
case M_VGA_C80x60: case M_VGA_M80x60:
params.regs[2] = 0x08;
params.regs[19] = 0x47;
goto special_480l;
#ifdef VGA_WIDTH90
case M_VGA_C90x30: case M_VGA_M90x30:
set_width90(&params);
/* FALLTHROUGH */
#endif
case M_VGA_C80x30: case M_VGA_M80x30:
params.regs[19] = 0x4f;
special_480l:
params.regs[9] |= 0xc0;
params.regs[16] = 0x08;
params.regs[17] = 0x3e;
params.regs[26] = 0xea;
params.regs[28] = 0xdf;
params.regs[31] = 0xe7;
params.regs[32] = 0x04;
goto setup_mode;
#ifdef VGA_WIDTH90
case M_VGA_C90x43: case M_VGA_M90x43:
set_width90(&params);
/* FALLTHROUGH */
#endif
case M_ENH_C80x43: case M_ENH_B80x43:
params.regs[28] = 87;
goto special_80x50;
#ifdef VGA_WIDTH90
case M_VGA_C90x50: case M_VGA_M90x50:
set_width90(&params);
/* FALLTHROUGH */
#endif
case M_VGA_C80x50: case M_VGA_M80x50:
special_80x50:
params.regs[2] = 8;
params.regs[19] = 7;
goto setup_mode;
#ifdef VGA_WIDTH90
case M_VGA_C90x25: case M_VGA_M90x25:
set_width90(&params);
/* FALLTHROUGH */
#endif
case M_VGA_C40x25: case M_VGA_C80x25:
case M_VGA_M80x25:
case M_B40x25: case M_C40x25:
case M_B80x25: case M_C80x25:
case M_ENH_B40x25: case M_ENH_C40x25:
case M_ENH_B80x25: case M_ENH_C80x25:
case M_EGAMONO80x25:
setup_mode:
vga_load_state(adp, &params);
break;
case M_VGA_MODEX:
/* "unchain" the VGA mode */
params.regs[5-1+0x04] &= 0xf7;
params.regs[5-1+0x04] |= 0x04;
/* turn off doubleword mode */
params.regs[10+0x14] &= 0xbf;
/* turn off word addressing */
params.regs[10+0x17] |= 0x40;
/* set logical screen width */
params.regs[10+0x13] = 80;
/* set 240 lines */
params.regs[10+0x11] = 0x2c;
params.regs[10+0x06] = 0x0d;
params.regs[10+0x07] = 0x3e;
params.regs[10+0x10] = 0xea;
params.regs[10+0x11] = 0xac;
params.regs[10+0x12] = 0xdf;
params.regs[10+0x15] = 0xe7;
params.regs[10+0x16] = 0x06;
/* set vertical sync polarity to reflect aspect ratio */
params.regs[9] = 0xe3;
goto setup_grmode;
case M_BG320: case M_CG320: case M_BG640:
case M_CG320_D: case M_CG640_E:
case M_CG640x350: case M_ENH_CG640:
case M_BG640x480: case M_CG640x480: case M_VGA_CG320:
setup_grmode:
vga_load_state(adp, &params);
break;
default:
return EINVAL;
}
adp->va_mode = mode;
info.vi_flags &= ~V_INFO_LINEAR; /* XXX */
update_adapter_info(adp, &info);
/* move hardware cursor out of the way */
vidd_set_hw_cursor(adp, -1, -1);
return 0;
#else /* VGA_NO_MODE_CHANGE */
return ENODEV;
#endif /* VGA_NO_MODE_CHANGE */
}
#ifndef VGA_NO_FONT_LOADING
static void
set_font_mode(video_adapter_t *adp, u_char *buf)
{
u_char *mp;
int s;
s = splhigh();
/* save register values */
if (adp->va_type == KD_VGA) {
outb(TSIDX, 0x02); buf[0] = inb(TSREG);
outb(TSIDX, 0x04); buf[1] = inb(TSREG);
outb(GDCIDX, 0x04); buf[2] = inb(GDCREG);
outb(GDCIDX, 0x05); buf[3] = inb(GDCREG);
outb(GDCIDX, 0x06); buf[4] = inb(GDCREG);
inb(adp->va_crtc_addr + 6);
outb(ATC, 0x10); buf[5] = inb(ATC + 1);
} else /* if (adp->va_type == KD_EGA) */ {
/*
* EGA cannot be read; copy parameters from the mode parameter
* table.
*/
mp = get_mode_param(adp->va_mode);
buf[0] = mp[5 + 0x02 - 1];
buf[1] = mp[5 + 0x04 - 1];
buf[2] = mp[55 + 0x04];
buf[3] = mp[55 + 0x05];
buf[4] = mp[55 + 0x06];
buf[5] = mp[35 + 0x10];
}
/* setup vga for loading fonts */
inb(adp->va_crtc_addr + 6); /* reset flip-flop */
outb(ATC, 0x10); outb(ATC, buf[5] & ~0x01);
inb(adp->va_crtc_addr + 6); /* reset flip-flop */
outb(ATC, 0x20); /* enable palette */
#ifdef VGA_SLOW_IOACCESS
#ifdef VGA_ALT_SEQACCESS
outb(TSIDX, 0x00); outb(TSREG, 0x01);
#endif
outb(TSIDX, 0x02); outb(TSREG, 0x04);
outb(TSIDX, 0x04); outb(TSREG, 0x07);
#ifdef VGA_ALT_SEQACCESS
outb(TSIDX, 0x00); outb(TSREG, 0x03);
#endif
outb(GDCIDX, 0x04); outb(GDCREG, 0x02);
outb(GDCIDX, 0x05); outb(GDCREG, 0x00);
outb(GDCIDX, 0x06); outb(GDCREG, 0x04);
#else /* VGA_SLOW_IOACCESS */
#ifdef VGA_ALT_SEQACCESS
outw(TSIDX, 0x0100);
#endif
outw(TSIDX, 0x0402);
outw(TSIDX, 0x0704);
#ifdef VGA_ALT_SEQACCESS
outw(TSIDX, 0x0300);
#endif
outw(GDCIDX, 0x0204);
outw(GDCIDX, 0x0005);
outw(GDCIDX, 0x0406); /* addr = a0000, 64kb */
#endif /* VGA_SLOW_IOACCESS */
splx(s);
}
static void
set_normal_mode(video_adapter_t *adp, u_char *buf)
{
int s;
s = splhigh();
/* setup vga for normal operation mode again */
inb(adp->va_crtc_addr + 6); /* reset flip-flop */
outb(ATC, 0x10); outb(ATC, buf[5]);
inb(adp->va_crtc_addr + 6); /* reset flip-flop */
outb(ATC, 0x20); /* enable palette */
#ifdef VGA_SLOW_IOACCESS
#ifdef VGA_ALT_SEQACCESS
outb(TSIDX, 0x00); outb(TSREG, 0x01);
#endif
outb(TSIDX, 0x02); outb(TSREG, buf[0]);
outb(TSIDX, 0x04); outb(TSREG, buf[1]);
#ifdef VGA_ALT_SEQACCESS
outb(TSIDX, 0x00); outb(TSREG, 0x03);
#endif
outb(GDCIDX, 0x04); outb(GDCREG, buf[2]);
outb(GDCIDX, 0x05); outb(GDCREG, buf[3]);
if (adp->va_crtc_addr == MONO_CRTC) {
outb(GDCIDX, 0x06); outb(GDCREG,(buf[4] & 0x03) | 0x08);
} else {
outb(GDCIDX, 0x06); outb(GDCREG,(buf[4] & 0x03) | 0x0c);
}
#else /* VGA_SLOW_IOACCESS */
#ifdef VGA_ALT_SEQACCESS
outw(TSIDX, 0x0100);
#endif
outw(TSIDX, 0x0002 | (buf[0] << 8));
outw(TSIDX, 0x0004 | (buf[1] << 8));
#ifdef VGA_ALT_SEQACCESS
outw(TSIDX, 0x0300);
#endif
outw(GDCIDX, 0x0004 | (buf[2] << 8));
outw(GDCIDX, 0x0005 | (buf[3] << 8));
if (adp->va_crtc_addr == MONO_CRTC)
outw(GDCIDX, 0x0006 | (((buf[4] & 0x03) | 0x08)<<8));
else
outw(GDCIDX, 0x0006 | (((buf[4] & 0x03) | 0x0c)<<8));
#endif /* VGA_SLOW_IOACCESS */
splx(s);
}
#endif /* VGA_NO_FONT_LOADING */
/*
* save_font():
* Read the font data in the requested font page from the video adapter.
*
* EGA/VGA
*/
static int
vga_save_font(video_adapter_t *adp, int page, int fontsize, int fontwidth,
u_char *data, int ch, int count)
{
#ifndef VGA_NO_FONT_LOADING
u_char buf[PARAM_BUFSIZE];
vm_offset_t segment;
int c;
#ifdef VGA_ALT_SEQACCESS
int s;
u_char val = 0;
#endif
prologue(adp, V_ADP_FONT, ENODEV);
if (fontsize < 14) {
/* FONT_8 */
fontsize = 8;
} else if (fontsize >= 32) {
fontsize = 32;
} else if (fontsize >= 16) {
/* FONT_16 */
fontsize = 16;
} else {
/* FONT_14 */
fontsize = 14;
}
if (page < 0 || page >= 8 || fontwidth != 8)
return EINVAL;
segment = FONT_BUF + 0x4000*page;
if (page > 3)
segment -= 0xe000;
#ifdef VGA_ALT_SEQACCESS
if (adp->va_type == KD_VGA) { /* what about EGA? XXX */
s = splhigh();
outb(TSIDX, 0x00); outb(TSREG, 0x01);
outb(TSIDX, 0x01); val = inb(TSREG); /* disable screen */
outb(TSIDX, 0x01); outb(TSREG, val | 0x20);
outb(TSIDX, 0x00); outb(TSREG, 0x03);
splx(s);
}
#endif
set_font_mode(adp, buf);
if (fontsize == 32) {
bcopy_fromio((uintptr_t)segment + ch*32, data, fontsize*count);
} else {
for (c = ch; count > 0; ++c, --count) {
bcopy_fromio((uintptr_t)segment + c*32, data, fontsize);
data += fontsize;
}
}
set_normal_mode(adp, buf);
#ifdef VGA_ALT_SEQACCESS
if (adp->va_type == KD_VGA) {
s = splhigh();
outb(TSIDX, 0x00); outb(TSREG, 0x01);
outb(TSIDX, 0x01); outb(TSREG, val & 0xdf); /* enable screen */
outb(TSIDX, 0x00); outb(TSREG, 0x03);
splx(s);
}
#endif
return 0;
#else /* VGA_NO_FONT_LOADING */
return ENODEV;
#endif /* VGA_NO_FONT_LOADING */
}
/*
* load_font():
* Set the font data in the requested font page.
* NOTE: it appears that some recent video adapters do not support
* the font page other than 0... XXX
*
* EGA/VGA
*/
static int
vga_load_font(video_adapter_t *adp, int page, int fontsize, int fontwidth,
u_char *data, int ch, int count)
{
#ifndef VGA_NO_FONT_LOADING
u_char buf[PARAM_BUFSIZE];
vm_offset_t segment;
int c;
#ifdef VGA_ALT_SEQACCESS
int s;
u_char val = 0;
#endif
prologue(adp, V_ADP_FONT, ENODEV);
if (fontsize < 14) {
/* FONT_8 */
fontsize = 8;
} else if (fontsize >= 32) {
fontsize = 32;
} else if (fontsize >= 16) {
/* FONT_16 */
fontsize = 16;
} else {
/* FONT_14 */
fontsize = 14;
}
if (page < 0 || page >= 8 || fontwidth != 8)
return EINVAL;
segment = FONT_BUF + 0x4000*page;
if (page > 3)
segment -= 0xe000;
#ifdef VGA_ALT_SEQACCESS
if (adp->va_type == KD_VGA) { /* what about EGA? XXX */
s = splhigh();
outb(TSIDX, 0x00); outb(TSREG, 0x01);
outb(TSIDX, 0x01); val = inb(TSREG); /* disable screen */
outb(TSIDX, 0x01); outb(TSREG, val | 0x20);
outb(TSIDX, 0x00); outb(TSREG, 0x03);
splx(s);
}
#endif
set_font_mode(adp, buf);
if (fontsize == 32) {
bcopy_toio(data, (uintptr_t)segment + ch*32, fontsize*count);
} else {
for (c = ch; count > 0; ++c, --count) {
bcopy_toio(data, (uintptr_t)segment + c*32, fontsize);
data += fontsize;
}
}
set_normal_mode(adp, buf);
#ifdef VGA_ALT_SEQACCESS
if (adp->va_type == KD_VGA) {
s = splhigh();
outb(TSIDX, 0x00); outb(TSREG, 0x01);
outb(TSIDX, 0x01); outb(TSREG, val & 0xdf); /* enable screen */
outb(TSIDX, 0x00); outb(TSREG, 0x03);
splx(s);
}
#endif
return 0;
#else /* VGA_NO_FONT_LOADING */
return ENODEV;
#endif /* VGA_NO_FONT_LOADING */
}
/*
* show_font():
* Activate the requested font page.
* NOTE: it appears that some recent video adapters do not support
* the font page other than 0... XXX
*
* EGA/VGA
*/
static int
vga_show_font(video_adapter_t *adp, int page)
{
#ifndef VGA_NO_FONT_LOADING
static u_char cg[] = { 0x00, 0x05, 0x0a, 0x0f, 0x30, 0x35, 0x3a, 0x3f };
int s;
prologue(adp, V_ADP_FONT, ENODEV);
if (page < 0 || page >= 8)
return EINVAL;
s = splhigh();
outb(TSIDX, 0x03); outb(TSREG, cg[page]);
splx(s);
return 0;
#else /* VGA_NO_FONT_LOADING */
return ENODEV;
#endif /* VGA_NO_FONT_LOADING */
}
/*
* save_palette():
* Read DAC values. The values have expressed in 8 bits.
*
* VGA
*/
static int
vga_save_palette(video_adapter_t *adp, u_char *palette)
{
int bits;
int i;
prologue(adp, V_ADP_PALETTE, ENODEV);
/*
* We store 8 bit values in the palette buffer, while the standard
* VGA has 6 bit DAC .
*/
outb(PALRADR, 0x00);
bits = (adp->va_flags & V_ADP_DAC8) != 0 ? 0 : 2;
for (i = 0; i < 256*3; ++i)
palette[i] = inb(PALDATA) << bits;
inb(adp->va_crtc_addr + 6); /* reset flip/flop */
return 0;
}
static int
vga_save_palette2(video_adapter_t *adp, int base, int count,
u_char *r, u_char *g, u_char *b)
{
int bits;
int i;
prologue(adp, V_ADP_PALETTE, ENODEV);
outb(PALRADR, base);
bits = (adp->va_flags & V_ADP_DAC8) != 0 ? 0 : 2;
for (i = 0; i < count; ++i) {
r[i] = inb(PALDATA) << bits;
g[i] = inb(PALDATA) << bits;
b[i] = inb(PALDATA) << bits;
}
inb(adp->va_crtc_addr + 6); /* reset flip/flop */
return 0;
}
/*
* load_palette():
* Set DAC values.
*
* VGA
*/
static int
vga_load_palette(video_adapter_t *adp, u_char *palette)
{
int bits;
int i;
prologue(adp, V_ADP_PALETTE, ENODEV);
outb(PIXMASK, 0xff); /* no pixelmask */
outb(PALWADR, 0x00);
bits = (adp->va_flags & V_ADP_DAC8) != 0 ? 0 : 2;
for (i = 0; i < 256*3; ++i)
outb(PALDATA, palette[i] >> bits);
inb(adp->va_crtc_addr + 6); /* reset flip/flop */
outb(ATC, 0x20); /* enable palette */
return 0;
}
static int
vga_load_palette2(video_adapter_t *adp, int base, int count,
u_char *r, u_char *g, u_char *b)
{
int bits;
int i;
prologue(adp, V_ADP_PALETTE, ENODEV);
outb(PIXMASK, 0xff); /* no pixelmask */
outb(PALWADR, base);
bits = (adp->va_flags & V_ADP_DAC8) != 0 ? 0 : 2;
for (i = 0; i < count; ++i) {
outb(PALDATA, r[i] >> bits);
outb(PALDATA, g[i] >> bits);
outb(PALDATA, b[i] >> bits);
}
inb(adp->va_crtc_addr + 6); /* reset flip/flop */
outb(ATC, 0x20); /* enable palette */
return 0;
}
/*
* set_border():
* Change the border color.
*
* CGA/EGA/VGA
*/
static int
vga_set_border(video_adapter_t *adp, int color)
{
prologue(adp, V_ADP_BORDER, ENODEV);
switch (adp->va_type) {
case KD_EGA:
case KD_VGA:
inb(adp->va_crtc_addr + 6); /* reset flip-flop */
outb(ATC, 0x31); outb(ATC, color & 0xff);
break;
case KD_CGA:
outb(adp->va_crtc_addr + 5, color & 0x0f); /* color select register */
break;
case KD_MONO:
case KD_HERCULES:
default:
break;
}
return 0;
}
/*
* save_state():
* Read video register values.
* NOTE: this function only reads the standard EGA/VGA registers.
* any extra/extended registers of SVGA adapters are not saved.
*
* VGA
*/
static int
vga_save_state(video_adapter_t *adp, void *p, size_t size)
{
video_info_t info;
u_char *buf;
int crtc_addr;
int i, j;
int s;
if (size == 0) {
/* return the required buffer size */
prologue(adp, V_ADP_STATESAVE, 0);
return sizeof(adp_state_t);
} else {
prologue(adp, V_ADP_STATESAVE, ENODEV);
if (size < sizeof(adp_state_t))
return EINVAL;
}
((adp_state_t *)p)->sig = V_STATE_SIG;
buf = ((adp_state_t *)p)->regs;
bzero(buf, V_MODE_PARAM_SIZE);
crtc_addr = adp->va_crtc_addr;
s = splhigh();
outb(TSIDX, 0x00); outb(TSREG, 0x01); /* stop sequencer */
for (i = 0, j = 5; i < 4; i++) {
outb(TSIDX, i + 1);
buf[j++] = inb(TSREG);
}
buf[9] = inb(MISC + 10); /* dot-clock */
outb(TSIDX, 0x00); outb(TSREG, 0x03); /* start sequencer */
for (i = 0, j = 10; i < 25; i++) { /* crtc */
outb(crtc_addr, i);
buf[j++] = inb(crtc_addr + 1);
}
for (i = 0, j = 35; i < 20; i++) { /* attribute ctrl */
inb(crtc_addr + 6); /* reset flip-flop */
outb(ATC, i);
buf[j++] = inb(ATC + 1);
}
for (i = 0, j = 55; i < 9; i++) { /* graph data ctrl */
outb(GDCIDX, i);
buf[j++] = inb(GDCREG);
}
inb(crtc_addr + 6); /* reset flip-flop */
outb(ATC, 0x20); /* enable palette */
splx(s);
#if 1
if (vga_get_info(adp, adp->va_mode, &info) == 0) {
if (info.vi_flags & V_INFO_GRAPHICS) {
buf[0] = info.vi_width/info.vi_cwidth; /* COLS */
buf[1] = info.vi_height/info.vi_cheight - 1; /* ROWS */
} else {
buf[0] = info.vi_width; /* COLS */
buf[1] = info.vi_height - 1; /* ROWS */
}
buf[2] = info.vi_cheight; /* POINTS */
}
#else
buf[0] = readb(BIOS_PADDRTOVADDR(0x44a)); /* COLS */
buf[1] = readb(BIOS_PADDRTOVADDR(0x484)); /* ROWS */
buf[2] = readb(BIOS_PADDRTOVADDR(0x485)); /* POINTS */
buf[3] = readb(BIOS_PADDRTOVADDR(0x44c));
buf[4] = readb(BIOS_PADDRTOVADDR(0x44d));
#endif
return 0;
}
/*
* load_state():
* Set video registers at once.
* NOTE: this function only updates the standard EGA/VGA registers.
* any extra/extended registers of SVGA adapters are not changed.
*
* EGA/VGA
*/
static int
vga_load_state(video_adapter_t *adp, void *p)
{
u_char *buf;
int crtc_addr;
int s;
int i;
prologue(adp, V_ADP_STATELOAD, ENODEV);
if (((adp_state_t *)p)->sig != V_STATE_SIG)
return EINVAL;
buf = ((adp_state_t *)p)->regs;
crtc_addr = adp->va_crtc_addr;
#if VGA_DEBUG > 1
dump_buffer(buf, V_MODE_PARAM_SIZE);
#endif
s = splhigh();
outb(TSIDX, 0x00); outb(TSREG, 0x01); /* stop sequencer */
for (i = 0; i < 4; ++i) { /* program sequencer */
outb(TSIDX, i + 1);
outb(TSREG, buf[i + 5]);
}
outb(MISC, buf[9]); /* set dot-clock */
outb(TSIDX, 0x00); outb(TSREG, 0x03); /* start sequencer */
outb(crtc_addr, 0x11);
outb(crtc_addr + 1, inb(crtc_addr + 1) & 0x7F);
for (i = 0; i < 25; ++i) { /* program crtc */
outb(crtc_addr, i);
outb(crtc_addr + 1, buf[i + 10]);
}
inb(crtc_addr+6); /* reset flip-flop */
for (i = 0; i < 20; ++i) { /* program attribute ctrl */
outb(ATC, i);
outb(ATC, buf[i + 35]);
}
for (i = 0; i < 9; ++i) { /* program graph data ctrl */
outb(GDCIDX, i);
outb(GDCREG, buf[i + 55]);
}
inb(crtc_addr + 6); /* reset flip-flop */
outb(ATC, 0x20); /* enable palette */
#ifdef notyet /* a temporary workaround for kernel panic, XXX */
#ifndef VGA_NO_BIOS
if (adp->va_unit == V_ADP_PRIMARY) {
writeb(BIOS_PADDRTOVADDR(0x44a), buf[0]); /* COLS */
writeb(BIOS_PADDRTOVADDR(0x484), buf[1] + rows_offset - 1); /* ROWS */
writeb(BIOS_PADDRTOVADDR(0x485), buf[2]); /* POINTS */
#if 0
writeb(BIOS_PADDRTOVADDR(0x44c), buf[3]);
writeb(BIOS_PADDRTOVADDR(0x44d), buf[4]);
#endif
}
#endif /* VGA_NO_BIOS */
#endif /* notyet */
splx(s);
return 0;
}
/*
* set_origin():
* Change the origin (window mapping) of the banked frame buffer.
*/
static int
vga_set_origin(video_adapter_t *adp, off_t offset)
{
/*
* The standard video modes do not require window mapping;
* always return error.
*/
return ENODEV;
}
/*
* read_hw_cursor():
* Read the position of the hardware text cursor.
*
* all adapters
*/
static int
vga_read_hw_cursor(video_adapter_t *adp, int *col, int *row)
{
u_int16_t off;
int s;
if (!vga_init_done)
return ENXIO;
if (adp->va_info.vi_flags & V_INFO_GRAPHICS)
return ENODEV;
s = spltty();
outb(adp->va_crtc_addr, 14);
off = inb(adp->va_crtc_addr + 1);
outb(adp->va_crtc_addr, 15);
off = (off << 8) | inb(adp->va_crtc_addr + 1);
splx(s);
*row = off / adp->va_info.vi_width;
*col = off % adp->va_info.vi_width;
return 0;
}
/*
* set_hw_cursor():
* Move the hardware text cursor. If col and row are both -1,
* the cursor won't be shown.
*
* all adapters
*/
static int
vga_set_hw_cursor(video_adapter_t *adp, int col, int row)
{
u_int16_t off;
int s;
if (!vga_init_done)
return ENXIO;
if ((col == -1) && (row == -1)) {
off = -1;
} else {
if (adp->va_info.vi_flags & V_INFO_GRAPHICS)
return ENODEV;
off = row*adp->va_info.vi_width + col;
}
s = spltty();
outb(adp->va_crtc_addr, 14);
outb(adp->va_crtc_addr + 1, off >> 8);
outb(adp->va_crtc_addr, 15);
outb(adp->va_crtc_addr + 1, off & 0x00ff);
splx(s);
return 0;
}
/*
* set_hw_cursor_shape():
* Change the shape of the hardware text cursor. If the height is
* zero or negative, the cursor won't be shown.
*
* all adapters
*/
static int
vga_set_hw_cursor_shape(video_adapter_t *adp, int base, int height,
int celsize, int blink)
{
int s;
if (!vga_init_done)
return ENXIO;
s = spltty();
switch (adp->va_type) {
case KD_VGA:
case KD_CGA:
case KD_MONO:
case KD_HERCULES:
default:
if (height <= 0) {
/* make the cursor invisible */
outb(adp->va_crtc_addr, 10);
outb(adp->va_crtc_addr + 1, 32);
outb(adp->va_crtc_addr, 11);
outb(adp->va_crtc_addr + 1, 0);
} else {
outb(adp->va_crtc_addr, 10);
outb(adp->va_crtc_addr + 1, celsize - base - height);
outb(adp->va_crtc_addr, 11);
outb(adp->va_crtc_addr + 1, celsize - base - 1);
}
break;
case KD_EGA:
if (height <= 0) {
/* make the cursor invisible */
outb(adp->va_crtc_addr, 10);
outb(adp->va_crtc_addr + 1, celsize);
outb(adp->va_crtc_addr, 11);
outb(adp->va_crtc_addr + 1, 0);
} else {
outb(adp->va_crtc_addr, 10);
outb(adp->va_crtc_addr + 1, celsize - base - height);
outb(adp->va_crtc_addr, 11);
outb(adp->va_crtc_addr + 1, celsize - base);
}
break;
}
splx(s);
return 0;
}
/*
* blank_display()
* Put the display in power save/power off mode.
*
* all adapters
*/
static int
vga_blank_display(video_adapter_t *adp, int mode)
{
u_char val;
int s;
s = splhigh();
switch (adp->va_type) {
case KD_VGA:
switch (mode) {
case V_DISPLAY_SUSPEND:
case V_DISPLAY_STAND_BY:
outb(TSIDX, 0x01);
val = inb(TSREG);
outb(TSIDX, 0x01);
outb(TSREG, val | 0x20);
outb(adp->va_crtc_addr, 0x17);
val = inb(adp->va_crtc_addr + 1);
outb(adp->va_crtc_addr + 1, val & ~0x80);
break;
case V_DISPLAY_BLANK:
outb(TSIDX, 0x01);
val = inb(TSREG);
outb(TSIDX, 0x01);
outb(TSREG, val | 0x20);
break;
case V_DISPLAY_ON:
outb(TSIDX, 0x01);
val = inb(TSREG);
outb(TSIDX, 0x01);
outb(TSREG, val & 0xDF);
outb(adp->va_crtc_addr, 0x17);
val = inb(adp->va_crtc_addr + 1);
outb(adp->va_crtc_addr + 1, val | 0x80);
break;
}
break;
case KD_EGA:
/* no support yet */
splx(s);
return ENODEV;
case KD_CGA:
switch (mode) {
case V_DISPLAY_SUSPEND:
case V_DISPLAY_STAND_BY:
case V_DISPLAY_BLANK:
outb(adp->va_crtc_addr + 4, 0x25);
break;
case V_DISPLAY_ON:
outb(adp->va_crtc_addr + 4, 0x2d);
break;
}
break;
case KD_MONO:
case KD_HERCULES:
switch (mode) {
case V_DISPLAY_SUSPEND:
case V_DISPLAY_STAND_BY:
case V_DISPLAY_BLANK:
outb(adp->va_crtc_addr + 4, 0x21);
break;
case V_DISPLAY_ON:
outb(adp->va_crtc_addr + 4, 0x29);
break;
}
break;
default:
break;
}
splx(s);
return 0;
}
/*
* mmap():
* Mmap frame buffer.
*
* all adapters
*/
static int
vga_mmap_buf(video_adapter_t *adp, vm_ooffset_t offset, vm_paddr_t *paddr,
int prot, vm_memattr_t *memattr)
{
if (adp->va_info.vi_flags & V_INFO_LINEAR)
return -1;
#if VGA_DEBUG > 0
printf("vga_mmap_buf(): window:0x%jx, offset:0x%jx\n",
(uintmax_t)adp->va_info.vi_window, (uintmax_t)offset);
#endif
/* XXX: is this correct? */
if (offset > adp->va_window_size - PAGE_SIZE)
return -1;
*paddr = adp->va_info.vi_window + offset;
return 0;
}
#ifndef VGA_NO_MODE_CHANGE
static void
planar_fill(video_adapter_t *adp, int val)
{
int length;
int at; /* position in the frame buffer */
int l;
outw(GDCIDX, 0x0005); /* read mode 0, write mode 0 */
outw(GDCIDX, 0x0003); /* data rotate/function select */
outw(GDCIDX, 0x0f01); /* set/reset enable */
outw(GDCIDX, 0xff08); /* bit mask */
outw(GDCIDX, (val << 8) | 0x00); /* set/reset */
at = 0;
length = adp->va_line_width*adp->va_info.vi_height;
while (length > 0) {
l = imin(length, adp->va_window_size);
vidd_set_win_org(adp, at);
bzero_io(adp->va_window, l);
length -= l;
at += l;
}
outw(GDCIDX, 0x0000); /* set/reset */
outw(GDCIDX, 0x0001); /* set/reset enable */
}
static void
packed_fill(video_adapter_t *adp, int val)
{
int length;
int at; /* position in the frame buffer */
int l;
at = 0;
length = adp->va_line_width*adp->va_info.vi_height;
while (length > 0) {
l = imin(length, adp->va_window_size);
vidd_set_win_org(adp, at);
fill_io(val, adp->va_window, l);
length -= l;
at += l;
}
}
static void
direct_fill(video_adapter_t *adp, int val)
{
int length;
int at; /* position in the frame buffer */
int l;
at = 0;
length = adp->va_line_width*adp->va_info.vi_height;
while (length > 0) {
l = imin(length, adp->va_window_size);
vidd_set_win_org(adp, at);
switch (adp->va_info.vi_pixel_size) {
case sizeof(u_int16_t):
fillw_io(val, adp->va_window, l/sizeof(u_int16_t));
break;
case 3:
/* FIXME */
break;
case sizeof(u_int32_t):
filll_io(val, adp->va_window, l/sizeof(u_int32_t));
break;
}
length -= l;
at += l;
}
}
static int
vga_clear(video_adapter_t *adp)
{
switch (adp->va_info.vi_mem_model) {
case V_INFO_MM_TEXT:
/* do nothing? XXX */
break;
case V_INFO_MM_PLANAR:
planar_fill(adp, 0);
break;
case V_INFO_MM_PACKED:
packed_fill(adp, 0);
break;
case V_INFO_MM_DIRECT:
direct_fill(adp, 0);
break;
}
return 0;
}
#ifdef notyet
static void
planar_fill_rect(video_adapter_t *adp, int val, int x, int y, int cx, int cy)
{
int banksize;
int bank;
int pos;
int offset; /* offset within window */
int bx;
int l;
outw(GDCIDX, 0x0005); /* read mode 0, write mode 0 */
outw(GDCIDX, 0x0003); /* data rotate/function select */
outw(GDCIDX, 0x0f01); /* set/reset enable */
outw(GDCIDX, 0xff08); /* bit mask */
outw(GDCIDX, (val << 8) | 0x00); /* set/reset */
banksize = adp->va_window_size;
bank = -1;
while (cy > 0) {
pos = adp->va_line_width*y + x/8;
if (bank != pos/banksize) {
vidd_set_win_org(adp, pos);
bank = pos/banksize;
}
offset = pos%banksize;
bx = (x + cx)/8 - x/8;
if (x % 8) {
outw(GDCIDX, ((0xff00 >> (x % 8)) & 0xff00) | 0x08);
writeb(adp->va_window + offset, 0);
++offset;
--bx;
if (offset >= banksize) {
offset = 0;
++bank; /* next bank */
vidd_set_win_org(adp, bank*banksize);
}
outw(GDCIDX, 0xff08); /* bit mask */
}
while (bx > 0) {
l = imin(bx, banksize);
bzero_io(adp->va_window + offset, l);
offset += l;
bx -= l;
if (offset >= banksize) {
offset = 0;
++bank; /* next bank */
vidd_set_win_org(adp, bank*banksize);
}
}
if ((x + cx) % 8) {
outw(GDCIDX, (~(0xff00 >> ((x + cx) % 8)) & 0xff00) | 0x08);
writeb(adp->va_window + offset, 0);
++offset;
if (offset >= banksize) {
offset = 0;
++bank; /* next bank */
vidd_set_win_org(adp, bank*banksize);
}
outw(GDCIDX, 0xff08); /* bit mask */
}
++y;
--cy;
}
outw(GDCIDX, 0xff08); /* bit mask */
outw(GDCIDX, 0x0000); /* set/reset */
outw(GDCIDX, 0x0001); /* set/reset enable */
}
static void
packed_fill_rect(video_adapter_t *adp, int val, int x, int y, int cx, int cy)
{
int banksize;
int bank;
int pos;
int offset; /* offset within window */
int end;
banksize = adp->va_window_size;
bank = -1;
cx *= adp->va_info.vi_pixel_size;
while (cy > 0) {
pos = adp->va_line_width*y + x*adp->va_info.vi_pixel_size;
if (bank != pos/banksize) {
vidd_set_win_org(adp, pos);
bank = pos/banksize;
}
offset = pos%banksize;
end = imin(offset + cx, banksize);
fill_io(val, adp->va_window + offset,
(end - offset)/adp->va_info.vi_pixel_size);
/* the line may cross the window boundary */
if (offset + cx > banksize) {
++bank; /* next bank */
vidd_set_win_org(adp, bank*banksize);
end = offset + cx - banksize;
fill_io(val, adp->va_window, end/adp->va_info.vi_pixel_size);
}
++y;
--cy;
}
}
static void
direct_fill_rect16(video_adapter_t *adp, int val, int x, int y, int cx, int cy)
{
int banksize;
int bank;
int pos;
int offset; /* offset within window */
int end;
/*
* XXX: the function assumes that banksize is a muliple of
* sizeof(u_int16_t).
*/
banksize = adp->va_window_size;
bank = -1;
cx *= sizeof(u_int16_t);
while (cy > 0) {
pos = adp->va_line_width*y + x*sizeof(u_int16_t);
if (bank != pos/banksize) {
vidd_set_win_org(adp, pos);
bank = pos/banksize;
}
offset = pos%banksize;
end = imin(offset + cx, banksize);
fillw_io(val, adp->va_window + offset,
(end - offset)/sizeof(u_int16_t));
/* the line may cross the window boundary */
if (offset + cx > banksize) {
++bank; /* next bank */
vidd_set_win_org(adp, bank*banksize);
end = offset + cx - banksize;
fillw_io(val, adp->va_window, end/sizeof(u_int16_t));
}
++y;
--cy;
}
}
static void
direct_fill_rect24(video_adapter_t *adp, int val, int x, int y, int cx, int cy)
{
int banksize;
int bank;
int pos;
int offset; /* offset within window */
int end;
int i;
int j;
u_int8_t b[3];
b[0] = val & 0x0000ff;
b[1] = (val >> 8) & 0x0000ff;
b[2] = (val >> 16) & 0x0000ff;
banksize = adp->va_window_size;
bank = -1;
cx *= 3;
while (cy > 0) {
pos = adp->va_line_width*y + x*3;
if (bank != pos/banksize) {
vidd_set_win_org(adp, pos);
bank = pos/banksize;
}
offset = pos%banksize;
end = imin(offset + cx, banksize);
for (i = 0, j = offset; j < end; i = (++i)%3, ++j) {
writeb(adp->va_window + j, b[i]);
}
/* the line may cross the window boundary */
if (offset + cx >= banksize) {
++bank; /* next bank */
vidd_set_win_org(adp, bank*banksize);
j = 0;
end = offset + cx - banksize;
for (; j < end; i = (++i)%3, ++j) {
writeb(adp->va_window + j, b[i]);
}
}
++y;
--cy;
}
}
static void
direct_fill_rect32(video_adapter_t *adp, int val, int x, int y, int cx, int cy)
{
int banksize;
int bank;
int pos;
int offset; /* offset within window */
int end;
/*
* XXX: the function assumes that banksize is a muliple of
* sizeof(u_int32_t).
*/
banksize = adp->va_window_size;
bank = -1;
cx *= sizeof(u_int32_t);
while (cy > 0) {
pos = adp->va_line_width*y + x*sizeof(u_int32_t);
if (bank != pos/banksize) {
vidd_set_win_org(adp, pos);
bank = pos/banksize;
}
offset = pos%banksize;
end = imin(offset + cx, banksize);
filll_io(val, adp->va_window + offset,
(end - offset)/sizeof(u_int32_t));
/* the line may cross the window boundary */
if (offset + cx > banksize) {
++bank; /* next bank */
vidd_set_win_org(adp, bank*banksize);
end = offset + cx - banksize;
filll_io(val, adp->va_window, end/sizeof(u_int32_t));
}
++y;
--cy;
}
}
static int
vga_fill_rect(video_adapter_t *adp, int val, int x, int y, int cx, int cy)
{
switch (adp->va_info.vi_mem_model) {
case V_INFO_MM_TEXT:
/* do nothing? XXX */
break;
case V_INFO_MM_PLANAR:
planar_fill_rect(adp, val, x, y, cx, cy);
break;
case V_INFO_MM_PACKED:
packed_fill_rect(adp, val, x, y, cx, cy);
break;
case V_INFO_MM_DIRECT:
switch (adp->va_info.vi_pixel_size) {
case sizeof(u_int16_t):
direct_fill_rect16(adp, val, x, y, cx, cy);
break;
case 3:
direct_fill_rect24(adp, val, x, y, cx, cy);
break;
case sizeof(u_int32_t):
direct_fill_rect32(adp, val, x, y, cx, cy);
break;
}
break;
}
return 0;
}
#else /* !notyet */
static int
vga_fill_rect(video_adapter_t *adp, int val, int x, int y, int cx, int cy)
{
return ENODEV;
}
#endif /* notyet */
static int
vga_bitblt(video_adapter_t *adp,...)
{
/* FIXME */
return ENODEV;
}
#endif /* !VGA_NO_MODE_CHANGE */
static int
get_palette(video_adapter_t *adp, int base, int count,
u_char *red, u_char *green, u_char *blue, u_char *trans)
{
u_char *r;
u_char *g;
u_char *b;
if (count < 0 || base < 0 || count > 256 || base > 256 ||
base + count > 256)
return EINVAL;
r = malloc(count*3, M_DEVBUF, M_WAITOK);
g = r + count;
b = g + count;
if (vga_save_palette2(adp, base, count, r, g, b)) {
free(r, M_DEVBUF);
return ENODEV;
}
copyout(r, red, count);
copyout(g, green, count);
copyout(b, blue, count);
if (trans != NULL) {
bzero(r, count);
copyout(r, trans, count);
}
free(r, M_DEVBUF);
return 0;
}
static int
set_palette(video_adapter_t *adp, int base, int count,
u_char *red, u_char *green, u_char *blue, u_char *trans)
{
u_char *r;
u_char *g;
u_char *b;
int err;
if (count < 0 || base < 0 || count > 256 || base > 256 ||
base + count > 256)
return EINVAL;
r = malloc(count*3, M_DEVBUF, M_WAITOK);
g = r + count;
b = g + count;
err = copyin(red, r, count);
if (!err)
err = copyin(green, g, count);
if (!err)
err = copyin(blue, b, count);
if (!err)
err = vga_load_palette2(adp, base, count, r, g, b);
free(r, M_DEVBUF);
return (err ? ENODEV : 0);
}
static int
vga_dev_ioctl(video_adapter_t *adp, u_long cmd, caddr_t arg)
{
switch (cmd) {
case FBIO_GETWINORG: /* get frame buffer window origin */
*(u_int *)arg = 0;
return 0;
case FBIO_SETWINORG: /* set frame buffer window origin */
return ENODEV;
case FBIO_SETDISPSTART: /* set display start address */
return (set_display_start(adp,
((video_display_start_t *)arg)->x,
((video_display_start_t *)arg)->y)
? ENODEV : 0);
case FBIO_SETLINEWIDTH: /* set scan line length in pixel */
return (set_line_length(adp, *(u_int *)arg) ? ENODEV : 0);
case FBIO_GETPALETTE: /* get color palette */
return get_palette(adp, ((video_color_palette_t *)arg)->index,
((video_color_palette_t *)arg)->count,
((video_color_palette_t *)arg)->red,
((video_color_palette_t *)arg)->green,
((video_color_palette_t *)arg)->blue,
((video_color_palette_t *)arg)->transparent);
case FBIO_SETPALETTE: /* set color palette */
return set_palette(adp, ((video_color_palette_t *)arg)->index,
((video_color_palette_t *)arg)->count,
((video_color_palette_t *)arg)->red,
((video_color_palette_t *)arg)->green,
((video_color_palette_t *)arg)->blue,
((video_color_palette_t *)arg)->transparent);
case FBIOGTYPE: /* get frame buffer type info. */
((struct fbtype *)arg)->fb_type = fb_type(adp->va_type);
((struct fbtype *)arg)->fb_height = adp->va_info.vi_height;
((struct fbtype *)arg)->fb_width = adp->va_info.vi_width;
((struct fbtype *)arg)->fb_depth = adp->va_info.vi_depth;
if ((adp->va_info.vi_depth <= 1) || (adp->va_info.vi_depth > 8))
((struct fbtype *)arg)->fb_cmsize = 0;
else
((struct fbtype *)arg)->fb_cmsize = 1 << adp->va_info.vi_depth;
((struct fbtype *)arg)->fb_size = adp->va_buffer_size;
return 0;
case FBIOGETCMAP: /* get color palette */
return get_palette(adp, ((struct fbcmap *)arg)->index,
((struct fbcmap *)arg)->count,
((struct fbcmap *)arg)->red,
((struct fbcmap *)arg)->green,
((struct fbcmap *)arg)->blue, NULL);
case FBIOPUTCMAP: /* set color palette */
return set_palette(adp, ((struct fbcmap *)arg)->index,
((struct fbcmap *)arg)->count,
((struct fbcmap *)arg)->red,
((struct fbcmap *)arg)->green,
((struct fbcmap *)arg)->blue, NULL);
default:
return fb_commonioctl(adp, cmd, arg);
}
}
static void
dump_buffer(u_char *buf, size_t len)
{
int i;
for(i = 0; i < len;) {
printf("%02x ", buf[i]);
if ((++i % 16) == 0)
printf("\n");
}
}
/*
* diag():
* Print some information about the video adapter and video modes,
* with requested level of details.
*
* all adapters
*/
static int
vga_diag(video_adapter_t *adp, int level)
{
u_char *mp;
#if FB_DEBUG > 1
video_info_t info;
int i;
#endif
if (!vga_init_done)
return ENXIO;
#if FB_DEBUG > 1
#ifndef VGA_NO_BIOS
printf("vga: RTC equip. code:0x%02x, DCC code:0x%02x\n",
rtcin(RTC_EQUIPMENT), readb(BIOS_PADDRTOVADDR(0x488)));
printf("vga: CRTC:0x%x, video option:0x%02x, ",
readw(BIOS_PADDRTOVADDR(0x463)),
readb(BIOS_PADDRTOVADDR(0x487)));
printf("rows:%d, cols:%d, font height:%d\n",
readb(BIOS_PADDRTOVADDR(0x44a)),
readb(BIOS_PADDRTOVADDR(0x484)) + 1,
readb(BIOS_PADDRTOVADDR(0x485)));
#endif /* VGA_NO_BIOS */
#if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE)
printf("vga: param table EGA/VGA:%p", video_mode_ptr);
printf(", CGA/MDA:%p\n", video_mode_ptr2);
printf("vga: rows_offset:%d\n", rows_offset);
#endif
#endif /* FB_DEBUG > 1 */
fb_dump_adp_info(VGA_DRIVER_NAME, adp, level);
#if FB_DEBUG > 1
if (adp->va_flags & V_ADP_MODECHANGE) {
for (i = 0; bios_vmode[i].vi_mode != EOT; ++i) {
if (bios_vmode[i].vi_mode == NA)
continue;
if (get_mode_param(bios_vmode[i].vi_mode) == NULL)
continue;
fb_dump_mode_info(VGA_DRIVER_NAME, adp, &bios_vmode[i], level);
}
} else {
vga_get_info(adp, adp->va_initial_mode, &info); /* shouldn't fail */
fb_dump_mode_info(VGA_DRIVER_NAME, adp, &info, level);
}
#endif /* FB_DEBUG > 1 */
if ((adp->va_type != KD_EGA) && (adp->va_type != KD_VGA))
return 0;
#if !defined(VGA_NO_BIOS) && !defined(VGA_NO_MODE_CHANGE)
if (video_mode_ptr == NULL)
printf("vga%d: %s: WARNING: video mode switching is not "
"fully supported on this adapter\n",
adp->va_unit, adp->va_name);
#endif
if (level <= 0)
return 0;
if (adp->va_type == KD_VGA) {
printf("VGA parameters upon power-up\n");
dump_buffer(adpstate.regs, sizeof(adpstate.regs));
printf("VGA parameters in BIOS for mode %d\n", adp->va_initial_mode);
dump_buffer(adpstate2.regs, sizeof(adpstate2.regs));
}
mp = get_mode_param(adp->va_initial_mode);
if (mp == NULL) /* this shouldn't be happening */
return 0;
printf("EGA/VGA parameters to be used for mode %d\n", adp->va_initial_mode);
dump_buffer(mp, V_MODE_PARAM_SIZE);
return 0;
}