Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#include <u.h>
#include <libc.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, uchar 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)
{
        ulong sdata;
        int i;
        uchar 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)
{
        uchar 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 */
};