Subversion Repositories shark

Rev

Blame | Last modification | View Log | RSS feed

/*
 * ics_gendac.c:
 *
 * This works only with ARK, since it has ARK specific code.
 */


#include <stdio.h>
#include "libvga.h"
#include "timing.h"
#include "vgaregs.h"
#include "driver.h"             /* for __svgalib_driver_report */
#include "ramdac.h"

/* SDAC/GENDAC registers */
#define SDAC_COMMAND            0
#define GENDAC_COMMAND          0
#define SDAC_PLL_WRITEINDEX     1
#define SDAC_PLL_READINDEX      2
#define SDAC_PLL_M              3       /* f2 programmed clock */
#define SDAC_PLL_N1_N2          4
#define SDAC_PLL_CONTROL        5

#define GENDAC_STATESIZE 6

static void GENDAC_init(void)
{
}


/*
 * From XFree86 common_hw/S3gendac.c and S3gendac.h.
 *
 * Progaming of the S3 gendac programable clocks, from the S3 Gendac
 * programing documentation by S3 Inc.
 * Jon Tombs <jon@esix2.us.es>
 *
 * Returns nonzero if success, 0 if failure.
 */

#define BASE_FREQ            14.31818   /* MHz */

#define DEBUG_FINDCLOCK 0

static int S3dacsFindClock(int freq_in, int min_n2, int freq_min, int freq_max,
                     int *best_m_out, int *best_n1_out, int *best_n2_out)
{
    double ffreq_in, ffreq_min, ffreq_max;
    double ffreq_out, diff, best_diff;
    unsigned int m;
    unsigned char n1, n2;
    unsigned char best_n1 = 16 + 2, best_n2 = 2, best_m = 125 + 2;

#if DEBUG_FINDCLOCK
    fprintf(stderr,"S3dacsFindClock: Trying to match clock of %0.3f MHz\n", freq_in / 1000.0);
#endif
   
    ffreq_in = freq_in / 1000.0 / BASE_FREQ;
    ffreq_min = freq_min / 1000.0 / BASE_FREQ;
    ffreq_max = freq_max / 1000.0 / BASE_FREQ;

    /* Check if getting freq_in is possible at all */
    if (freq_in < freq_min / 8) {
#if DEBUG_FINDCLOCK
        fprintf(stderr,"S3dacsFindClock: %0.3f MHz is too low (lowest is %0.3f MHz)\n",
               freq_in / 1000.0, freq_min / 1000.0 / 8);
#endif
        return 0;
    }  
    if (freq_in > freq_max / (1 << min_n2)) {
#if DEBUG_FINDCLOCK
        fprintf(stderr,"S3dacsFindClock: %0.3f MHz is too high (highest is %0.3f MHz)\n",
               freq_in / 1000.0, freq_max / 1000.0 / (1 << min_n2));
#endif
        return 0;
    }

    /* work out suitable timings */
    best_diff = ffreq_in;
    for (n2 = min_n2; n2 <= 3; n2++) {
        for (n1 = 1 + 2; n1 <= 31 + 2; n1++) {
            m = (int) (ffreq_in * n1 * (1 << n2) + 0.5);
            if (m < 1 + 2 || m > 127 + 2)
                continue;
            ffreq_out = (double) (m) / (double) (n1);
            if ((ffreq_out >= ffreq_min) && (ffreq_out <= ffreq_max)) {
                diff = ffreq_in - ffreq_out / (1 << n2);
                if (diff < 0.0)
                    diff = -diff;
                if (diff < best_diff) {
                    best_diff = diff;
                    best_m = m;
                    best_n1 = n1;
                    best_n2 = n2;
                }
            }
        }
    }

#if DEBUG_FINDCLOCK
    fprintf(stderr,"S3dacsFindClock: clock wanted %1.6f MHz, found %1.6f MHz (m %d, n1 %d, n2 %d)\n",
           freq_in / 1000.0,
           best_m / ((double) best_n1 * (1 << best_n2)) * BASE_FREQ,
           best_m, best_n1, best_n2);
#endif
   
    *best_m_out = best_m;
    *best_n1_out = best_n1;
    *best_n2_out = best_n2;
   
    return 1;
}

static int GENDAC_match_programmable_clock(int desiredclock)
{
    int min_m, min_n1, n2;
   
    /* Note: For ICS5342, min_n2 parameter should be one. */
    if (!S3dacsFindClock(desiredclock, 0, 100000, 250000, &min_m, &min_n1, &n2))
        return 0;

    return ((float) (min_m) / (float) (min_n1) / (1 << n2)) * BASE_FREQ * 1000;
}

static void GENDAC_initialize_clock_state(unsigned char *regs, int freq)
{
    int min_m, min_n1, n2;
    int n, m;

    if (!S3dacsFindClock(freq, 0, 100000, 250000, &min_m, &min_n1, &n2)) {
        fprintf(stderr,"Bad dot clock %0.3f MHz.\n", freq / 1000.0);
        return;
    }
   
    n = (min_n1 - 2) | (n2 << 5);
    m = min_m - 2;
    regs[SDAC_PLL_M] = m;
    regs[SDAC_PLL_N1_N2] = n;
#if 0
    if (__svgalib_driver_report)
        fprintf(stderr,"Initializing DAC PLL values; 0x%02X, 0x%02X.\n", m, n);
#endif
}

