Subversion Repositories shark

Rev

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

/*
 * IBMRGB52x.c:
 *
 * RAMDAC definitions for IBM's RGB52x PaletteDAC.
 *
 * Portion of this file is derived from XFree86's source code.
 * [insert XFree86's copyright here].
 */


#include <stdio.h>
#include "libvga.h"

#include "timing.h"
#include "vgaregs.h"
#include "driver.h"             /* for __svgalib_driver_report */
#include "ramdac.h"

#include "IBMRGB52x.h"

#define IBMRGB52x_STATESIZE     0x101

static int IBMRGB52x_dacspeed = 170000;         /* assuming 170MHz DAC */
static int IBMRGB52x_fref = 16000;      /* assuming 16MHz refclock */
static int IBMRGB52x_clk = 2;   /* use clock 2 */

#ifdef INCLUDE_IBMRGB52x_DAC_TEST
/*
 * s3IBMRGB_Probe() from XFree86.
 *
 * returns 0x01xx for 525, 0x02xx for 524/528, where xx = revision.
 */

static int IBMRGB52x_probe(void)
{
    unsigned char CR43, CR55, dac[3], lut[6];
    unsigned char ilow, ihigh, id, rev, id2, rev2;
    int i, j;
    int ret = 0;

    port_out(0x43, 0x3D4);
    CR43 = port_in(0x3D5);
    port_out(CR43 & ~0x02, 0x3D5);

    port_out(0x55, 0x3D4);
    CR55 = port_in(0x3D5);
    port_out(CR55 & ~0x03, 0x3D5);

    /* save DAC and first LUT entries */
    for (i = 0; i < 3; i++)
        dac[i] = port_in(IBMRGB_PIXEL_MASK + i);
    for (i = j = 0; i < 2; i++) {
        port_out(i, IBMRGB_READ_ADDR);
        lut[j++] = port_in(IBMRGB_RAMDAC_DATA);
        lut[j++] = port_in(IBMRGB_RAMDAC_DATA);
        lut[j++] = port_in(IBMRGB_RAMDAC_DATA);
    }

    port_out(0x55, 0x3D4);
    port_out((CR55 & ~0x03) | 0x01, 0x3D5);     /* set RS2 */

    /* read ID and revision */
    ilow = port_in(IBMRGB_INDEX_LOW);
    ihigh = port_in(IBMRGB_INDEX_HIGH);
    port_out(0, IBMRGB_INDEX_HIGH);     /* index high */
    port_out(IBMRGB_rev, IBMRGB_INDEX_LOW);
    rev = port_in(IBMRGB_INDEX_DATA);
    port_out(IBMRGB_id, IBMRGB_INDEX_LOW);
    id = port_in(IBMRGB_INDEX_DATA);

    /* known IDs:  
       1 = RGB525
       2 = RGB524, RGB528
     */


    if (id >= 1 && id <= 2) {
        /* check if ID and revision are read only */
        port_out(IBMRGB_rev, IBMRGB_INDEX_LOW);
        port_out(~rev, IBMRGB_INDEX_DATA);
        port_out(IBMRGB_id, IBMRGB_INDEX_LOW);
        port_out(~id, IBMRGB_INDEX_DATA);
        port_out(IBMRGB_rev, IBMRGB_INDEX_LOW);
        rev2 = port_in(IBMRGB_INDEX_DATA);
        port_out(IBMRGB_id, IBMRGB_INDEX_LOW);
        id2 = port_in(IBMRGB_INDEX_DATA);

        if (id == id2 && rev == rev2) {         /* IBM RGB52x found */
            ret = (id << 8) | rev;
        } else {
            port_out(IBMRGB_rev, IBMRGB_INDEX_LOW);
            port_out(rev, IBMRGB_INDEX_DATA);
            port_out(IBMRGB_id, IBMRGB_INDEX_LOW);
            port_out(id, IBMRGB_INDEX_DATA);
        }
    }
    port_out(ilow, IBMRGB_INDEX_LOW);
    port_out(ihigh, IBMRGB_INDEX_HIGH);

    port_out(0x55, 0x3D4);
    port_out(CR55 & ~0x03, 0x3D5);      /* reset RS2 */

    /* restore DAC and first LUT entries */
    for (i = j = 0; i < 2; i++) {
        port_out(i, IBMRGB_WRITE_ADDR);
        port_out(lut[j++], IBMRGB_RAMDAC_DATA);
        port_out(lut[j++], IBMRGB_RAMDAC_DATA);
        port_out(lut[j++], IBMRGB_RAMDAC_DATA);
    }
    for (i = 0; i < 3; i++)
        port_out(dac[i], IBMRGB_PIXEL_MASK + i);

    port_out(0x43, 0x3D4);
    port_out(CR43, 0x3D5);
    port_out(0x55, 0x3D4);
    port_out(CR55, 0x3D5);

    return ret;
}
#else
#define IBMRGB52x_probe 0
#endif

