#include <u.h>
#include <libc.h>
#include <bio.h>
#include "pci.h"
#include "vga.h"
/*
* S3 Trio64.
*/
static void
snarf(Vga* vga, Ctlr* ctlr)
{
int i;
/*
* The Trio has some extra sequencer registers which
* need to be unlocked for access.
*/
vgaxo(Seqx, 0x08, 0x06);
for(i = 0x08; i < 0x19; i++)
vga->sequencer[i] = vgaxi(Seqx, i);
vga->crt[0x2D] = vgaxi(Crtx, 0x2D);
vga->crt[0x2E] = vgaxi(Crtx, 0x2E);
vga->crt[0x2F] = vgaxi(Crtx, 0x2F);
s3generic.snarf(vga, ctlr);
ctlr->flag |= Fsnarf;
}
static void
options(Vga*, Ctlr* ctlr)
{
ctlr->flag |= Hlinear|Hpclk2x8|Henhanced|Foptions;
}
void
trio64clock(Vga* vga, Ctlr* ctlr)
{
int d;
ulong f, fmax, fmin, n, m, r;
double trouble;
/*
* The max value of R, M, N, and the speed rating of the part vary
* between parts and are passed to this routine in r[1] and f[1].
* R F
* Trio64 3 135000000
* ViRGE 3 135000000
* ViRGE/[DG]X 4 170000000
* ViRGE/GX2 4 170000000
* ViRGE/VX 4 220000000
*/
/*
* The PLL frequency is defined by the following equation:
* (M+2)
* Fout = ------------- x Fref
* (N+2) x 2**R
* where M, N and R have the following contraints:
* 1) (M+2) x Fref
* vga->f[1] <= ------------ <= vga->f[1]*2
* (N+2)
* 2) 1 <= M <= vga->m[1] (usually 127)
* 3) 1 <= N <= vga->n[1] (usually 31)
* 4) 0 <= R <= vga->r[1]
*
* First determine R:
* vga->f[1] < 2**R x Fout <= vga->f[1]*2
*/
for(r = 0; r <= vga->r[1]; r++){
f = vga->f[0]*(1<<r);
if(vga->f[1] < f && f <= vga->f[1]*2)
vga->r[0] = r;
}
if(vga->r[0] > vga->r[1])
error("%s: pclk %lud out of range\n", ctlr->name, vga->f[0]);
/*
* Now find the closest match for M and N.
*/
vga->d[0] = vga->f[0]+1;
for(n = 1; n <= vga->n[1]; n++){
trouble = vga->f[0]*(n+2)*(1<<vga->r[0]);
trouble /= RefFreq;
m = (trouble+0.5) - 2;
if(m > vga->m[1])
continue;
trouble = (m+2)*RefFreq;
trouble /= (n+2)*(1<<vga->r[0]);
f = trouble+0.5;
d = vga->f[0] - f;
if(d < 0)
d = -d;
if(d <= vga->d[0]){
vga->m[0] = m;
vga->n[0] = n;
vga->d[0] = d;
}
}
trouble = vga->f[0]*1.005;
fmax = trouble;
trouble = vga->f[0]*0.995;
fmin = trouble;
trouble = (vga->m[0]+2)*RefFreq;
trouble /= (vga->n[0]+2)*(1<<vga->r[0]);
f = trouble+0.5;
if(fmin >= f || f >= fmax)
error("%s: pclk %lud out of range\n", ctlr->name, vga->f[0]);
}
static void
init(Vga* vga, Ctlr* ctlr)
{
ulong pclk, x;
s3generic.init(vga, ctlr);
/*
* Clock bits. If the desired video clock is
* one of the two standard VGA clocks it can just be
* set using bits <3:2> of vga->misc, otherwise we
* need to programme the DCLK PLL.
*/
if(vga->mode->z > 8)
error("depth %d not supported\n", vga->mode->z);
if(vga->f[0] == 0)
vga->f[0] = vga->mode->frequency;
vga->misc &= ~0x0C;
if(vga->f[0] == VgaFreq0){
/* nothing to do */;
}
else if(vga->f[0] == VgaFreq1)
vga->misc |= 0x04;
else{
/*
* Part comes in -135MHz speed grade. In 8-bit mode
* the maximum DCLK is 80MHz. In 2x8-bit mode the maximum
* DCLK is 135MHz using the internal clock doubler.
*/
if((ctlr->flag & Hpclk2x8) && vga->mode->z == 8){
pclk = 135000000;
if(vga->f[0] > 80000000)
ctlr->flag |= Upclk2x8;
}
else
pclk = 80000000;
if(vga->f[0] > pclk)
error("%s: invalid pclk - %lud\n",
ctlr->name, vga->f[0]);
vga->f[1] = 135000000;
vga->r[1] = 3;
vga->n[1] = 31;
vga->m[1] = 127;
trio64clock(vga, ctlr);
vga->sequencer[0x12] = (vga->r[0]<<5)|vga->n[0];
vga->sequencer[0x13] = vga->m[0];
vga->misc |= 0x0C;
}
/*
* Internal clock generator.
*/
vga->sequencer[0x15] &= ~0x31;
vga->sequencer[0x15] |= 0x02;
vga->sequencer[0x18] &= ~0x80;
vga->crt[0x67] &= ~0xF2;
if(ctlr->flag & Upclk2x8){
vga->sequencer[0x15] |= 0x10;
vga->sequencer[0x18] |= 0x80;
/*
* There's a little strip of the border
* appears on the left in resolutions
* 1280 and above if the 0x02 bit isn't
* set (when it appears on the right...).
*/
vga->crt[0x67] |= 0x10;
}
/*
* VLB address latch delay.
*/
if((vga->crt[0x36] & 0x03) == 0x01)
vga->crt[0x58] &= ~0x08;
/*
* Start display FIFO fetch.
*/
x = vga->crt[0]-5;
vga->crt[0x3B] = x;
if(x & 0x100)
vga->crt[0x5D] |= 0x40;
/*
* Display memory access control.
* Calculation of the M-parameter (Crt54) is
* memory-system and dot-clock dependent, the
* values below are guesses from dumping
* registers.
*/
vga->crt[0x60] = 0xFF;
if(vga->mode->x <= 800)
vga->crt[0x54] = 0xE8;
else if(vga->mode->x <= 1024)
vga->crt[0x54] = 0xA8;
else
vga->crt[0x54] = 0x00/*0x48*/;
ctlr->flag |= Finit;
}
static void
load(Vga* vga, Ctlr* ctlr)
{
ushort advfunc;
s3generic.load(vga, ctlr);
vgaxo(Crtx, 0x60, vga->crt[0x60]);
vgaxo(Crtx, 0x67, vga->crt[0x67]);
/*
* Load the PLL registers if necessary.
* Not sure if the variable-delay method of setting the
* PLL will work without a write here to vga->misc,
* so use the immediate-load method by toggling bit 5
* of Seq15 if necessary.
*/
vgaxo(Seqx, 0x12, vga->sequencer[0x12]);
vgaxo(Seqx, 0x13, vga->sequencer[0x13]);
if((vga->misc & 0x0C) == 0x0C)
vgaxo(Seqx, 0x15, vga->sequencer[0x15]|0x20);
vgaxo(Seqx, 0x15, vga->sequencer[0x15]);
vgaxo(Seqx, 0x18, vga->sequencer[0x18]);
advfunc = 0x0000;
if(ctlr->flag & Uenhanced)
advfunc = 0x0001;
outportw(0x4AE8, advfunc);
}
static void
dump(Vga* vga, Ctlr* ctlr)
{
int i;
ulong dclk, m, n, r;
s3generic.dump(vga, ctlr);
printitem(ctlr->name, "Seq08");
for(i = 0x08; i < 0x19; i++)
printreg(vga->sequencer[i]);
printitem(ctlr->name, "Crt2D");
printreg(vga->crt[0x2D]);
printreg(vga->crt[0x2E]);
printreg(vga->crt[0x2F]);
n = vga->sequencer[0x12] & 0x1F;
r = (vga->sequencer[0x12]>>5) & 0x03;
m = vga->sequencer[0x13] & 0x7F;
dclk = (m+2)*RefFreq;
dclk /= (n+2)*(1<<r);
printitem(ctlr->name, "dclk m n r");
Bprint(&stdout, "%9ld %8ld - %8ld %8ld\n", dclk, m, n, r);
}
Ctlr trio64 = {
"trio64", /* name */
snarf, /* snarf */
options, /* options */
init, /* init */
load, /* load */
dump, /* dump */
};
|