Subversion Repositories shark

Rev

Rev 54 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * icd2061a.c
 *
 * support for the ICD 2061A programmable clockchip and compatibles
 * outside of the DAC
 *
 * Rev history:
 * Andreas Arens Dec 95: Created
 *
 * Andreas Arens Feb 15 1996: A few minor fixes
 */


#include "timing.h"
#include "libvga.h"
#include "ramdac.h"
#include "clockchip.h"
#include "driver.h"
#include "vga.h"

/*
 * ATTENTION: The ICD 2061A does not support reading of the currently selected
 * pixelclock. XFree86 also fails to restore this value correctly, but always
 * estores a 45 MHz pixelclock. My standard text mode (132x25) uses 40 MHz,
 * which is the value selected here.
 * You can use the SVGATextMode-1.0 'clockprobe' tool right after boot to
 * determine the value used with your card and modify here, but since 40 MHz
 * is the VESA suggested pixelclock for a 70 Hz 132x25 mode, the value here
 * seems fine. Note that 80xXX modes use 25 or 28 MHz clocks, which are fixed
 * and not affected by this. This might not be true for Diamond boards using
 * the DCS2824-0 clockchip, which is an ICD 2061A clone.
 */

#define I2061A_DEFAULT_TEXT_FREQUENCY   (40000L)        /* kHz */

/*
 * Clockchip code is derived from I2051Aalt.c in XFree86/common_hw which
 * in turn is derived from code available from the STB bulletin board.
 * A number of modifications have been made to fit this into SVGAlib.
 */


#define I2061A_CRYSTAL_FREQUENCY       (14.31818 * 2.0)

static double I2061A_range[15] =
{50.0, 51.0, 53.2, 58.5, 60.7, 64.4, 66.8, 73.5,
 75.6, 80.9, 83.2, 91.5, 100.0, 120.0, 120.0000001};

static long I2061A_SelectClock(long frequency)
                                /* in KHz */
{
    unsigned int m;
    int i;
    double freq, fvco;
    double dev, devx;
    double delta, deltax;
    double f0;
    unsigned int p, q;
    unsigned int bestp = 0, bestq = 0, bestm = 0, besti = 0;

    freq = ((double) frequency) / 1000.0;
    if (freq > I2061A_range[13])
        freq = I2061A_range[13];
    else if (freq < 7.0)
        freq = 7.0;

    /*
     *  Calculate values to load into ICD 2061A clock chip to set frequency
     */

    delta = 999.0;
    dev = 999.0;

    for (m = 0; m < 8; m++) {
        fvco = freq * (1 << m);
        if (fvco < 50.0 || fvco > 120.0)
            continue;

        f0 = fvco / I2061A_CRYSTAL_FREQUENCY;

        for (q = 14; q <= 71; q++) {    /* q={15..71}:Constraint 2 on page 14 */
            p = (int) (f0 * q + 0.5);
            if (p < 4 || p > 130)       /* p={4..130}:Constraint 5 on page 14 */
                continue;
            deltax = (double) (p) / (double) (q) - f0;
            if (deltax < 0)
                deltax = -deltax;
            if (deltax <= delta) {
                for (i = 13; i >= 0; i--)
                    if (fvco >= I2061A_range[i])
                        break;
                devx = (fvco - (I2061A_range[i] + I2061A_range[i + 1]) / 2) / fvco;
                if (devx < 0)
                    devx = -devx;
                if (deltax < delta || devx < dev) {
                    delta = deltax;
                    dev = devx;
                    bestp = p;
                    bestq = q;
                    bestm = m;
                    besti = i;
                }
            }
        }
    }
    return ((((((long) besti << 7) | (bestp - 3)) << 3) | bestm) << 7) | (bestq - 2);
}

static int I2061A_GetClock(long dwv)
{
    int clock_q = (dwv & 0x7f) + 2;
    int clock_m = (dwv >> 7) & 7;
    int clock_p = ((dwv >> 10) & 0x7f) + 3;
    double fvco;

    fvco = I2061A_CRYSTAL_FREQUENCY / (1 << clock_m);
    return (int) (((fvco * clock_p) / clock_q) * 1000);
}

/* needs some delay for really fast cpus */
#define wrt_clk_bit(v) outb(MIS_W, v), (void)inb(crtcaddr), (void)inb(crtcaddr)