#ifdef INCLUDE_IBMRGB52x_DAC
static void IBMRGBSetClock(long freq, int clk, long dacspeed, long fref,
                      int *best_m_out, int *best_n_out, int *best_df_out)
{
    volatile double ffreq, fdacspeed, ffref;
    volatile int df, n, m, max_n, min_df;
    volatile int best_m = 69, best_n = 17, best_df = 0;
    volatile double diff, mindiff;

#define FREQ_MIN   16250        /* 1000 * (0+65) / 4 */
#define FREQ_MAX  dacspeed

    if (freq < FREQ_MIN)
        ffreq = FREQ_MIN / 1000.0;
    else if (freq > FREQ_MAX)
        ffreq = FREQ_MAX / 1000.0;
    else
        ffreq = freq / 1000.0;

    fdacspeed = dacspeed / 1e3;
    ffref = fref / 1e3;

    ffreq /= ffref;
    ffreq *= 16;
    mindiff = ffreq;

    if (freq <= dacspeed / 4)
        min_df = 0;
    else if (freq <= dacspeed / 2)
        min_df = 1;
    else
        min_df = 2;

    for (df = 0; df < 4; df++) {
        ffreq /= 2;
        mindiff /= 2;
        if (df < min_df)
            continue;

        /* the remaining formula is  ffreq = (m+65) / n */

        if (df < 3)
            max_n = fref / 1000 / 2;
        else
            max_n = fref / 1000;
        if (max_n > 31)
            max_n = 31;

        for (n = 2; n <= max_n; n++) {
            m = (int) (ffreq * n + 0.5) - 65;
            if (m < 0)
                m = 0;
            else if (m > 63)
                m = 63;

            diff = (m + 65.0) / n - ffreq;
            if (diff < 0)
                diff = -diff;

            if (diff < mindiff) {
                mindiff = diff;
                best_n = n;
                best_m = m;
                best_df = df;
            }
        }
    }

#ifdef DEBUG
    cprintf("clk %d, setting to %f, m 0x%02x %d, n 0x%02x %d, df %d\n", clk,
           ((best_m + 65.0) / best_n) / (8 >> best_df) * ffref,
           best_m, best_m, best_n, best_n, best_df);
#endif
    *best_m_out = best_m;
    *best_n_out = best_n;
    *best_df_out = best_df;
}

static void IBMRGB52x_init(void)
{
    unsigned char tmp, CR55;
#ifdef INCLUDE_IBMRGB52x_DAC_TEST
    int idrev;

    idrev = IBMRGB52x_probe();
    if (__svgalib_driver_report)
        cprintf("svgalib: Using IBM RGB 52%d PaletteDAC, revision %d.\n",
               (idrev >> 8) == 1 ? 5 : 4,
               idrev & 0xff);
#else
    if (__svgalib_driver_report)
        cprintf("svgalib: Using IBM RGB 52x PaletteDAC.\n");
#endif
    /* set RS2 */
    port_out(0x55, 0x3D4);
    CR55 = port_in(0x3D5) & 0xFC;
    port_out(CR55 | 0x01, 0x3D5);

    tmp = port_in(IBMRGB_INDEX_CONTROL);
    port_out(tmp & ~0x01, IBMRGB_INDEX_CONTROL);        /* turn off auto-increment */
    port_out(0, IBMRGB_INDEX_HIGH);     /* reset index high */

    __svgalib_outCR(0x55, CR55);
}

