497 lines
16 KiB
C
497 lines
16 KiB
C
|
#include "asm/io.h"
|
||
|
#include "drivers/vga.h"
|
||
|
#include "vga.h"
|
||
|
|
||
|
/*
|
||
|
* $Id$
|
||
|
* $Source$
|
||
|
*
|
||
|
* from the Linux kernel code base.
|
||
|
* orig by Ben Pfaff and Petr Vandrovec.
|
||
|
*
|
||
|
* modified by
|
||
|
* Steve M. Gehlbach <steve@kesa.com>
|
||
|
*
|
||
|
* NOTE: to change the horiz and vertical pixels,
|
||
|
* change the xres,yres,xres_virt,yres_virt setting
|
||
|
* in the screeninfo structure below. You may also need
|
||
|
* to change the border settings as well.
|
||
|
*
|
||
|
* Convert the screeninfo structure to data for
|
||
|
* writing to the vga registers
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
// prototypes
|
||
|
static int vga_decode_var(const struct screeninfo *var, struct vga_par *par);
|
||
|
static int vga_set_regs(const struct vga_par *par);
|
||
|
|
||
|
u8 read_seq_b(u16 addr) {
|
||
|
outb(addr,SEQ_I);
|
||
|
return inb(SEQ_D);
|
||
|
}
|
||
|
u8 read_gra_b(u16 addr) {
|
||
|
outb(addr,GRA_I);
|
||
|
return inb(GRA_D);
|
||
|
}
|
||
|
u8 read_crtc_b(u16 addr) {
|
||
|
outb(addr,CRT_IC);
|
||
|
return inb(CRT_DC);
|
||
|
}
|
||
|
u8 read_att_b(u16 addr) {
|
||
|
inb(IS1_RC);
|
||
|
inb(0x80);
|
||
|
outb(addr,ATT_IW);
|
||
|
return inb(ATT_R);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
From: The Frame Buffer Device
|
||
|
by Geert Uytterhoeven <geert@linux-m68k.org>
|
||
|
in the linux kernel docs.
|
||
|
|
||
|
The following picture summarizes all timings. The horizontal retrace time is
|
||
|
the sum of the left margin, the right margin and the hsync length, while the
|
||
|
vertical retrace time is the sum of the upper margin, the lower margin and the
|
||
|
vsync length.
|
||
|
|
||
|
+----------+---------------------------------------------+----------+-------+
|
||
|
| | ^ | | |
|
||
|
| | |upper_margin | | |
|
||
|
| | | | | |
|
||
|
+----------###############################################----------+-------+
|
||
|
| # ^ # | |
|
||
|
| # | # | |
|
||
|
| # | # | |
|
||
|
| # | # | |
|
||
|
| left # | # right | hsync |
|
||
|
| margin # | xres # margin | len |
|
||
|
|<-------->#<---------------+--------------------------->#<-------->|<----->|
|
||
|
| # | # | |
|
||
|
| # | # | |
|
||
|
| # | # | |
|
||
|
| # |yres # | |
|
||
|
| # | # | |
|
||
|
| # | # | |
|
||
|
| # | # | |
|
||
|
| # | # | |
|
||
|
| # | # | |
|
||
|
| # | # | |
|
||
|
| # | # | |
|
||
|
| # | # | |
|
||
|
| # | # | |
|
||
|
+----------###############################################----------+-------+
|
||
|
| | ^ | | |
|
||
|
| | |lower_margin | | |
|
||
|
| | | | | |
|
||
|
+----------+---------------------------------------------+----------+-------+
|
||
|
| | ^ | | |
|
||
|
| | |vsync_len | | |
|
||
|
| | | | | |
|
||
|
+----------+---------------------------------------------+----------+-------+
|
||
|
|
||
|
All horizontal timings are in number of dotclocks
|
||
|
(in picoseconds, 1E-12 s), and vertical timings in number of scanlines.
|
||
|
|
||
|
The vga uses the following fields:
|
||
|
|
||
|
- pixclock: pixel clock in ps (pico seconds)
|
||
|
- xres,yres,xres_v,yres_v
|
||
|
- left_margin: time from sync to picture
|
||
|
- right_margin: time from picture to sync
|
||
|
- upper_margin: time from sync to picture
|
||
|
- lower_margin: time from picture to sync
|
||
|
- hsync_len: length of horizontal sync
|
||
|
- vsync_len: length of vertical sync
|
||
|
|
||
|
*/
|
||
|
|
||
|
/* our display parameters per the above */
|
||
|
|
||
|
static const struct screeninfo vga_settings = {
|
||
|
640,400,640,400,/* xres,yres,xres_virt,yres_virt */
|
||
|
0,0, /* xoffset,yoffset */
|
||
|
4, /* bits_per_pixel NOT USED*/
|
||
|
0, /* greyscale ? */
|
||
|
{0,0,0}, /* R */
|
||
|
{0,0,0}, /* G */
|
||
|
{0,0,0}, /* B */
|
||
|
{0,0,0}, /* transparency */
|
||
|
0, /* standard pixel format */
|
||
|
0, // activate now
|
||
|
-1,-1, // height and width in mm
|
||
|
0, // accel flags
|
||
|
39721, // pixclock: 79442 -> 12.587 Mhz (NOT USED)
|
||
|
// 70616 -> 14.161
|
||
|
// 39721 -> 25.175
|
||
|
// 35308 -> 28.322
|
||
|
|
||
|
48, 16, 39, 8, // margins left,right,upper,lower
|
||
|
96, // hsync length
|
||
|
2, // vsync length
|
||
|
0, // sync polarity
|
||
|
0, // non interlaced, single mode
|
||
|
{0,0,0,0,0,0} // compatibility
|
||
|
};
|
||
|
|
||
|
// ALPHA-MODE
|
||
|
// Hard coded to BIOS VGA mode 3 (alpha color text)
|
||
|
// screen size settable in screeninfo structure
|
||
|
|
||
|
static int vga_decode_var(const struct screeninfo *var,
|
||
|
struct vga_par *par)
|
||
|
{
|
||
|
u8 VgaAttributeTable[16] =
|
||
|
{ 0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x014, 0x007, 0x038, 0x039, 0x03A, 0x03B, 0x03C, 0x03D, 0x03E, 0x03F};
|
||
|
|
||
|
u32 xres, right, hslen, left, xtotal;
|
||
|
u32 yres, lower, vslen, upper, ytotal;
|
||
|
u32 vxres, xoffset, vyres, yoffset;
|
||
|
u32 pos;
|
||
|
u8 r7, rMode;
|
||
|
int i;
|
||
|
|
||
|
xres = (var->xres + 7) & ~7;
|
||
|
vxres = (var->xres_virtual + 0xF) & ~0xF;
|
||
|
xoffset = (var->xoffset + 7) & ~7;
|
||
|
left = (var->left_margin + 7) & ~7;
|
||
|
right = (var->right_margin + 7) & ~7;
|
||
|
hslen = (var->hsync_len + 7) & ~7;
|
||
|
|
||
|
if (vxres < xres)
|
||
|
vxres = xres;
|
||
|
if (xres + xoffset > vxres)
|
||
|
xoffset = vxres - xres;
|
||
|
|
||
|
xres >>= 3;
|
||
|
right >>= 3;
|
||
|
hslen >>= 3;
|
||
|
left >>= 3;
|
||
|
vxres >>= 3;
|
||
|
xtotal = xres + right + hslen + left;
|
||
|
if (xtotal >= 256)
|
||
|
return VERROR; //xtotal too big
|
||
|
if (hslen > 32)
|
||
|
return VERROR; //hslen too big
|
||
|
if (right + hslen + left > 64)
|
||
|
return VERROR; //hblank too big
|
||
|
par->crtc[CRTC_H_TOTAL] = xtotal - 5;
|
||
|
par->crtc[CRTC_H_BLANK_START] = xres - 1;
|
||
|
par->crtc[CRTC_H_DISP] = xres - 1;
|
||
|
pos = xres + right;
|
||
|
par->crtc[CRTC_H_SYNC_START] = pos;
|
||
|
pos += hslen;
|
||
|
par->crtc[CRTC_H_SYNC_END] = (pos & 0x1F) | 0x20 ; //<--- stpc text mode p178
|
||
|
pos += left - 2; /* blank_end + 2 <= total + 5 */
|
||
|
par->crtc[CRTC_H_BLANK_END] = (pos & 0x1F) | 0x80;
|
||
|
if (pos & 0x20)
|
||
|
par->crtc[CRTC_H_SYNC_END] |= 0x80;
|
||
|
|
||
|
yres = var->yres;
|
||
|
lower = var->lower_margin;
|
||
|
vslen = var->vsync_len;
|
||
|
upper = var->upper_margin;
|
||
|
vyres = var->yres_virtual;
|
||
|
yoffset = var->yoffset;
|
||
|
|
||
|
if (yres > vyres)
|
||
|
vyres = yres;
|
||
|
if (vxres * vyres > 65536) {
|
||
|
vyres = 65536 / vxres;
|
||
|
if (vyres < yres)
|
||
|
return VERROR; // out of memory
|
||
|
}
|
||
|
if (yoffset + yres > vyres)
|
||
|
yoffset = vyres - yres;
|
||
|
|
||
|
if (var->vmode & VMODE_DOUBLE) {
|
||
|
yres <<= 1;
|
||
|
lower <<= 1;
|
||
|
vslen <<= 1;
|
||
|
upper <<= 1;
|
||
|
}
|
||
|
ytotal = yres + lower + vslen + upper;
|
||
|
if (ytotal > 1024) {
|
||
|
ytotal >>= 1;
|
||
|
yres >>= 1;
|
||
|
lower >>= 1;
|
||
|
vslen >>= 1;
|
||
|
upper >>= 1;
|
||
|
rMode = 0x04;
|
||
|
} else
|
||
|
rMode = 0x00;
|
||
|
if (ytotal > 1024)
|
||
|
return VERROR; //ytotal too big
|
||
|
if (vslen > 16)
|
||
|
return VERROR; //vslen too big
|
||
|
par->crtc[CRTC_V_TOTAL] = ytotal - 2;
|
||
|
r7 = 0x10; /* disable linecompare */
|
||
|
if (ytotal & 0x100) r7 |= 0x01;
|
||
|
if (ytotal & 0x200) r7 |= 0x20;
|
||
|
par->crtc[CRTC_PRESET_ROW] = 0;
|
||
|
|
||
|
|
||
|
// GMODE <--> ALPHA-MODE
|
||
|
// default using alpha mode so we need to set char rows= CHAR_HEIGHT-1
|
||
|
par->crtc[CRTC_MAX_SCAN] = 0x40 | (CHAR_HEIGHT-1); /* 16 scanlines, linecmp max*/
|
||
|
|
||
|
if (var->vmode & VMODE_DOUBLE)
|
||
|
par->crtc[CRTC_MAX_SCAN] |= 0x80;
|
||
|
par->crtc[CRTC_CURSOR_START] = 0x00; // curs enabled, start line = 0
|
||
|
par->crtc[CRTC_CURSOR_END] = CHAR_HEIGHT-1; // end line = 12
|
||
|
pos = yoffset * vxres + (xoffset >> 3);
|
||
|
par->crtc[CRTC_START_HI] = pos >> 8;
|
||
|
par->crtc[CRTC_START_LO] = pos & 0xFF;
|
||
|
par->crtc[CRTC_CURSOR_HI] = 0x00;
|
||
|
par->crtc[CRTC_CURSOR_LO] = 0x00;
|
||
|
pos = yres - 1;
|
||
|
par->crtc[CRTC_V_DISP_END] = pos & 0xFF;
|
||
|
par->crtc[CRTC_V_BLANK_START] = pos & 0xFF;
|
||
|
if (pos & 0x100)
|
||
|
r7 |= 0x0A; /* 0x02 -> DISP_END, 0x08 -> BLANK_START */
|
||
|
if (pos & 0x200) {
|
||
|
r7 |= 0x40; /* 0x40 -> DISP_END */
|
||
|
par->crtc[CRTC_MAX_SCAN] |= 0x20; /* BLANK_START */
|
||
|
}
|
||
|
pos += lower;
|
||
|
par->crtc[CRTC_V_SYNC_START] = pos & 0xFF;
|
||
|
if (pos & 0x100)
|
||
|
r7 |= 0x04;
|
||
|
if (pos & 0x200)
|
||
|
r7 |= 0x80;
|
||
|
pos += vslen;
|
||
|
par->crtc[CRTC_V_SYNC_END] = (pos & 0x0F) & ~0x10; /* disabled reg write prot, IRQ */
|
||
|
pos += upper - 1; /* blank_end + 1 <= ytotal + 2 */
|
||
|
par->crtc[CRTC_V_BLANK_END] = pos & 0xFF; /* 0x7F for original VGA,
|
||
|
but some SVGA chips requires all 8 bits to set */
|
||
|
if (vxres >= 512)
|
||
|
return VERROR; //vxres too long
|
||
|
par->crtc[CRTC_OFFSET] = vxres >> 1;
|
||
|
|
||
|
// put the underline off of the character, necessary in alpha color mode
|
||
|
par->crtc[CRTC_UNDERLINE] = 0x1f;
|
||
|
|
||
|
par->crtc[CRTC_MODE] = rMode | 0xA3; // word mode
|
||
|
par->crtc[CRTC_LINE_COMPARE] = 0xFF;
|
||
|
par->crtc[CRTC_OVERFLOW] = r7;
|
||
|
|
||
|
|
||
|
// not used ??
|
||
|
par->vss = 0x00; /* 3DA */
|
||
|
|
||
|
for (i = 0x00; i < 0x10; i++) {
|
||
|
par->atc[i] = VgaAttributeTable[i];
|
||
|
}
|
||
|
// GMODE <--> ALPHA-MODE
|
||
|
par->atc[ATC_MODE] = 0x0c; // text mode
|
||
|
|
||
|
par->atc[ATC_OVERSCAN] = 0x00; // no border
|
||
|
par->atc[ATC_PLANE_ENABLE] = 0x0F;
|
||
|
par->atc[ATC_PEL] = xoffset & 7;
|
||
|
par->atc[ATC_COLOR_PAGE] = 0x00;
|
||
|
|
||
|
par->misc = 0x67; /* enable CPU, ports 0x3Dx, positive sync*/
|
||
|
if (var->sync & SYNC_HOR_HIGH_ACT)
|
||
|
par->misc &= ~0x40;
|
||
|
if (var->sync & SYNC_VERT_HIGH_ACT)
|
||
|
par->misc &= ~0x80;
|
||
|
|
||
|
par->seq[SEQ_CLOCK_MODE] = 0x01; //8-bit char; 0x01=alpha mode
|
||
|
par->seq[SEQ_PLANE_WRITE] = 0x03; // just char/attr plane
|
||
|
par->seq[SEQ_CHARACTER_MAP] = 0x00;
|
||
|
par->seq[SEQ_MEMORY_MODE] = 0x02; // A/G bit not used in stpc; O/E on, C4 off
|
||
|
|
||
|
par->gdc[GDC_SR_VALUE] = 0x00;
|
||
|
// bits set in the SR_EN regs will enable set/reset action
|
||
|
// based on the bit settings in the SR_VAL register
|
||
|
par->gdc[GDC_SR_ENABLE] = 0x00;
|
||
|
par->gdc[GDC_COMPARE_VALUE] = 0x00;
|
||
|
par->gdc[GDC_DATA_ROTATE] = 0x00;
|
||
|
par->gdc[GDC_PLANE_READ] = 0;
|
||
|
par->gdc[GDC_MODE] = 0x10; //Okay
|
||
|
|
||
|
// GMODE <--> ALPHA-MMODE
|
||
|
par->gdc[GDC_MISC] = 0x0e; // b0=0 ->alpha mode; memory at 0xb8000
|
||
|
|
||
|
par->gdc[GDC_COMPARE_MASK] = 0x00;
|
||
|
par->gdc[GDC_BIT_MASK] = 0xFF;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// originally from the stpc web site
|
||
|
//
|
||
|
static const unsigned char VgaLookupTable[3 * 0x3f + 3] = {
|
||
|
// Red Green Blue
|
||
|
0x000, 0x000, 0x000, // 00h
|
||
|
0x000, 0x000, 0x02A, // 01h
|
||
|
0x000, 0x02A, 0x000, // 02h
|
||
|
0x000, 0x02A, 0x02A, // 03h
|
||
|
0x02A, 0x000, 0x000, // 04h
|
||
|
0x02A, 0x000, 0x02A, // 05h
|
||
|
0x02A, 0x02A, 0x000, // 06h
|
||
|
0x02A, 0x02A, 0x02A, // 07h
|
||
|
0x000, 0x000, 0x015, // 08h
|
||
|
0x000, 0x000, 0x03F, // 09h
|
||
|
0x000, 0x02A, 0x015, // 0Ah
|
||
|
0x000, 0x02A, 0x03F, // 0Bh
|
||
|
0x02A, 0x000, 0x015, // 0Ch
|
||
|
0x02A, 0x000, 0x03F, // 0Dh
|
||
|
0x02A, 0x02A, 0x015, // 0Eh
|
||
|
0x02A, 0x02A, 0x03F, // 0Fh
|
||
|
0x000, 0x015, 0x000, // 10h
|
||
|
0x000, 0x015, 0x02A, // 11h
|
||
|
0x000, 0x03F, 0x000, // 12h
|
||
|
0x000, 0x03F, 0x02A, // 13h
|
||
|
0x02A, 0x015, 0x000, // 14h
|
||
|
0x02A, 0x015, 0x02A, // 15h
|
||
|
0x02A, 0x03F, 0x000, // 16h
|
||
|
0x02A, 0x03F, 0x02A, // 17h
|
||
|
0x000, 0x015, 0x015, // 18h
|
||
|
0x000, 0x015, 0x03F, // 19h
|
||
|
0x000, 0x03F, 0x015, // 1Ah
|
||
|
0x000, 0x03F, 0x03F, // 1Bh
|
||
|
0x02A, 0x015, 0x015, // 1Ch
|
||
|
0x02A, 0x015, 0x03F, // 1Dh
|
||
|
0x02A, 0x03F, 0x015, // 1Eh
|
||
|
0x02A, 0x03F, 0x03F, // 1Fh
|
||
|
0x015, 0x000, 0x000, // 20h
|
||
|
0x015, 0x000, 0x02A, // 21h
|
||
|
0x015, 0x02A, 0x000, // 22h
|
||
|
0x015, 0x02A, 0x02A, // 23h
|
||
|
0x03F, 0x000, 0x000, // 24h
|
||
|
0x03F, 0x000, 0x02A, // 25h
|
||
|
0x03F, 0x02A, 0x000, // 26h
|
||
|
0x03F, 0x02A, 0x02A, // 27h
|
||
|
0x015, 0x000, 0x015, // 28h
|
||
|
0x015, 0x000, 0x03F, // 29h
|
||
|
0x015, 0x02A, 0x015, // 2Ah
|
||
|
0x015, 0x02A, 0x03F, // 2Bh
|
||
|
0x03F, 0x000, 0x015, // 2Ch
|
||
|
0x03F, 0x000, 0x03F, // 2Dh
|
||
|
0x03F, 0x02A, 0x015, // 2Eh
|
||
|
0x03F, 0x02A, 0x03F, // 2Fh
|
||
|
0x015, 0x015, 0x000, // 30h
|
||
|
0x015, 0x015, 0x02A, // 31h
|
||
|
0x015, 0x03F, 0x000, // 32h
|
||
|
0x015, 0x03F, 0x02A, // 33h
|
||
|
0x03F, 0x015, 0x000, // 34h
|
||
|
0x03F, 0x015, 0x02A, // 35h
|
||
|
0x03F, 0x03F, 0x000, // 36h
|
||
|
0x03F, 0x03F, 0x02A, // 37h
|
||
|
0x015, 0x015, 0x015, // 38h
|
||
|
0x015, 0x015, 0x03F, // 39h
|
||
|
0x015, 0x03F, 0x015, // 3Ah
|
||
|
0x015, 0x03F, 0x03F, // 3Bh
|
||
|
0x03F, 0x015, 0x015, // 3Ch
|
||
|
0x03F, 0x015, 0x03F, // 3Dh
|
||
|
0x03F, 0x03F, 0x015, // 3Eh
|
||
|
0x03F, 0x03F, 0x03F, // 3Fh
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
* From the Linux kernel.
|
||
|
* orig by Ben Pfaff and Petr Vandrovec.
|
||
|
* see the note in the vga.h for attribution.
|
||
|
*
|
||
|
* modified by
|
||
|
* Steve M. Gehlbach <steve@kesa.com>
|
||
|
* for the linuxbios project
|
||
|
*
|
||
|
* Write the data in the vga parameter structure
|
||
|
* to the vga registers, along with other default
|
||
|
* settings.
|
||
|
*
|
||
|
*/
|
||
|
static int vga_set_regs(const struct vga_par *par)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
/* update misc output register */
|
||
|
outb(par->misc, MIS_W);
|
||
|
|
||
|
/* synchronous reset on */
|
||
|
outb(0x00, SEQ_I);
|
||
|
outb(0x00, SEQ_D);
|
||
|
|
||
|
/* write sequencer registers */
|
||
|
outb(1, SEQ_I);
|
||
|
outb(par->seq[1] | 0x20, SEQ_D); // blank display
|
||
|
for (i = 2; i < SEQ_C; i++) {
|
||
|
outb(i, SEQ_I);
|
||
|
outb(par->seq[i], SEQ_D);
|
||
|
}
|
||
|
|
||
|
/* synchronous reset off */
|
||
|
outb(0x00, SEQ_I);
|
||
|
outb(0x03, SEQ_D);
|
||
|
|
||
|
/* deprotect CRT registers 0-7 */
|
||
|
outb(0x11, CRT_IC);
|
||
|
outb(par->crtc[0x11], CRT_DC);
|
||
|
|
||
|
/* write CRT registers */
|
||
|
for (i = 0; i < CRTC_C; i++) {
|
||
|
outb(i, CRT_IC);
|
||
|
outb(par->crtc[i], CRT_DC);
|
||
|
}
|
||
|
/* write graphics controller registers */
|
||
|
for (i = 0; i < GRA_C; i++) {
|
||
|
outb(i, GRA_I);
|
||
|
outb(par->gdc[i], GRA_D);
|
||
|
}
|
||
|
|
||
|
/* write attribute controller registers */
|
||
|
for (i = 0; i < ATT_C; i++) {
|
||
|
inb(IS1_RC); /* reset flip-flop */
|
||
|
inb(0x80); //delay
|
||
|
outb(i, ATT_IW);
|
||
|
inb(0x80); //delay
|
||
|
|
||
|
outb(par->atc[i], ATT_IW);
|
||
|
inb(0x80); //delay
|
||
|
}
|
||
|
|
||
|
// initialize the color table
|
||
|
outb(0, PEL_IW);
|
||
|
i = 0;
|
||
|
// length is a magic number right now
|
||
|
while ( i < (0x3f*3 + 3) ) {
|
||
|
outb(VgaLookupTable[i++], PEL_D);
|
||
|
outb(VgaLookupTable[i++], PEL_D);
|
||
|
outb(VgaLookupTable[i++], PEL_D);
|
||
|
}
|
||
|
|
||
|
outb(0x0ff, PEL_MSK); // palette mask
|
||
|
|
||
|
// very important
|
||
|
// turn on video, disable palette access
|
||
|
inb(IS1_RC); /* reset flip-flop */
|
||
|
inb(0x80); //delay
|
||
|
outb(0x20, ATT_IW);
|
||
|
|
||
|
/* Wait for screen to stabilize. */
|
||
|
//for(i=0;i<1000;i++) { inb(0x80); }
|
||
|
|
||
|
outb(0x01, SEQ_I); // unblank display
|
||
|
outb(par->seq[1], SEQ_D);
|
||
|
|
||
|
// turn on display, disable access to attr palette
|
||
|
inb(IS1_RC);
|
||
|
outb(0x20, ATT_IW);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
vga_load_regs(void)
|
||
|
{
|
||
|
struct vga_par par;
|
||
|
|
||
|
if (vga_decode_var(&vga_settings, &par) == 0) {
|
||
|
vga_set_regs(&par);
|
||
|
}
|
||
|
}
|