318 lines
6.3 KiB
C
318 lines
6.3 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"
|
|
|
|
/*
|
|
* IBM RGB524.
|
|
* 170/220MHz High Performance Palette DAC.
|
|
*
|
|
* Assumes hooked up to an S3 Vision96[48].
|
|
*/
|
|
enum {
|
|
IndexLo = 0x00,
|
|
IndexHi = 0x01,
|
|
Data = 0x02,
|
|
IndexCtl = 0x03,
|
|
};
|
|
|
|
enum { /* index registers */
|
|
MiscClock = 0x02,
|
|
PixelFormat = 0x0A,
|
|
PLLControl1 = 0x10,
|
|
PLLControl2 = 0x11,
|
|
PLLReference = 0x14,
|
|
Frequency0 = 0x20,
|
|
MiscControl1 = 0x70,
|
|
MiscControl2 = 0x71,
|
|
};
|
|
|
|
static uint8_t
|
|
setrs2(void)
|
|
{
|
|
uint8_t rs2;
|
|
|
|
rs2 = vgaxi(Crtx, 0x55);
|
|
vgaxo(Crtx, 0x55, (rs2 & 0xFC)|0x01);
|
|
|
|
return rs2;
|
|
}
|
|
|
|
static uint8_t
|
|
rgb524xi(int index)
|
|
{
|
|
outportb(dacxreg[IndexLo], index & 0xFF);
|
|
outportb(dacxreg[IndexHi], (index>>8) & 0xFF);
|
|
|
|
return inportb(dacxreg[Data]);
|
|
}
|
|
|
|
static void
|
|
rgb524xo(int index, uint8_t data)
|
|
{
|
|
outportb(dacxreg[IndexLo], index & 0xFF);
|
|
outportb(dacxreg[IndexHi], (index>>8) & 0xFF);
|
|
|
|
outportb(dacxreg[Data], data);
|
|
}
|
|
|
|
static void
|
|
restorers2(uint8_t rs2)
|
|
{
|
|
vgaxo(Crtx, 0x55, rs2);
|
|
}
|
|
|
|
static void
|
|
clock(Vga* vga, Ctlr* ctlr)
|
|
{
|
|
if(vga->f[0] >= 16250000 && vga->f[0] <= 32000000){
|
|
vga->f[0] = (vga->f[0]/250000)*250000;
|
|
vga->d[0] = (4*vga->f[0])/1000000 - 65;
|
|
}
|
|
else if(vga->f[0] >= 32500000 && vga->f[0] <= 64000000){
|
|
vga->f[0] = (vga->f[0]/500000)*500000;
|
|
vga->d[0] = 0x40|((2*vga->f[0])/1000000 - 65);
|
|
}
|
|
else if(vga->f[0] >= 65000000 && vga->f[0] <= 128000000){
|
|
vga->f[0] = (vga->f[0]/1000000)*1000000;
|
|
vga->d[0] = 0x80|(vga->f[0]/1000000 - 65);
|
|
}
|
|
else if(vga->f[0] >= 130000000 && vga->f[0] <= 220000000){
|
|
vga->f[0] = (vga->f[0]/2000000)*2000000;
|
|
vga->d[0] = 0xC0|((vga->f[0]/2)/1000000 - 65);
|
|
}
|
|
else
|
|
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 -170 and -220MHz speed-grades.
|
|
*/
|
|
pclk = 170000000;
|
|
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 clock-doubler or not.
|
|
*/
|
|
if((ctlr->flag & Uclk2) == 0 && vga->mode->z == 8)
|
|
resyncinit(vga, ctlr, Uclk2, 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 PLL.
|
|
*/
|
|
vga->misc &= ~0x0C;
|
|
if(vga->mode->z == 8 || (vga->f[0] != VgaFreq0 && vga->f[0] != VgaFreq1)){
|
|
/*
|
|
* Initialise the PLL parameters.
|
|
* Use internal FS3 fixed-reference divider.
|
|
*/
|
|
clock(vga, ctlr);
|
|
vga->i[0] = 0x03;
|
|
}
|
|
else if(vga->f[0] == VgaFreq0)
|
|
vga->i[0] = 0;
|
|
else if(vga->f[0] == VgaFreq1){
|
|
vga->misc |= 0x04;
|
|
vga->i[0] = 1;
|
|
}
|
|
|
|
ctlr->flag |= Finit;
|
|
}
|
|
|
|
static void
|
|
load(Vga* vga, Ctlr* ctlr)
|
|
{
|
|
uint8_t mc2, rs2, x;
|
|
char *val;
|
|
int f;
|
|
|
|
rs2 = setrs2();
|
|
|
|
/*
|
|
* Set VgaFreq[01].
|
|
*/
|
|
rgb524xo(PLLControl1, 0x00);
|
|
rgb524xo(Frequency0, 0x24);
|
|
rgb524xo(Frequency0+1, 0x30);
|
|
|
|
if(val = dbattr(vga->attr, "rgb524refclk")){
|
|
f = strtol(val, 0, 0);
|
|
if(f > 1000000)
|
|
f /= 1000000;
|
|
rgb524xo(PLLReference, f/2);
|
|
}
|
|
|
|
/*
|
|
* Enable pixel programming and clock divide
|
|
* factor.
|
|
*/
|
|
x = rgb524xi(MiscClock) & ~0x0E;
|
|
x |= 0x01;
|
|
if(ctlr->flag & Uclk2)
|
|
x |= 0x02;
|
|
rgb524xo(MiscClock, x);
|
|
|
|
if(vga->mode->z == 1)
|
|
rgb524xo(PixelFormat, 0x02);
|
|
else if(vga->mode->z == 8)
|
|
rgb524xo(PixelFormat, 0x03);
|
|
|
|
x = rgb524xi(MiscControl1) & ~0x41;
|
|
x |= 0x01;
|
|
rgb524xo(MiscControl1, x);
|
|
|
|
mc2 = rgb524xi(MiscControl2) & ~0x41;
|
|
vga->crt[0x22] &= ~0x08;
|
|
if(vga->i[0] == 3){
|
|
rgb524xo(Frequency0+3, vga->d[0]);
|
|
rgb524xo(PLLControl1, 0x02);
|
|
rgb524xo(PLLControl2, vga->i[0]);
|
|
mc2 |= 0x41;
|
|
vga->crt[0x22] |= 0x08;
|
|
}
|
|
rgb524xo(MiscControl2, mc2);
|
|
vgaxo(Crtx, 0x22, vga->crt[0x22]);
|
|
|
|
restorers2(rs2);
|
|
ctlr->flag |= Fload;
|
|
}
|
|
|
|
static void
|
|
dump(Vga* vga, Ctlr* ctlr)
|
|
{
|
|
uint8_t rs2, r, x[256];
|
|
char buf[32];
|
|
int df, i, maxf, vcodc, vf;
|
|
|
|
rs2 = setrs2();
|
|
printitem(ctlr->name, "index00");
|
|
for(i = 0x00; i < 0x0F; i++){
|
|
x[i] = rgb524xi(i);
|
|
printreg(x[i]);
|
|
}
|
|
printitem(ctlr->name, "index10");
|
|
for(i = 0x10; i < 0x17; i++){
|
|
x[i] = rgb524xi(i);
|
|
printreg(x[i]);
|
|
}
|
|
printitem(ctlr->name, "index20");
|
|
for(i = 0x20; i < 0x30; i++){
|
|
x[i] = rgb524xi(i);
|
|
printreg(x[i]);
|
|
}
|
|
printitem(ctlr->name, "index30");
|
|
for(i = 0x30; i < 0x37; i++){
|
|
x[i] = rgb524xi(i);
|
|
printreg(x[i]);
|
|
}
|
|
printitem(ctlr->name, "index40");
|
|
for(i = 0x40; i < 0x49; i++){
|
|
x[i] = rgb524xi(i);
|
|
printreg(x[i]);
|
|
}
|
|
printitem(ctlr->name, "index60");
|
|
for(i = 0x60; i < 0x63; i++){
|
|
x[i] = rgb524xi(i);
|
|
printreg(x[i]);
|
|
}
|
|
printitem(ctlr->name, "index70");
|
|
for(i = 0x70; i < 0x73; i++){
|
|
x[i] = rgb524xi(i);
|
|
printreg(x[i]);
|
|
}
|
|
printitem(ctlr->name, "index8E");
|
|
for(i = 0x8E; i < 0x92; i++){
|
|
x[i] = rgb524xi(i);
|
|
printreg(x[i]);
|
|
}
|
|
restorers2(rs2);
|
|
|
|
/*
|
|
* x[0x10] pixel clock frequency selection
|
|
* 0, 2 for direct programming
|
|
* x[0x20-0x2F] pixel frequency 0-15
|
|
*/
|
|
printitem(ctlr->name, "refclk");
|
|
Bprint(&stdout, "%12ud\n", x[PLLReference]*2*1000000);
|
|
if((i = (x[0x10] & 0x07)) == 0x00 || i == 0x02){
|
|
/*
|
|
* Direct programming, external frequency select.
|
|
* F[0-4] are probably tied directly to the 2 clock-select
|
|
* bits in the VGA Misc register.
|
|
*/
|
|
if(i == 0)
|
|
maxf = 4;
|
|
else
|
|
maxf = 16;
|
|
for(i = 0; i < maxf; i++){
|
|
if((r = x[0x20+i]) == 0)
|
|
continue;
|
|
sprint(buf, "direct F%X", i);
|
|
printitem(ctlr->name, buf);
|
|
df = (r>>6) & 0x03;
|
|
vcodc = r & 0x3F;
|
|
|
|
vf = 0;
|
|
switch(df){
|
|
case 0:
|
|
vf = (vcodc+65)/4;
|
|
break;
|
|
|
|
case 1:
|
|
vf = (vcodc+65)/2;
|
|
break;
|
|
|
|
case 2:
|
|
vf = (vcodc+65);
|
|
break;
|
|
|
|
case 3:
|
|
vf = (vcodc+65)*2;
|
|
break;
|
|
}
|
|
Bprint(&stdout, "%12ud\n", vf);
|
|
}
|
|
}
|
|
}
|
|
|
|
Ctlr rgb524 = {
|
|
"rgb524", /* name */
|
|
0, /* snarf */
|
|
0, /* options */
|
|
init, /* init */
|
|
load, /* load */
|
|
dump, /* dump */
|
|
};
|