static int IBMRGB52x_match_programmable_clock(int desiredclock)
{
    int m, n, df;

    IBMRGBSetClock(desiredclock, IBMRGB52x_clk, IBMRGB52x_dacspeed,
                   IBMRGB52x_fref, &m, &n, &df);

    return ((m + 65.0) / n) / (8 >> df) * IBMRGB52x_fref;
}

static void IBMRGB52x_initialize_clock_state(unsigned char *regs, int freq)
{
    int m, n, df;

    IBMRGBSetClock(freq, IBMRGB52x_clk, IBMRGB52x_dacspeed,
                   IBMRGB52x_fref, &m, &n, &df);

    if (__svgalib_driver_report)
        cprintf("clk %d, setting to %.3f, m 0x%02x %d, n 0x%02x %d, df %d\n",
               IBMRGB52x_clk, ((m + 65.0) / n) / (8 >> df) * IBMRGB52x_fref / 1000,
               m, m, n, n, df);

    regs[IBMRGB_misc_clock] |= 0x01;
    regs[IBMRGB_m0 + 2 * IBMRGB52x_clk] = (df << 6) | (m & 0x3f);
    regs[IBMRGB_n0 + 2 * IBMRGB52x_clk] = n;
    regs[IBMRGB_pll_ctrl2] &= 0xf0;
    regs[IBMRGB_pll_ctrl2] |= IBMRGB52x_clk;
    regs[IBMRGB_pll_ctrl1] &= 0xf8;
    regs[IBMRGB_pll_ctrl1] |= 0x03;
}

static void IBMRGB52x_savestate(unsigned char *regs)
{
    int i;
    unsigned char tmp;

    /* set RS2 */
    port_out(0x55, 0x3D4);
    tmp = port_in(0x3D5) & 0xFC;
    port_out(tmp | 0x01, 0x3D5);

    for (i = 0; i < 0x100; i++) {
        port_out(i, IBMRGB_INDEX_LOW);  /* high index is set to 0 */
        regs[i] = port_in(IBMRGB_INDEX_DATA);
    }
    regs[0x100] = __svgalib_inCR(0x22);

    __svgalib_outCR(0x55, tmp);
}

/* SL: not complete, need more work for 525. */
static void IBMRGB52x_restorestate(const unsigned char *regs)
{
    int i;
    unsigned char tmp;

    /* set RS2 */
    port_out(0x55, 0x3D4);
    tmp = port_in(0x3D5) & 0xFC;
    port_out(tmp | 0x01, 0x3D5);

    for (i = 0; i < 0x100; i++) {
        port_out(i, IBMRGB_INDEX_LOW);  /* high index is set to 0 */
        port_out(regs[i], IBMRGB_INDEX_DATA);
    }
    __svgalib_outCR(0x22, regs[0x100]);

    __svgalib_outCR(0x55, tmp);
}

static int IBMRGB52x_map_clock(int bpp, int pixelclock)
{
    return pixelclock;
}

static int IBMRGB52x_map_horizontal_crtc(int bpp, int pixelclock, int htiming)
{
#ifdef PIXEL_MULTIPLEXING
    switch (bpp) {
    case 4:
        break;
    case 8:
        return htiming / 2;
    case 16:
        break;
    case 24:
        return htiming * 3 / 2;
    case 32:
        return htiming * 2;
    }
#endif
    return htiming;
}