/* ATTENTION: This assumes CRTC registers and S3 registers to be UNLOCKED! */
static void I2061A_init_clock(unsigned long setup)
{
    unsigned char nclk[2], clk[2];
    unsigned short restore42;
    unsigned short oldclk;
    unsigned short bitval;
    int i;
    unsigned char c;
    unsigned short crtcaddr = (inb(MIS_R) & 0x01) ? CRT_IC : CRT_IM;

    oldclk = inb(MIS_R);

    outb(crtcaddr, 0x42);
    restore42 = inb(crtcaddr + 1);

    outw(SEQ_I, 0x0100);

    outb(SEQ_I, 1);
    c = inb(SEQ_D);
    outb(SEQ_D, 0x20 | c);

    outb(crtcaddr, 0x42);
    outb(crtcaddr + 1, 0x03);

    outw(SEQ_I, 0x0300);

    nclk[0] = oldclk & 0xF3;
    nclk[1] = nclk[0] | 0x08;
    clk[0] = nclk[0] | 0x04;
    clk[1] = nclk[0] | 0x0C;

    outb(crtcaddr, 0x42);
    (void) inb(crtcaddr + 1);

    outw(SEQ_I, 0x0100);

    wrt_clk_bit(oldclk | 0x08);
    wrt_clk_bit(oldclk | 0x0C);
    for (i = 0; i < 5; i++) {
        wrt_clk_bit(nclk[1]);
        wrt_clk_bit(clk[1]);
    }
    wrt_clk_bit(nclk[1]);
    wrt_clk_bit(nclk[0]);
    wrt_clk_bit(clk[0]);
    wrt_clk_bit(nclk[0]);
    wrt_clk_bit(clk[0]);
    for (i = 0; i < 24; i++) {
        bitval = setup & 0x01;
        setup >>= 1;
        wrt_clk_bit(clk[1 - bitval]);
        wrt_clk_bit(nclk[1 - bitval]);
        wrt_clk_bit(nclk[bitval]);
        wrt_clk_bit(clk[bitval]);
    }
    wrt_clk_bit(clk[1]);
    wrt_clk_bit(nclk[1]);
    wrt_clk_bit(clk[1]);

    outb(SEQ_I, 1);
    c = inb(SEQ_D);
    outb(SEQ_D, 0xDF & c);

    outb(crtcaddr, 0x42);
    outb(crtcaddr + 1, restore42);

    outb(MIS_W, oldclk);

    outw(SEQ_I, 0x0300);

    vga_waitretrace();
    vga_waitretrace();
    vga_waitretrace();
    vga_waitretrace();
    vga_waitretrace();
    vga_waitretrace();
    vga_waitretrace();          /* 0.10 second delay... */
}

static int I2061A_match_programmable_clock(int desiredclock)
{
    long dvw;

    dvw = I2061A_SelectClock((long) desiredclock);
    if (dvw)
        return I2061A_GetClock(dvw);
    return 0;
}

static void I2061A_saveState(unsigned char *regs)
{
    long *dvwp;

    if (__svgalib_I2061A_clockchip_methods.DAC_saveState)
        __svgalib_I2061A_clockchip_methods.DAC_saveState(regs);

    dvwp = (long *) (regs + __svgalib_I2061A_clockchip_methods.DAC_stateSize);
    *dvwp = I2061A_SelectClock(__svgalib_I2061A_clockchip_methods.TextFrequency);
}

static void I2061A_restoreState(const unsigned char *regs)
{
    unsigned int clknum = 2;
    long *dvwp;

    if (__svgalib_I2061A_clockchip_methods.DAC_restoreState)
        __svgalib_I2061A_clockchip_methods.DAC_restoreState(regs);
    dvwp = (long *) (regs + __svgalib_I2061A_clockchip_methods.DAC_stateSize);
    if (*dvwp) {
        /*
         * Write ICD 2061A clock chip - assumes S3 to be unlocked!
         */

        I2061A_init_clock(((unsigned long) *dvwp) | (((long) clknum) << 21));
    }
}

static void I2061A_initializeState(unsigned char *regs, int bpp, int colormode, int pixelclock)
{
    long *dvwp;

    if (__svgalib_I2061A_clockchip_methods.DAC_initializeState)
        __svgalib_I2061A_clockchip_methods.DAC_initializeState(regs, bpp, colormode, pixelclock);

    dvwp = (long *) (regs + __svgalib_I2061A_clockchip_methods.DAC_stateSize);

    if (bpp > 16)
        pixelclock *= 4;
    else if (bpp > 8)
        pixelclock *= 2;

    *dvwp = I2061A_SelectClock((long) pixelclock);
}

/* This functions patches the DacMethod to route through the ClockChip Method */
static void I2061A_init(CardSpecs * cardspecs, DacMethods * DAC)
{
    if (DAC && !__svgalib_I2061A_clockchip_methods.DAC_initializeState) {
        if (__svgalib_driver_report)
            cprintf("svgalib: Using ICD2061A or compatible clockchip.\n");
        __svgalib_I2061A_clockchip_methods.DAC_initializeState = DAC->initializeState;
        __svgalib_I2061A_clockchip_methods.DAC_saveState = DAC->saveState;
        __svgalib_I2061A_clockchip_methods.DAC_restoreState = DAC->restoreState;
        __svgalib_I2061A_clockchip_methods.DAC_stateSize = DAC->stateSize;
        DAC->initializeState = I2061A_initializeState;
        DAC->saveState = I2061A_saveState;
        DAC->restoreState = I2061A_restoreState;
        DAC->stateSize += sizeof(long);
        cardspecs->matchProgrammableClock = I2061A_match_programmable_clock;
        cardspecs->flags |= CLOCK_PROGRAMMABLE;
    }
}

ClockChipMethods __svgalib_I2061A_clockchip_methods =
{
    I2061A_init,
    I2061A_saveState,
    I2061A_restoreState,
    I2061A_initializeState,
    NULL,                       /* DAC function save area */
    NULL,
    NULL,
    I2061A_DEFAULT_TEXT_FREQUENCY,
    0,
};