static void GENDAC_savestate(unsigned char *regs)
{
    unsigned char tmp;
    tmp = __svgalib_inSR(0x1c);
    __svgalib_outSR(0x1c, tmp | 0x80);

    regs[SDAC_COMMAND] = inb(0x3c6);
    regs[SDAC_PLL_WRITEINDEX] = inb(0x3c8);     /* PLL write index */
    regs[SDAC_PLL_READINDEX] = inb(0x3c7);      /* PLL read index */
    outb(0x3c7, 2);             /* index to f2 reg */
    regs[SDAC_PLL_M] = inb(0x3c9);      /* f2 PLL M divider */
    regs[SDAC_PLL_N1_N2] = inb(0x3c9);  /* f2 PLL N1/N2 divider */
    outb(0x3c7, 0x0e);          /* index to PLL control */
    regs[SDAC_PLL_CONTROL] = inb(0x3c9);        /* PLL control */

    __svgalib_outSR(0x1c, tmp & ~0x80);
}

static void GENDAC_restorestate(const unsigned char *regs)
{
    unsigned char tmp;

    tmp = __svgalib_inseq(0x1c);
    __svgalib_outseq(0x1c, tmp | 0x80);

    outb(0x3c6, regs[SDAC_COMMAND]);
    outb(0x3c8, 2);             /* index to f2 reg */
    outb(0x3c9, regs[SDAC_PLL_M]);      /* f2 PLL M divider */
    outb(0x3c9, regs[SDAC_PLL_N1_N2]);  /* f2 PLL N1/N2 divider */
    outb(0x3c8, 0x0e);          /* index to PLL control */
    outb(0x3c9, regs[SDAC_PLL_CONTROL]);        /* PLL control */
    outb(0x3c8, regs[SDAC_PLL_WRITEINDEX]);     /* PLL write index */
    outb(0x3c7, regs[SDAC_PLL_READINDEX]);      /* PLL read index */

    __svgalib_outseq(0x1c, tmp);
}

/*
 * SDAC: 16-bit DAC, 110 MHz raw clock limit.
 *
 * The 135 MHz version supports pixel multiplexing in 8bpp modes with a
 * halved raw clock. (SL: at least mine doesn't.)
 */


static int GENDAC_probe(void)
{
    int i;
    inb(0x3c6);
    inb(0x3c6);
    inb(0x3c6);
    i=inb(0x3c6);
    if(i==177) return 1;
    return 0;
}

static int GENDAC_map_clock(int bpp, int pixelclock)
{
    if (bpp == 16)
        return pixelclock * 2;
    if (bpp == 24)
        return pixelclock * 3;
    if (bpp == 32)
        return pixelclock * 4;
    return pixelclock;
}

static int GENDAC_map_horizontal_crtc(int bpp, int pixelclock, int htiming)
{
    /* XXXX Not sure. */
    if (bpp == 24)
        return htiming * 3;
    if (bpp == 16)
        return htiming * 2;
    return htiming;
}

static void GENDAC_initializestate(unsigned char *regs, int bpp, int colormode,
                                   int pixelclock)
{
    int daccomm;                /* DAC command register. */
    daccomm = 0;
    if (colormode == RGB16_555)
        daccomm = 0x20;
    else if (colormode == RGB16_565)
        daccomm = 0x60;
    else if (colormode == RGB24_888_B)
        daccomm = 0x40;
    regs[GENDAC_COMMAND] = daccomm;
    GENDAC_initialize_clock_state(regs,
                                       GENDAC_map_clock(bpp, pixelclock));
}

static void GENDAC_qualify_cardspecs(CardSpecs * cardspecs, int dacspeed)
{
    dacspeed = __svgalib_setDacSpeed(dacspeed, 110000);
    cardspecs->maxPixelClock4bpp = dacspeed;
    cardspecs->maxPixelClock8bpp = dacspeed;
    cardspecs->maxPixelClock16bpp = dacspeed / 2;
    cardspecs->maxPixelClock24bpp = dacspeed / 3;
    cardspecs->maxPixelClock32bpp = 0;
    cardspecs->mapClock = GENDAC_map_clock;
    cardspecs->matchProgrammableClock = GENDAC_match_programmable_clock;
    cardspecs->mapHorizontalCrtc = GENDAC_map_horizontal_crtc;
    cardspecs->flags |= CLOCK_PROGRAMMABLE;
}

DacMethods __svgalib_ICS_GENDAC_methods =
{
    S3_GENDAC,
    "ICS-GENDAC (5342)",
    DAC_HAS_PROGRAMMABLE_CLOCKS,
    GENDAC_probe,
    GENDAC_init,
    GENDAC_qualify_cardspecs,
    GENDAC_savestate,
    GENDAC_restorestate,
    GENDAC_initializestate,
    GENDAC_STATESIZE
};