static void IBMRGB52x_initializestate(unsigned char *regs, int bpp, int colormode,
                                      int pixelclock)
{
    unsigned char tmp;

    regs[IBMRGB_misc_clock] = (regs[IBMRGB_misc_clock] & 0xf0) | 0x03;
    regs[IBMRGB_sync] = 0;
    regs[IBMRGB_hsync_pos] = 0;
    regs[IBMRGB_pwr_mgmt] = 0;
    regs[IBMRGB_dac_op] &= ~0x08;       /* no sync on green */
    regs[IBMRGB_dac_op] |= 0x02;        /* fast slew */
    regs[IBMRGB_pal_ctrl] = 0;
    regs[IBMRGB_misc1] &= ~0x43;
    regs[IBMRGB_misc1] |= 1;
#ifdef PIXEL_MULTIPLEXING
    if (bpp >= 8)
        regs[IBMRGB_misc2] = 0x43;      /* use SID bus? 0x47 for DAC_8_BIT */
#endif
    tmp = __svgalib_inCR(0x22);
    if (bpp <= 8)               /* and 968 */
        __svgalib_outCR(0x22, tmp | 0x08);
    else
        __svgalib_outCR(0x22, tmp & ~0x08);

    regs[IBMRGB_pix_fmt] &= ~0x07;
    switch (bpp) {
    case 4:
    case 8:
        regs[IBMRGB_pix_fmt] |= 0x03;
        regs[IBMRGB_8bpp] = 0x00;
        break;
    case 15:
        regs[IBMRGB_pix_fmt] |= 0x04;
        regs[IBMRGB_16bpp] = 0x02;
        break;
    case 16:
        regs[IBMRGB_pix_fmt] |= 0x04;
        regs[IBMRGB_16bpp] = 0x00;
        break;
    case 24:
        regs[IBMRGB_pix_fmt] |= 0x05;   /* SL: guess */
        regs[IBMRGB_24bpp] = 0x00;
        break;
    case 32:
        regs[IBMRGB_pix_fmt] |= 0x06;
        regs[IBMRGB_32bpp] = 0x00;
        break;
    }
    IBMRGB52x_initialize_clock_state(regs,
                                   IBMRGB52x_map_clock(bpp, pixelclock));
}

static void IBMRGB52x_qualify_cardspecs(CardSpecs * cardspecs, int dacspeed)
{
    IBMRGB52x_dacspeed = __svgalib_setDacSpeed(dacspeed, 170000);       /* 220 MHz version exist also */
    cardspecs->maxPixelClock4bpp = IBMRGB52x_dacspeed;
    cardspecs->maxPixelClock8bpp = IBMRGB52x_dacspeed;
#ifdef PIXEL_MULTIPLEXING
    cardspecs->maxPixelClock16bpp = IBMRGB52x_dacspeed;
    cardspecs->maxPixelClock24bpp = IBMRGB52x_dacspeed * 3 / 2;
    cardspecs->maxPixelClock32bpp = IBMRGB52x_dacspeed / 2;
#else
    cardspecs->maxPixelClock16bpp = 0;
    cardspecs->maxPixelClock24bpp = 0;
    cardspecs->maxPixelClock32bpp = 0;
#endif
    cardspecs->mapClock = IBMRGB52x_map_clock;
    cardspecs->matchProgrammableClock = IBMRGB52x_match_programmable_clock;
    cardspecs->mapHorizontalCrtc = IBMRGB52x_map_horizontal_crtc;
    cardspecs->flags |= CLOCK_PROGRAMMABLE;
}

DacMethods __svgalib_IBMRGB52x_methods =
{
    IBMRGB52x,
    "IBM RGB 52x PaletteDAC",
    DAC_HAS_PROGRAMMABLE_CLOCKS,
    IBMRGB52x_probe,
    IBMRGB52x_init,
    IBMRGB52x_qualify_cardspecs,
    IBMRGB52x_savestate,
    IBMRGB52x_restorestate,
    IBMRGB52x_initializestate,
    IBMRGB52x_STATESIZE
};
#endif