347 lines
7.5 KiB
C
347 lines
7.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"
|
|
|
|
/*
|
|
* Cirrus Logic True Color VGA Family - CL-GD542X.
|
|
* Also works for Alpine VGA Family - CL-GD543X.
|
|
* Just the basics. BUGS:
|
|
* the added capabilities of the 543X aren't used.
|
|
*/
|
|
|
|
typedef struct {
|
|
uint8_t id; /* Id */
|
|
uint32_t vclk; /* Maximum dot clock */
|
|
} Gd542x;
|
|
|
|
static Gd542x family[] = {
|
|
{ 0x88, 75000000, }, /* CL-GD5420 */
|
|
{ 0x8C, 80000000, }, /* CL-GD5422 */
|
|
{ 0x94, 80000000, }, /* CL-GD5424 */
|
|
{ 0x90, 80000000, }, /* CL-GD5426 */
|
|
{ 0x98, 80000000, }, /* CL-GD5428 */
|
|
{ 0x9C, 86000000, }, /* CL-GD5429 */
|
|
{ 0xA0, 86000000, }, /* CL-GD5430 */
|
|
{ 0xA8, 86000000, }, /* CL-GD5434 */
|
|
|
|
{ 0xAC, 135000000, }, /* CL-GD5436 */
|
|
{ 0xB8, 135000000, }, /* CL-GD5446 */
|
|
{ 0xBC, 135000000, }, /* CL-GD5480 */
|
|
|
|
{ 0x30, 80000000, }, /* CL-GD7543 */
|
|
|
|
{ 0x00, },
|
|
};
|
|
|
|
static Gd542x*
|
|
identify(Vga* vga, Ctlr* ctlr)
|
|
{
|
|
Gd542x *gd542x;
|
|
uint8_t id;
|
|
|
|
id = vga->crt[0x27] & ~0x03;
|
|
for(gd542x = &family[0]; gd542x->id; gd542x++){
|
|
if(gd542x->id == id)
|
|
return gd542x;
|
|
}
|
|
|
|
error("%s: unknown chip id - 0x%2.2X\n", ctlr->name, vga->crt[0x27]);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
snarf(Vga* vga, Ctlr* ctlr)
|
|
{
|
|
int i;
|
|
Gd542x *gd542x;
|
|
|
|
/*
|
|
* Unlock extended registers.
|
|
*/
|
|
vgaxo(Seqx, 0x06, 0x12);
|
|
|
|
/*
|
|
* Save all the registers, even though we'll only
|
|
* change a handful.
|
|
*/
|
|
for(i = 0x06; i < 0x20; i++)
|
|
vga->sequencer[i] = vgaxi(Seqx, i);
|
|
|
|
for(i = 0x09; i < 0x3A; i++)
|
|
vga->graphics[i] = vgaxi(Grx, i);
|
|
|
|
for(i = 0x19; i < 0x1E; i++)
|
|
vga->crt[i] = vgaxi(Crtx, i);
|
|
|
|
vga->crt[0x27] = vgaxi(Crtx, 0x27);
|
|
|
|
/*
|
|
* Hack for Hidden DAC Register. Do 4 dummy reads
|
|
* of Pixmask first.
|
|
*/
|
|
for(i = 0; i < 4; i++)
|
|
vgai(Pixmask);
|
|
vga->crt[0x28] = vgai(Pixmask);
|
|
|
|
i = 0;
|
|
switch(vga->crt[0x27] & ~0x03){
|
|
|
|
case 0x88: /* CL-GD5420 */
|
|
case 0x8C: /* CL-GD5422 */
|
|
case 0x94: /* CL-GD5424 */
|
|
case 0x80: /* CL-GD5425 */
|
|
case 0x90: /* CL-GD5426 */
|
|
case 0x98: /* CL-GD5427 */
|
|
case 0x9C: /* CL-GD5429 */
|
|
/*
|
|
* The BIOS leaves the memory size in Seq0A, bits 4 and 3.
|
|
* See Technical Reference Manual Appendix E1, Section 1.3.2.
|
|
*
|
|
* The storage area for the 64x64 cursors is the last 16Kb of
|
|
* display memory.
|
|
*/
|
|
i = (vga->sequencer[0x0A]>>3) & 0x03;
|
|
break;
|
|
|
|
case 0xA0: /* CL-GD5430 */
|
|
case 0xA8: /* CL-GD5434 */
|
|
case 0xAC: /* CL-GD5436 */
|
|
case 0xB8: /* CL-GD5446 */
|
|
case 0x30: /* CL-GD7543 */
|
|
/*
|
|
* Attempt to intuit the memory size from the DRAM control
|
|
* register. Minimum is 512KB.
|
|
* If DRAM bank switching is on then there's double.
|
|
*/
|
|
i = (vga->sequencer[0x0F]>>3) & 0x03;
|
|
if(vga->sequencer[0x0F] & 0x80)
|
|
i++;
|
|
|
|
/*
|
|
* If it's a PCI card, can do linear.
|
|
* Most of the Cirrus chips can do linear addressing with
|
|
* all the different buses, but it can get messy. It's easy
|
|
* to cut PCI on the CLGD543x chips out of the pack.
|
|
*/
|
|
if(((vga->sequencer[0x17]>>3) & 0x07) == 0x04)
|
|
ctlr->flag |= Hlinear;
|
|
break;
|
|
case 0xBC: /* CL-GD5480 */
|
|
i = 2; /* 1024 = 256<<2 */
|
|
if((vga->sequencer[0x0F] & 0x18) == 0x18){
|
|
i <<= 1; /* 2048 = 256<<3 */
|
|
if(vga->sequencer[0x0F] & 0x80)
|
|
i <<= 2; /* 2048 = 256<<4 */
|
|
}
|
|
if(vga->sequencer[0x17] & 0x80)
|
|
i <<= 1;
|
|
ctlr->flag |= Hlinear;
|
|
break;
|
|
|
|
default: /* uh, ah dunno */
|
|
break;
|
|
}
|
|
|
|
if(vga->linear && (ctlr->flag & Hlinear)){
|
|
vga->vmz = 16*1024*1024;
|
|
vga->vma = 16*1024*1024;
|
|
ctlr->flag |= Ulinear;
|
|
}
|
|
else
|
|
vga->vmz = (256<<i)*1024;
|
|
|
|
gd542x = identify(vga, ctlr);
|
|
if(vga->f[1] == 0 || vga->f[1] > gd542x->vclk)
|
|
vga->f[1] = gd542x->vclk;
|
|
ctlr->flag |= Fsnarf;
|
|
}
|
|
|
|
void
|
|
clgd54xxclock(Vga* vga, Ctlr* ctlr)
|
|
{
|
|
int f;
|
|
uint32_t d, dmin, fmin, n, nmin, p;
|
|
|
|
trace("%s->init->clgd54xxclock\n", ctlr->name);
|
|
|
|
/*
|
|
* Athough the Technical Reference Manual says only a handful
|
|
* of frequencies are tested, it also gives the following formula
|
|
* which can be used to generate any frequency within spec.,
|
|
* including the tested ones.
|
|
*
|
|
* Numerator is 7-bits, denominator 5-bits.
|
|
* Guess from the Technical Reference Manual that
|
|
* The post divisor is 1 for vclk<40MHz.
|
|
*
|
|
* Look for values of n and d and p that give
|
|
* the least error for
|
|
* vclk = (RefFreq*n)/(d*(1+p));
|
|
*
|
|
* There's nothing like brute force and ignorance.
|
|
*/
|
|
fmin = vga->f[0];
|
|
nmin = 69;
|
|
dmin = 24;
|
|
if(vga->f[0] >= 40000000)
|
|
p = 0;
|
|
else
|
|
p = 1;
|
|
for(n = 1; n < 128; n++){
|
|
for(d = 1; d < 32; d++){
|
|
f = vga->f[0] - (RefFreq*n)/(d*(1+p));
|
|
if(f < 0)
|
|
f = -f;
|
|
if(f <= fmin){
|
|
fmin = f;
|
|
nmin = n;
|
|
dmin = d;
|
|
}
|
|
}
|
|
}
|
|
|
|
vga->f[0] = (RefFreq*nmin)/(dmin*(1+p));
|
|
vga->d[0] = dmin;
|
|
vga->n[0] = nmin;
|
|
vga->p[0] = p;
|
|
}
|
|
|
|
void
|
|
init(Vga* vga, Ctlr* ctlr)
|
|
{
|
|
Mode *mode;
|
|
Gd542x *gd542x;
|
|
uint16_t x;
|
|
|
|
mode = vga->mode;
|
|
gd542x = identify(vga, ctlr);
|
|
|
|
if(vga->f[0] == 0)
|
|
vga->f[0] = vga->mode->frequency;
|
|
if(vga->f[0] > gd542x->vclk)
|
|
error("%s: pclk %lud too high (> %lud)\n",
|
|
ctlr->name, vga->f[0], gd542x->vclk);
|
|
|
|
if(mode->z > 8)
|
|
error("%s: depth %d not supported\n", ctlr->name, mode->z);
|
|
|
|
/*
|
|
* VCLK3
|
|
*/
|
|
clgd54xxclock(vga, ctlr);
|
|
vga->misc |= 0x0C;
|
|
vga->sequencer[0x0E] = vga->n[0];
|
|
vga->sequencer[0x1E] = (vga->d[0]<<1)|vga->p[0];
|
|
|
|
vga->sequencer[0x07] = 0x00;
|
|
if(mode->z == 8)
|
|
vga->sequencer[0x07] |= 0x01;
|
|
|
|
if(vga->f[0] >= 42000000)
|
|
vga->sequencer[0x0F] |= 0x20;
|
|
else
|
|
vga->sequencer[0x0F] &= ~0x20;
|
|
|
|
vga->sequencer[0x16] = (vga->sequencer[0x16] & 0xF0)|0x08;
|
|
|
|
/*
|
|
* Overflow bits.
|
|
*/
|
|
vga->crt[0x1A] = 0x00;
|
|
x = mode->ehb>>3;
|
|
if(x & 0x40)
|
|
vga->crt[0x1A] |= 0x10;
|
|
if(x & 0x80)
|
|
vga->crt[0x1A] |= 0x20;
|
|
if(vga->crt[0x16] & 0x100)
|
|
vga->crt[0x1A] |= 0x40;
|
|
if(vga->crt[0x16] & 0x200)
|
|
vga->crt[0x1A] |= 0x80;
|
|
vga->crt[0x1B] = 0x22;
|
|
if(vga->crt[0x13] & 0x100)
|
|
vga->crt[0x1B] |= 0x10;
|
|
|
|
vga->graphics[0x0B] = 0x00;
|
|
if(vga->vmz > 1024*1024)
|
|
vga->graphics[0x0B] |= 0x20;
|
|
|
|
if(mode->interlace == 'v'){
|
|
vga->crt[0x19] = vga->crt[0x00]/2;
|
|
vga->crt[0x1A] |= 0x01;
|
|
}
|
|
}
|
|
|
|
static void
|
|
load(Vga* vga, Ctlr* ctlr)
|
|
{
|
|
vgaxo(Seqx, 0x0E, vga->sequencer[0x0E]);
|
|
vgaxo(Seqx, 0x1E, vga->sequencer[0x1E]);
|
|
if(ctlr->flag & Ulinear)
|
|
vga->sequencer[0x07] |= 0xE0;
|
|
vgaxo(Seqx, 0x07, vga->sequencer[0x07]);
|
|
vgaxo(Seqx, 0x0F, vga->sequencer[0x0F]);
|
|
vgaxo(Seqx, 0x16, vga->sequencer[0x16]);
|
|
|
|
if(vga->mode->interlace == 'v')
|
|
vgaxo(Crtx, 0x19, vga->crt[0x19]);
|
|
vgaxo(Crtx, 0x1A, vga->crt[0x1A]);
|
|
vgaxo(Crtx, 0x1B, vga->crt[0x1B]);
|
|
|
|
vgaxo(Grx, 0x0B, vga->graphics[0x0B]);
|
|
}
|
|
|
|
static void
|
|
dump(Vga* vga, Ctlr* ctlr)
|
|
{
|
|
int i;
|
|
char *name;
|
|
|
|
name = ctlr->name;
|
|
|
|
printitem(name, "Seq06");
|
|
for(i = 0x06; i < 0x20; i++)
|
|
printreg(vga->sequencer[i]);
|
|
|
|
printitem(name, "Crt19");
|
|
for(i = 0x19; i < 0x1E; i++)
|
|
printreg(vga->crt[i]);
|
|
|
|
printitem(name, "Gr09");
|
|
for(i = 0x09; i < 0x3A; i++)
|
|
printreg(vga->graphics[i]);
|
|
|
|
printitem(name, "Id Hdr");
|
|
printreg(vga->crt[0x27]);
|
|
printreg(vga->crt[0x28]);
|
|
}
|
|
|
|
Ctlr clgd542x = {
|
|
"clgd542x", /* name */
|
|
snarf, /* snarf */
|
|
0, /* options */
|
|
init, /* init */
|
|
load, /* load */
|
|
dump, /* dump */
|
|
};
|
|
|
|
Ctlr clgd542xhwgc = {
|
|
"clgd542xhwgc", /* name */
|
|
0, /* snarf */
|
|
0, /* options */
|
|
0, /* init */
|
|
0, /* load */
|
|
0, /* dump */
|
|
};
|