288 lines
6.8 KiB
C
288 lines
6.8 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"
|
|
|
|
/*
|
|
* Clocks which require fiddling with the S3 registers
|
|
* in order to be loaded.
|
|
*/
|
|
static void
|
|
setcrt42(Vga* vga, Ctlr* ctlr, uint8_t index)
|
|
{
|
|
trace("%s->clock->setcrt42\n", ctlr->name);
|
|
|
|
vgao(MiscW, vga->misc & ~0x0C);
|
|
outportb(Crtx+1, 0x00);
|
|
|
|
vga->crt[0x42] = (vga->crt[0x42] & 0xF0)|index;
|
|
vgao(MiscW, vga->misc);
|
|
vgaxo(Crtx, 0x42, vga->crt[0x42]);
|
|
}
|
|
|
|
static void
|
|
icd2061aload(Vga* vga, Ctlr* ctlr)
|
|
{
|
|
uint32_t sdata;
|
|
int i;
|
|
uint8_t crt42;
|
|
|
|
trace("%s->clock->icd2061aload\n", ctlr->name);
|
|
/*
|
|
* The serial word to be loaded into the icd2061a is
|
|
* (2<<21)|(vga->i<<17)|((vga->n)<<10)|(vga->p<<7)|vga->d
|
|
* Always select ICD2061A REG2.
|
|
*/
|
|
sdata = (2<<21)|(vga->i[0]<<17)|((vga->n[0])<<10)|(vga->p[0]<<7)|vga->d[0];
|
|
|
|
/*
|
|
* The display should be already off to enable us to clock the
|
|
* serial data word into either MiscW or Crt42.
|
|
*
|
|
* Set the Misc register to make clock-select-out the contents of
|
|
* register Crt42. Must turn the sequencer off when changing bits
|
|
* <3:2> of Misc. Also, must set Crt42 to 0 before setting <3:2>
|
|
* of Misc due to a hardware glitch.
|
|
*/
|
|
vgao(MiscW, vga->misc & ~0x0C);
|
|
crt42 = vgaxi(Crtx, 0x42) & 0xF0;
|
|
outportb(Crtx+1, 0x00);
|
|
|
|
vgaxo(Seqx, 0x00, 0x00);
|
|
vgao(MiscW, vga->misc|0x0C);
|
|
vgaxo(Seqx, 0x00, 0x03);
|
|
|
|
/*
|
|
* Unlock the ICD2061A. The unlock sequence is at least 5 low-to-high
|
|
* transitions of CLK with DATA high, followed by a single low-to-high
|
|
* transition of CLK with DATA low.
|
|
* Using Crt42, CLK is bit0, DATA is bit 1. If we used MiscW, they'd
|
|
* be bits 2 and 3 respectively.
|
|
*/
|
|
outportb(Crtx+1, crt42|0x00); /* -DATA|-CLK */
|
|
outportb(Crtx+1, crt42|0x02); /* +DATA|-CLK */
|
|
for(i = 0; i < 5; i++){
|
|
outportb(Crtx+1, crt42|0x03); /* +DATA|+CLK */
|
|
outportb(Crtx+1, crt42|0x02); /* +DATA|-CLK */
|
|
}
|
|
outportb(Crtx+1, crt42|0x00); /* -DATA|-CLK */
|
|
outportb(Crtx+1, crt42|0x01); /* -DATA|+CLK */
|
|
|
|
/*
|
|
* Now write the serial data word, framed by a start-bit and a stop-bit.
|
|
* The data is written using a modified Manchester encoding such that a
|
|
* data-bit is sampled on the rising edge of CLK and the complement of
|
|
* the data-bit is sampled on the previous falling edge of CLK.
|
|
*/
|
|
outportb(Crtx+1, crt42|0x00); /* -DATA|-CLK (start-bit) */
|
|
outportb(Crtx+1, crt42|0x01); /* -DATA|+CLK */
|
|
|
|
for(i = 0; i < 24; i++){ /* serial data word */
|
|
if(sdata & 0x01){
|
|
outportb(Crtx+1, crt42|0x01); /* -DATA|+CLK */
|
|
outportb(Crtx+1, crt42|0x00); /* -DATA|-CLK (falling edge) */
|
|
outportb(Crtx+1, crt42|0x02); /* +DATA|-CLK */
|
|
outportb(Crtx+1, crt42|0x03); /* +DATA|+CLK (rising edge) */
|
|
}
|
|
else {
|
|
outportb(Crtx+1, crt42|0x03); /* +DATA|+CLK */
|
|
outportb(Crtx+1, crt42|0x02); /* +DATA|-CLK (falling edge) */
|
|
outportb(Crtx+1, crt42|0x00); /* -DATA|-CLK */
|
|
outportb(Crtx+1, crt42|0x01); /* -DATA|+CLK (rising edge) */
|
|
}
|
|
sdata >>= 1;
|
|
}
|
|
|
|
outportb(Crtx+1, crt42|0x03); /* +DATA|+CLK (stop-bit) */
|
|
outportb(Crtx+1, crt42|0x02); /* +DATA|-CLK */
|
|
outportb(Crtx+1, crt42|0x03); /* +DATA|+CLK */
|
|
|
|
/*
|
|
* We always use REG2 in the ICD2061A.
|
|
*/
|
|
setcrt42(vga, ctlr, 0x02);
|
|
}
|
|
|
|
static void
|
|
ch9294load(Vga* vga, Ctlr* ctlr)
|
|
{
|
|
trace("%s->clock->ch9294load\n", ctlr->name);
|
|
|
|
setcrt42(vga, ctlr, vga->i[0]);
|
|
}
|
|
|
|
static void
|
|
tvp3025load(Vga* vga, Ctlr* ctlr)
|
|
{
|
|
uint8_t crt5c, x;
|
|
|
|
trace("%s->clock->tvp3025load\n", ctlr->name);
|
|
|
|
/*
|
|
* Crt5C bit 5 is RS4.
|
|
* Clear it to select TVP3025 registers for
|
|
* the calls to tvp302xo().
|
|
*/
|
|
crt5c = vgaxi(Crtx, 0x5C);
|
|
vgaxo(Crtx, 0x5C, crt5c & ~0x20);
|
|
|
|
tvp3020xo(0x2C, 0x00);
|
|
tvp3020xo(0x2D, vga->d[0]);
|
|
tvp3020xo(0x2D, vga->n[0]);
|
|
tvp3020xo(0x2D, 0x08|vga->p[0]);
|
|
|
|
tvp3020xo(0x2F, 0x01);
|
|
tvp3020xo(0x2F, 0x01);
|
|
tvp3020xo(0x2F, vga->p[0]);
|
|
x = 0x54;
|
|
if(vga->ctlr && (vga->ctlr->flag & Uenhanced))
|
|
x = 0xC4;
|
|
tvp3020xo(0x1E, x);
|
|
|
|
vgaxo(Crtx, 0x5C, crt5c);
|
|
vgao(MiscW, vga->misc);
|
|
|
|
ctlr->flag |= Fload;
|
|
}
|
|
|
|
static void
|
|
tvp3026load(Vga* vga, Ctlr* ctlr)
|
|
{
|
|
trace("%s->clock->tvp3026load\n", ctlr->name);
|
|
|
|
if((vga->misc & 0x0C) != 0x0C && vga->mode->z == 1){
|
|
tvp3026xo(0x1A, 0x07);
|
|
tvp3026xo(0x18, 0x80);
|
|
tvp3026xo(0x19, 0x98);
|
|
tvp3026xo(0x2C, 0x2A);
|
|
tvp3026xo(0x2F, 0x00);
|
|
tvp3026xo(0x2D, 0x00);
|
|
tvp3026xo(0x39, 0x18);
|
|
setcrt42(vga, ctlr, 0);
|
|
}
|
|
else if(vga->mode->z == 8){
|
|
tvp3026xo(0x1A, 0x05);
|
|
tvp3026xo(0x18, 0x80);
|
|
tvp3026xo(0x19, 0x4C);
|
|
tvp3026xo(0x2C, 0x2A);
|
|
tvp3026xo(0x2F, 0x00);
|
|
tvp3026xo(0x2D, 0x00);
|
|
|
|
tvp3026xo(0x2C, 0x00);
|
|
tvp3026xo(0x2D, 0xC0|vga->n[0]);
|
|
tvp3026xo(0x2D, vga->m[0] & 0x3F);
|
|
tvp3026xo(0x2D, 0xB0|vga->p[0]);
|
|
while(!(tvp3026xi(0x2D) & 0x40))
|
|
;
|
|
|
|
tvp3026xo(0x39, 0x38|vga->q[1]);
|
|
tvp3026xo(0x2C, 0x00);
|
|
tvp3026xo(0x2F, 0xC0|vga->n[1]);
|
|
tvp3026xo(0x2F, vga->m[1]);
|
|
tvp3026xo(0x2F, 0xF0|vga->p[1]);
|
|
while(!(tvp3026xi(0x2F) & 0x40))
|
|
;
|
|
|
|
setcrt42(vga, ctlr, 3);
|
|
}
|
|
|
|
ctlr->flag |= Fload;
|
|
}
|
|
|
|
static struct {
|
|
char* name;
|
|
void (*load)(Vga*, Ctlr*);
|
|
} clocks[] = {
|
|
{ "icd2061a", icd2061aload, },
|
|
{ "ch9294", ch9294load, },
|
|
{ "tvp3025clock", tvp3025load, },
|
|
{ "tvp3026clock", tvp3026load, },
|
|
{ 0 },
|
|
};
|
|
|
|
static void
|
|
init(Vga* vga, Ctlr* ctlr)
|
|
{
|
|
char name[Namelen+1], *p;
|
|
int i;
|
|
|
|
if(vga->clock == 0)
|
|
return;
|
|
|
|
/*
|
|
* Check we know about it.
|
|
*/
|
|
strncpy(name, vga->clock->name, Namelen);
|
|
name[Namelen] = 0;
|
|
if(p = strchr(name, '-'))
|
|
*p = 0;
|
|
for(i = 0; clocks[i].name; i++){
|
|
if(strcmp(clocks[i].name, name) == 0)
|
|
break;
|
|
}
|
|
if(clocks[i].name == 0)
|
|
error("%s: unknown clock \"%s\"\n", ctlr->name, vga->clock->name);
|
|
|
|
if(vga->clock->init && (vga->clock->flag & Finit) == 0)
|
|
(*vga->clock->init)(vga, vga->clock);
|
|
|
|
/*
|
|
* If we don't already have a desired pclk,
|
|
* take it from the mode.
|
|
*/
|
|
if(vga->f[0] == 0)
|
|
vga->f[0] = vga->mode->frequency;
|
|
if(vga->f[0] != VgaFreq0 && vga->f[0] != VgaFreq1)
|
|
vga->misc |= 0x0C;
|
|
|
|
ctlr->flag |= Finit;
|
|
}
|
|
|
|
static void
|
|
load(Vga* vga, Ctlr* ctlr)
|
|
{
|
|
char name[Namelen+1], *p;
|
|
int i;
|
|
|
|
if(vga->clock == 0 || (vga->clock->flag & Fload))
|
|
return;
|
|
|
|
strncpy(name, vga->clock->name, Namelen);
|
|
name[Namelen] = 0;
|
|
if(p = strchr(name, '-'))
|
|
*p = 0;
|
|
|
|
for(i = 0; clocks[i].name; i++){
|
|
if(strcmp(clocks[i].name, name))
|
|
continue;
|
|
clocks[i].load(vga, ctlr);
|
|
if(strcmp(clocks[i].name, "icd2061a") == 0){
|
|
clocks[i].load(vga, ctlr);
|
|
clocks[i].load(vga, ctlr);
|
|
}
|
|
|
|
ctlr->flag |= Fload;
|
|
return;
|
|
}
|
|
}
|
|
|
|
Ctlr s3clock = {
|
|
"s3clock", /* name */
|
|
0, /* snarf */
|
|
0, /* options */
|
|
init, /* init */
|
|
load, /* load */
|
|
0, /* dump */
|
|
};
|