jehanne/sys/src/cmd/hmi/vga/vga.c

496 lines
9.5 KiB
C

/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <lib9.h>
#include <bio.h>
#include "pci.h"
#include "vga.h"
enum {
NSeqx = 0x05,
NCrtx = 0x19,
NGrx = 0x09,
NAttrx = 0x15,
};
uint8_t
vgai(int32_t port)
{
uint8_t data;
switch(port){
case MiscR:
case Status0:
case Status1:
case FeatureR:
case PaddrW:
case Pdata:
case Pixmask:
case Pstatus:
data = inportb(port);
break;
default:
error("vgai(0x%4.4lX): unknown port\n", port);
/*NOTREACHED*/
data = 0xFF;
break;
}
return data;
}
uint8_t
vgaxi(int32_t port, uint8_t index)
{
uint8_t data;
switch(port){
case Seqx:
case Crtx:
case Grx:
outportb(port, index);
data = inportb(port+1);
break;
case Attrx:
/*
* Allow processor access to the colour
* palette registers. Writes to Attrx must
* be preceded by a read from Status1 to
* initialise the register to point to the
* index register and not the data register.
* Processor access is allowed by turning
* off bit 0x20.
*/
inportb(Status1);
if(index < 0x10){
outportb(Attrx, index);
data = inportb(Attrx+1);
inportb(Status1);
outportb(Attrx, 0x20|index);
}
else{
outportb(Attrx, 0x20|index);
data = inportb(Attrx+1);
}
break;
default:
error("vgaxi(0x%4.4lx, 0x%2.2uX): unknown port\n", port, index);
/*NOTREACHED*/
data = 0xFF;
break;
}
return data;
}
void
vgao(int32_t port, uint8_t data)
{
switch(port){
case MiscW:
case FeatureW:
case PaddrW:
case Pdata:
case Pixmask:
case PaddrR:
outportb(port, data);
break;
default:
error("vgao(0x%4.4lX, 0x%2.2uX): unknown port\n", port, data);
/*NOTREACHED*/
break;
}
}
void
vgaxo(int32_t port, uint8_t index, uint8_t data)
{
switch(port){
case Seqx:
case Crtx:
case Grx:
/*
* We could use an outport here, but some chips
* (e.g. 86C928) have trouble with that for some
* registers.
*/
outportb(port, index);
outportb(port+1, data);
break;
case Attrx:
inportb(Status1);
if(index < 0x10){
outportb(Attrx, index);
outportb(Attrx, data);
inportb(Status1);
outportb(Attrx, 0x20|index);
}
else{
outportb(Attrx, 0x20|index);
outportb(Attrx, data);
}
break;
default:
error("vgaxo(0x%4.4lX, 0x%2.2uX, 0x%2.2uX): unknown port\n",
port, index, data);
break;
}
}
static void
snarf(Vga* vga, Ctlr* ctlr)
{
int i;
/*
* Generic VGA registers:
* misc, feature;
* sequencer;
* crt;
* graphics;
* attribute;
* palette.
*/
vga->misc = vgai(MiscR);
vga->feature = vgai(FeatureR);
for(i = 0; i < NSeqx; i++)
vga->sequencer[i] = vgaxi(Seqx, i);
for(i = 0; i < NCrtx; i++)
vga->crt[i] = vgaxi(Crtx, i);
for(i = 0; i < NGrx; i++)
vga->graphics[i] = vgaxi(Grx, i);
for(i = 0; i < NAttrx; i++)
vga->attribute[i] = vgaxi(Attrx, i);
if(dflag)
palette.snarf(vga, ctlr);
ctlr->flag |= Fsnarf;
}
static void
init(Vga* vga, Ctlr* ctlr)
{
Mode *mode;
int vt, vde, vrs, vre;
uint32_t tmp;
mode = vga->mode;
memset(vga->sequencer, 0, NSeqx*sizeof(vga->sequencer[0]));
memset(vga->crt, 0, NCrtx*sizeof(vga->crt[0]));
memset(vga->graphics, 0, NGrx*sizeof(vga->graphics[0]));
memset(vga->attribute, 0, NAttrx*sizeof(vga->attribute[0]));
if(dflag)
memset(vga->palette, 0, sizeof(vga->palette));
/*
* Misc. If both the horizontal and vertical sync polarity
* options are set, use them. Otherwise use the defaults for
* the given vertical size.
*/
vga->misc = 0x23;
if(mode->frequency == VgaFreq1)
vga->misc |= 0x04;
if(mode->hsync && mode->vsync){
if(mode->hsync == '-')
vga->misc |= 0x40;
if(mode->vsync == '-')
vga->misc |= 0x80;
}
else{
if(mode->y < 480)
vga->misc |= 0x40;
else if(mode->y < 400)
vga->misc |= 0x80;
else if(mode->y < 768)
vga->misc |= 0xC0;
}
/*
* Sequencer
*/
vga->sequencer[0x00] = 0x03;
vga->sequencer[0x01] = 0x01;
vga->sequencer[0x02] = 0x0F;
vga->sequencer[0x03] = 0x00;
if(mode->z >= 8)
vga->sequencer[0x04] = 0x0A;
else
vga->sequencer[0x04] = 0x06;
/*
* Crt. Most of the work here is in dealing
* with field overflow.
*/
memset(vga->crt, 0, NCrtx);
vga->crt[0x00] = (mode->ht>>3)-5;
vga->crt[0x01] = (mode->x>>3)-1;
vga->crt[0x02] = (mode->shb>>3)-1;
/*
* End Horizontal Blank is a 6-bit field, 5-bits
* in Crt3, high bit in Crt5.
*/
tmp = mode->ehb>>3;
vga->crt[0x03] = 0x80|(tmp & 0x1F);
if(tmp & 0x20)
vga->crt[0x05] |= 0x80;
if(mode->shs == 0)
mode->shs = mode->shb;
vga->crt[0x04] = mode->shs>>3;
if(mode->ehs == 0)
mode->ehs = mode->ehb;
vga->crt[0x05] |= ((mode->ehs>>3) & 0x1F);
/*
* Vertical Total is 10-bits, 8 in Crt6, the high
* two bits in Crt7. What if vt is >= 1024? We hope
* the specific controller has some more overflow
* bits.
*
* Interlace: if 'v', divide the vertical timing
* values by 2.
*/
vt = mode->vt;
vde = mode->y;
vrs = mode->vrs;
vre = mode->vre;
if(mode->interlace == 'v'){
vt /= 2;
vde /= 2;
vrs /= 2;
vre /= 2;
}
tmp = vt-2;
vga->crt[0x06] = tmp;
if(tmp & 0x100)
vga->crt[0x07] |= 0x01;
if(tmp & 0x200)
vga->crt[0x07] |= 0x20;
tmp = vrs;
vga->crt[0x10] = tmp;
if(tmp & 0x100)
vga->crt[0x07] |= 0x04;
if(tmp & 0x200)
vga->crt[0x07] |= 0x80;
vga->crt[0x11] = 0x20|(vre & 0x0F);
tmp = vde-1;
vga->crt[0x12] = tmp;
if(tmp & 0x100)
vga->crt[0x07] |= 0x02;
if(tmp & 0x200)
vga->crt[0x07] |= 0x40;
vga->crt[0x15] = vrs;
if(vrs & 0x100)
vga->crt[0x07] |= 0x08;
if(vrs & 0x200)
vga->crt[0x09] |= 0x20;
vga->crt[0x16] = (vrs+1);
vga->crt[0x17] = 0x83;
tmp = ((vga->virtx*mode->z)/8);
if(tmp >= 512){
vga->crt[0x14] |= 0x60;
tmp /= 8;
}
else if(tmp >= 256){
vga->crt[0x17] |= 0x08;
tmp /= 4;
}
else{
vga->crt[0x17] |= 0x40;
tmp /= 2;
}
vga->crt[0x13] = tmp;
if(mode->x*mode->y*mode->z/8 > 64*1024)
vga->crt[0x17] |= 0x20;
vga->crt[0x18] = 0x7FF;
if(vga->crt[0x18] & 0x100)
vga->crt[0x07] |= 0x10;
if(vga->crt[0x18] & 0x200)
vga->crt[0x09] |= 0x40;
/*
* Graphics
*/
memset(vga->graphics, 0, NGrx);
if((vga->sequencer[0x04] & 0x04) == 0)
vga->graphics[0x05] |= 0x10;
if(mode->z >= 8)
vga->graphics[0x05] |= 0x40;
vga->graphics[0x06] = 0x05;
vga->graphics[0x07] = 0x0F;
vga->graphics[0x08] = 0xFF;
/*
* Attribute
*/
memset(vga->attribute, 0, NAttrx);
for(tmp = 0; tmp < 0x10; tmp++)
vga->attribute[tmp] = tmp;
vga->attribute[0x10] = 0x01;
if(mode->z >= 8)
vga->attribute[0x10] |= 0x40;
vga->attribute[0x11] = 0xFF;
vga->attribute[0x12] = 0x0F;
/*
* Palette
*/
if(dflag)
palette.init(vga, ctlr);
ctlr->flag |= Finit;
}
static void
load(Vga* vga, Ctlr* ctlr)
{
int i;
/*
* Reset the sequencer and leave it off.
* Load the generic VGA registers:
* misc;
* sequencer (but not seq01, display enable);
* take the sequencer out of reset;
* take off write-protect on crt[0x00-0x07];
* crt;
* graphics;
* attribute;
* palette.
vgaxo(Seqx, 0x00, 0x00);
*/
vgao(MiscW, vga->misc);
for(i = 2; i < NSeqx; i++)
vgaxo(Seqx, i, vga->sequencer[i]);
/*vgaxo(Seqx, 0x00, 0x03);*/
vgaxo(Crtx, 0x11, vga->crt[0x11] & ~0x80);
for(i = 0; i < NCrtx; i++)
vgaxo(Crtx, i, vga->crt[i]);
for(i = 0; i < NGrx; i++)
vgaxo(Grx, i, vga->graphics[i]);
for(i = 0; i < NAttrx; i++)
vgaxo(Attrx, i, vga->attribute[i]);
if(dflag)
palette.load(vga, ctlr);
ctlr->flag |= Fload;
}
static void
dump(Vga* vga, Ctlr* ctlr)
{
int i;
printitem(ctlr->name, "misc");
printreg(vga->misc);
printitem(ctlr->name, "feature");
printreg(vga->feature);
printitem(ctlr->name, "sequencer");
for(i = 0; i < NSeqx; i++)
printreg(vga->sequencer[i]);
printitem(ctlr->name, "crt");
for(i = 0; i < NCrtx; i++)
printreg(vga->crt[i]);
printitem(ctlr->name, "graphics");
for(i = 0; i < NGrx; i++)
printreg(vga->graphics[i]);
printitem(ctlr->name, "attribute");
for(i = 0; i < NAttrx; i++)
printreg(vga->attribute[i]);
if(dflag)
palette.dump(vga, ctlr);
printitem(ctlr->name, "virtual");
Bprint(&stdout, "%ld %ld\n", vga->virtx, vga->virty);
printitem(ctlr->name, "panning");
Bprint(&stdout, "%s\n", vga->panning ? "on" : "off");
if(vga->f[0]){
printitem(ctlr->name, "clock[0] f");
Bprint(&stdout, "%9ld\n", vga->f[0]);
printitem(ctlr->name, "clock[0] d i m");
Bprint(&stdout, "%9ld %8ld - %8ld\n",
vga->d[0], vga->i[0], vga->m[0]);
printitem(ctlr->name, "clock[0] n p q r");
Bprint(&stdout, "%9ld %8ld - %8ld %8ld\n",
vga->n[0], vga->p[0], vga->q[0], vga->r[0]);
}
if(vga->f[1]){
printitem(ctlr->name, "clock[1] f");
Bprint(&stdout, "%9ld\n", vga->f[1]);
printitem(ctlr->name, "clock[1] d i m");
Bprint(&stdout, "%9ld %8ld - %8ld\n",
vga->d[1], vga->i[1], vga->m[1]);
printitem(ctlr->name, "clock[1] n p q r");
Bprint(&stdout, "%9ld %8ld - %8ld %8ld\n",
vga->n[1], vga->p[1], vga->q[1], vga->r[1]);
}
if(vga->vma || vga->vmb){
printitem(ctlr->name, "vm a b");
Bprint(&stdout, "%9lud %8lud\n", vga->vma, vga->vmb);
}
if(vga->vmz){
printitem(ctlr->name, "vmz");
Bprint(&stdout, "%9lud\n", vga->vmz);
}
printitem(ctlr->name, "apz");
Bprint(&stdout, "%9lud\n", vga->apz);
printitem(ctlr->name, "linear");
Bprint(&stdout, "%9d\n", vga->linear);
}
Ctlr generic = {
"vga", /* name */
snarf, /* snarf */
0, /* options */
init, /* init */
load, /* load */
dump, /* dump */
};