271 lines
5.6 KiB
C
271 lines
5.6 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"
|
|
|
|
/*
|
|
* ICS534x GENDAC.
|
|
* For now assumed to be hooked to either a Tseng Labs ET4000-W32p
|
|
* (Hercules Dynamite Power, the Hercules generates RS2 using CLK3)
|
|
* or an ARK2000pv (Diamond Stealth64 Graphics 2001).
|
|
*/
|
|
static uint8_t
|
|
setrs2(Vga* vga, Ctlr* ctlr)
|
|
{
|
|
uint8_t rs2;
|
|
|
|
rs2 = 0;
|
|
if(strncmp(vga->ctlr->name, "et4000-w32", 10) == 0){
|
|
rs2 = vgaxi(Crtx, 0x31);
|
|
vgaxo(Crtx, 0x31, 0x40|rs2);
|
|
}
|
|
else if(strncmp(vga->ctlr->name, "ark2000pv", 9) == 0){
|
|
rs2 = vgaxi(Seqx, 0x1C);
|
|
vgaxo(Seqx, 0x1C, 0x80|rs2);
|
|
}
|
|
else
|
|
error("%s: not configured for %s\n", vga->ctlr->name, ctlr->name);
|
|
|
|
return rs2;
|
|
}
|
|
|
|
static void
|
|
restorers2(Vga* vga, uint8_t rs2)
|
|
{
|
|
if(strncmp(vga->ctlr->name, "et4000-w32", 10) == 0)
|
|
vgaxo(Crtx, 0x31, rs2);
|
|
else if(strncmp(vga->ctlr->name, "ark2000pv", 9) == 0)
|
|
vgaxo(Seqx, 0x1C, rs2);
|
|
}
|
|
|
|
static void
|
|
options(Vga* vga, Ctlr* ctlr)
|
|
{
|
|
ctlr->flag |= Hpclk2x8|Foptions;
|
|
}
|
|
|
|
static void
|
|
clock(Vga* vga, Ctlr* ctlr)
|
|
{
|
|
uint32_t f, m, n, r;
|
|
double fmin, fmax, t, tok;
|
|
|
|
/*
|
|
* The PLL frequency is defined by:
|
|
* (M+2)
|
|
* Fout = ------------ x Fref
|
|
* (N+2) x 2**R
|
|
* where M, N and R have the following contraints:
|
|
* 1) 2MHz < Fref < 32MHz
|
|
* 2) Fref
|
|
* 600KHz < ----- <= 8Mhz
|
|
* (N+2)
|
|
* 3) (M+2) x Fref
|
|
* 60MHz <= ------------ <= 270MHz
|
|
* (N+2)
|
|
* 4) Fout < 135MHz
|
|
* 5) 1 <= M <= 127, 1 <= N <= 31, 0 <= R <= 3
|
|
*
|
|
* First determine R by finding the highest value
|
|
* for which
|
|
* 2**R x Fout <= 270Mhz
|
|
* The datasheet says that if the multiplexed 16-bit
|
|
* pseudo-colour mode is used then N2 (vga->r) must
|
|
* be 2.
|
|
*/
|
|
if(ctlr->flag & Upclk2x8)
|
|
vga->r[0] = 2;
|
|
else{
|
|
vga->r[0] = 4;
|
|
for(r = 0; r <= 3; r++){
|
|
f = vga->f[0]*(1<<r);
|
|
if(60000000 < f && f <= 270000000)
|
|
vga->r[0] = r;
|
|
}
|
|
if(vga->r[0] > 3)
|
|
error("%s: pclk %lud out of range\n",
|
|
ctlr->name, vga->f[0]);
|
|
}
|
|
|
|
/*
|
|
* Now find the closest match for M and N.
|
|
* Lower values of M and N give better noise rejection.
|
|
*/
|
|
fmin = vga->f[0]*0.995;
|
|
fmax = vga->f[0]*1.005;
|
|
tok = 0.0;
|
|
for(n = 31; n >= 1; n--){
|
|
t = RefFreq/(n+2);
|
|
if(600000 >= t || t > 8000000)
|
|
continue;
|
|
|
|
t = vga->f[0]*(n+2)*(1<<vga->r[0]);
|
|
t /= RefFreq;
|
|
m = (t+0.5) - 2;
|
|
if(m > 127)
|
|
continue;
|
|
|
|
t = (m+2)*RefFreq;
|
|
t /= (n+2)*(1<<vga->r[0]);
|
|
|
|
if(fmin <= t && t < fmax){
|
|
vga->m[0] = m;
|
|
vga->n[0] = n;
|
|
tok = t;
|
|
}
|
|
}
|
|
|
|
if(tok == 0.0)
|
|
error("%s: pclk %lud out of range\n", ctlr->name, vga->f[0]);
|
|
}
|
|
|
|
static void
|
|
init(Vga* vga, Ctlr* ctlr)
|
|
{
|
|
uint32_t pclk;
|
|
char *p;
|
|
|
|
/*
|
|
* Part comes in -135, -110 and -80MHz speed-grades.
|
|
*/
|
|
pclk = 80000000;
|
|
if(p = strrchr(ctlr->name, '-'))
|
|
pclk = strtoul(p+1, 0, 0) * 1000000;
|
|
|
|
/*
|
|
* If we don't already have a desired pclk,
|
|
* take it from the mode.
|
|
* Check it's within range.
|
|
*/
|
|
if(vga->f[0] == 0)
|
|
vga->f[0] = vga->mode->frequency;
|
|
if(vga->f[0] > pclk)
|
|
error("%s: invalid pclk - %ld\n", ctlr->name, vga->f[0]);
|
|
|
|
/*
|
|
* Determine whether to use 2x8-bit mode or not.
|
|
* If yes and the clock has already been initialised,
|
|
* initialise it again.
|
|
*/
|
|
if(vga->ctlr && (vga->ctlr->flag & Hpclk2x8) && vga->mode->z == 8 && vga->f[0] >= pclk/2){
|
|
vga->f[0] /= 2;
|
|
resyncinit(vga, ctlr, Upclk2x8, 0);
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
vga->misc &= ~0x0C;
|
|
if(vga->f[0] == VgaFreq0)
|
|
vga->i[0] = 0;
|
|
else if(vga->f[0] == VgaFreq1){
|
|
vga->misc |= 0x04;
|
|
vga->i[0] = 1;
|
|
}
|
|
else{
|
|
/*
|
|
* Initialise the PLL parameters.
|
|
* Use CLK0 f7 internal clock (there are only 3
|
|
* clock-select bits).
|
|
*/
|
|
clock(vga, ctlr);
|
|
vga->i[0] = 0x07;
|
|
}
|
|
|
|
ctlr->flag |= Finit;
|
|
}
|
|
|
|
static void
|
|
load(Vga* vga, Ctlr* ctlr)
|
|
{
|
|
uint8_t rs2, mode, pll;
|
|
|
|
rs2 = setrs2(vga, ctlr);
|
|
|
|
/*
|
|
* Put the chip into snooze mode,
|
|
* colour mode 0.
|
|
*/
|
|
mode = 0x00;
|
|
outportb(Pixmask, 0x01);
|
|
|
|
if(ctlr->flag & Upclk2x8)
|
|
mode = 0x10;
|
|
|
|
/*
|
|
* If necessary, set the PLL parameters for CLK0 f7
|
|
* and make sure the PLL control register selects the
|
|
* correct clock. Preserve the memory clock setting.
|
|
*/
|
|
outportb(PaddrR, 0x0E);
|
|
pll = inportb(Pdata) & 0x10;
|
|
if(vga->i[0] == 0x07){
|
|
outportb(PaddrW, vga->i[0]);
|
|
outportb(Pdata, vga->m[0]);
|
|
outportb(Pdata, (vga->r[0]<<5)|vga->n[0]);
|
|
pll |= 0x27;
|
|
}
|
|
outportb(PaddrW, 0x0E);
|
|
outportb(Pdata, pll);
|
|
outportb(Pixmask, mode);
|
|
|
|
restorers2(vga, rs2);
|
|
ctlr->flag |= Fload;
|
|
}
|
|
|
|
static void
|
|
dump(Vga* vga, Ctlr* ctlr)
|
|
{
|
|
int i;
|
|
uint8_t rs2, m, n;
|
|
char buf[32];
|
|
uint32_t f;
|
|
|
|
rs2 = setrs2(vga, ctlr);
|
|
|
|
printitem(ctlr->name, "command");
|
|
printreg(inportb(Pixmask));
|
|
|
|
outportb(PaddrR, 0x00);
|
|
for(i = 0; i < 0x0E; i++){
|
|
sprint(buf, "f%X m n", i);
|
|
printitem(ctlr->name, buf);
|
|
m = inportb(Pdata);
|
|
printreg(m);
|
|
n = inportb(Pdata);
|
|
printreg(n);
|
|
|
|
f = 14318180*(m+2);
|
|
f /= (n & 0x1F)+2;
|
|
f /= 1<<((n>>5) & 0x03);
|
|
Bprint(&stdout, "%12lud", f);
|
|
}
|
|
printitem(ctlr->name, "control");
|
|
printreg(inportb(Pdata));
|
|
|
|
restorers2(vga, rs2);
|
|
}
|
|
|
|
Ctlr ics534x = {
|
|
"ics534x", /* name */
|
|
0, /* snarf */
|
|
options, /* options */
|
|
init, /* init */
|
|
load, /* load */
|
|
dump, /* dump */
|
|
};
|