root/src/add-ons/accelerants/matrox/engine/mga_info.c
/* Read initialisation information from card */
/* some bits are hacks, where PINS is not known */
/* Authors:
   Mark Watson 2/2000,
   Rudolf Cornelissen 10/2002-5/2006
*/

#define MODULE_BIT 0x00002000

#include "mga_std.h"

/* Parse the BIOS PINS structure if there */
status_t parse_pins ()
{
        uint8 pins_len = 0;
        uint8 *rom;
        uint8 *pins;
        uint8 chksum = 0;
        int i;
        status_t result = B_ERROR;

        /* preset PINS read status to failed */
        si->ps.pins_status = B_ERROR;

        /* check the validity of PINS */
        LOG(2,("INFO: Reading PINS info\n"));
        rom = (uint8 *) si->rom_mirror;
        /* check BIOS signature */
        if (rom[0]!=0x55 || rom[1]!=0xaa)
        {
                LOG(8,("INFO: BIOS signature not found\n"));
                return B_ERROR;
        }
        LOG(2,("INFO: BIOS signature $AA55 found OK\n"));
        /* check for a valid PINS struct adress */
        pins = rom + (rom[0x7FFC]|(rom[0x7FFD]<<8));
        if ((pins - rom) > 0x7F80)
        {
                LOG(8,("INFO: invalid PINS adress\n"));
                return B_ERROR;
        }
        /* checkout new PINS struct version if there */
        if ((pins[0] == 0x2E) && (pins[1] == 0x41))
        {
                pins_len = pins[2];
                if (pins_len < 3 || pins_len > 128)
                {
                        LOG(8,("INFO: invalid PINS size\n"));
                        return B_ERROR;
                }
                
                /* calculate PINS checksum */
                for (i = 0; i < pins_len; i++)
                {
                        chksum += pins[i];
                }
                if (chksum)
                {
                        LOG(8,("INFO: PINS checksum error\n"));
                        return B_ERROR;
                }
                LOG(2,("INFO: new PINS, version %u.%u, length %u\n", pins[5], pins[4], pins[2]));
                /* fill out the si->ps struct if possible */
                switch (pins[5])
                {
                        case 2:
                                result = pins2_read(pins, pins_len);
                                break;
                        case 3:
                                result = pins3_read(pins, pins_len);
                                break;
                        case 4:
                                result = pins4_read(pins, pins_len);
                                break;
                        case 5:
                                result = pins5_read(pins, pins_len);
                                break;
                        default:
                                LOG(8,("INFO: unknown PINS version\n"));
                                return B_ERROR;
                                break;
                }
        }
        /* checkout old 64 byte PINS struct version if there */
        else if ((pins[0] == 0x40) && (pins[1] == 0x00))
        {
                pins_len = 0x40;
                /* this PINS version has no checksum */

                LOG(2,("INFO: old PINS found\n"));
                /* fill out the si->ps struct */
                result = pins1_read(pins, pins_len);
        }
        /* no valid PINS signature found */
        else
        {
                LOG(8,("INFO: no PINS signature found\n"));
                return B_ERROR;
        }
        /* check PINS read result */
        if (result == B_ERROR)
        {
                LOG(8,("INFO: PINS read/decode error\n"));
                return B_ERROR;
        }
        /* PINS scan succeeded */
        si->ps.pins_status = B_OK;
        LOG(2,("INFO: PINS scan completed succesfully\n"));
        return B_OK;
}

status_t pins1_read(uint8 *pins, uint8 length)
{
        if (length != 64)
        {
                LOG(8,("INFO: wrong PINS length, expected 64, got %d\n", length));
                return B_ERROR;
        }

//reset all for test:
//float:
        si->ps.f_ref = 0;
//uint32:
        si->ps.max_system_vco = 0;
        si->ps.min_system_vco = 0;
        si->ps.min_pixel_vco = 0;
        si->ps.min_video_vco = 0;
        si->ps.std_engine_clock_dh = 0;
        si->ps.max_dac1_clock_32 = 0;
        si->ps.max_dac1_clock_32dh = 0;
        si->ps.memory_size = 0;
        si->ps.mctlwtst_reg = 0;
        si->ps.memrdbk_reg = 0;
        si->ps.option2_reg = 0;
        si->ps.option3_reg = 0;
        si->ps.option4_reg = 0;
//uint8:
        si->ps.v3_option2_reg = 0;
        si->ps.v3_clk_div = 0;
        si->ps.v3_mem_type = 0;
//uint16:
        si->ps.v5_mem_type = 0;
//bools:
        si->ps.secondary_head = false;
        si->ps.tvout = false;
        si->ps.primary_dvi = false;
        si->ps.secondary_dvi = false;
        si->ps.sdram = true;

//experimental: checkout!
        si->ps.max_dac1_clock_32 = pins[22];//ramdac
        si->ps.max_pixel_vco = (pins[25] << 8) | pins[24];//PCLK
        si->ps.std_engine_clock = (pins[29] << 8) | pins[28];
        if ((uint32)((pins[31] << 8) | pins[30]) < si->ps.std_engine_clock)
                si->ps.std_engine_clock = (pins[31] << 8) | pins[30];
        if ((uint32)((pins[33] << 8) | pins[32]) < si->ps.std_engine_clock)
                si->ps.std_engine_clock = (pins[33] << 8) | pins[32];

//temp. test to see some vals..
        si->ps.max_video_vco = (pins[27] << 8) | pins[26];//LCLK
        //feature flags:
        si->ps.option_reg = (pins[53] << 24) | (pins[52] << 16) | (pins[51] << 8) | pins [50];

        si->ps.max_dac2_clock = (pins[35] << 8) | pins[34];//clkmod
        si->ps.max_dac2_clock_8 = (pins[37] << 8) | pins[36];//testclk
        si->ps.max_dac2_clock_16 = (pins[39] << 8) | pins[38];//vgafreq1
        si->ps.max_dac2_clock_24 = (pins[41] << 8) | pins[40];//vgafreq2
        si->ps.max_dac2_clock_32 = (pins[55] << 8) | pins[54];//vga clock
        si->ps.max_dac2_clock_32dh = pins[58];//vid ctrl

        si->ps.max_dac1_clock = (pins[29] << 8) | pins[28];//clkbase
        si->ps.max_dac1_clock_8 = (pins[31] << 8) | pins[30];//4mb
        si->ps.max_dac1_clock_16 = (pins[33] << 8) | pins[32];//8mb
        si->ps.max_dac1_clock_24 = pins[23];//ramdac type

//test! Don't actually use the reported settings for now...
        return B_OK;
}

status_t pins2_read(uint8 *pins, uint8 length)
{
        if (length != 64)
        {
                LOG(8,("INFO: wrong PINS length, expected 64, got %d\n", length));
                return B_ERROR;
        }

        LOG(2,("INFO: PINS version 2 details not yet known\n"));
        return B_ERROR;
}

/* pins v3 is used by G100 and G200. */
status_t pins3_read(uint8 *pins, uint8 length)
{
        /* used to calculate RAM refreshrate */
        float mclk_period;
        uint32 rfhcnt;

        if (length != 64)
        {
                LOG(8,("INFO: wrong PINS length, expected 64, got %d\n", length));
                return B_ERROR;
        }

        /* fill out the shared info si->ps struct */
        si->ps.max_pixel_vco = pins[36] + 100;

        si->ps.max_dac1_clock_8 = pins[37] + 100;
        si->ps.max_dac1_clock_16 = pins[38] + 100;
        si->ps.max_dac1_clock_24 = pins[39] + 100;
        si->ps.max_dac1_clock_32 = pins[40] + 100;

        si->ps.std_engine_clock = pins[44];
        if (pins [45] < si->ps.std_engine_clock) si->ps.std_engine_clock = pins[45];
        if (pins [46] < si->ps.std_engine_clock) si->ps.std_engine_clock = pins[46];
        if (pins [47] < si->ps.std_engine_clock) si->ps.std_engine_clock = pins[47];
        if ((si->ps.card_type == G200) && (pins[58] & 0x04))
        {
                /* G200 can work without divisor */
                si->ps.std_engine_clock *= 1;
        }
        else
        {
                if (pins[52] & 0x01)
                        si->ps.std_engine_clock *= 3;
                else
                        si->ps.std_engine_clock *= 2;
        }

        if (pins[52] & 0x20) si->ps.f_ref = 14.31818;
        else si->ps.f_ref = 27.00000;

        /* G100 and G200 support 2-16Mb RAM */
        si->ps.memory_size = 2 << ((pins[55] & 0xc0) >> 6);
        /* more memory specifics */
        si->ps.mctlwtst_reg = (pins[51] << 24) | (pins[50] << 16) | (pins[49] << 8) | pins [48];
        si->ps.memrdbk_reg = 
                (pins[56] & 0x0f) | ((pins[56] & 0xf0) << 1) | ((pins[57] & 0x03) << 22) | ((pins[57] & 0xf0) << 21);
        /* Mark did this as one step in the above stuff, which must be wrong:
        ((pins[p3_memrd+1]&0x03)>>2)<<16; //FIXME - ROR */

        si->ps.v3_clk_div = pins[52];
        si->ps.v3_mem_type = pins[54];
        si->ps.v3_option2_reg = pins[58];

        /* for cards using this version of PINS both functions are in maven */
        si->ps.tvout = !(pins[59] & 0x01);
        /* beware of TVout add-on boards: test for the MAVEN ourself! */
        if (i2c_maven_probe() == B_OK)
        {
                si->ps.tvout = true;
        }

        /* G200 and earlier cards are always singlehead cards */
        si->ps.secondary_head = false;
        if (si->ps.card_type >= G400) si->ps.secondary_head = !(pins[59] & 0x01);

        /* setup via gathered info from pins */
        si->ps.option_reg = 0;
        /* calculate refresh timer info-bits for 15uS interval (or shorter). See G100/G200 specs */
        /* calculate std memory clock period (nS) */
        if ((si->ps.card_type == G200) && (pins[58] & 0x08))
        {
                /* G200 can work without Mclk divisor */
                mclk_period = 1000.0 / si->ps.std_engine_clock;
        }
        else
        {
                if (pins[52] & 0x02)
                        /* this factor is only used on G200, not on G100 */
                        mclk_period = 3000.0 / si->ps.std_engine_clock;
                else
                        mclk_period = 2000.0 / si->ps.std_engine_clock;
        }
        /* calculate needed setting, 'round-down' result! */
        rfhcnt = (uint32)(((15000 / mclk_period) - 1) / 64);
        /* check for register limit */
        if (rfhcnt > 0x3f) rfhcnt = 0x3f;
        /* add to option register */
        si->ps.option_reg |= (rfhcnt << 15);
        /* the rest of the OPTION info for pins v3 comes via 'v3_clk_div' and 'v3_mem_type'. */

        /* assuming the only possible panellink will be on the first head */
        si->ps.primary_dvi = !(pins[59] & 0x40);
        /* logical consequence of the above */
        si->ps.secondary_dvi = false;

        /* indirect logical consequences, see also G100 and G200 specs */
        si->ps.max_system_vco = si->ps.max_pixel_vco;
        si->ps.max_dac1_clock = si->ps.max_dac1_clock_8;
        si->ps.max_dac1_clock_32dh = si->ps.max_dac1_clock_32;
        si->ps.std_engine_clock_dh = si->ps.std_engine_clock;
        si->ps.sdram = (si->ps.v3_clk_div & 0x10);
        /* not supported: */
        si->ps.max_dac2_clock_8 = 0;
        si->ps.max_dac2_clock_24 = 0;
        /* see G100, G200 and G400 specs */
        si->ps.min_system_vco = 50;
        si->ps.min_pixel_vco = 50;
        /* fixme: ehhh, no specs: confirm/tune these by testing?! */
        si->ps.max_video_vco = si->ps.max_pixel_vco;
        si->ps.min_video_vco = 50;
        /* assuming G100, G200 MAVEN has same specs as G400 MAVEN */
        si->ps.max_dac2_clock = 136;
        si->ps.max_dac2_clock_16 = 136;
        si->ps.max_dac2_clock_32dh = 136;
        si->ps.max_dac2_clock_32 = 136;

        /* not used here: */
        si->ps.option2_reg = 0;
        si->ps.option3_reg = 0;
        si->ps.option4_reg = 0;
        si->ps.v5_mem_type = 0;
        return B_OK;
}

/* pins v4 is used by G400 and G400MAX */
status_t pins4_read(uint8 *pins, uint8 length)
{
        /* used to calculate RAM refreshrate */
        float mclk_period;
        uint32 rfhcnt;

        if (length != 128)
        {
                LOG(8,("INFO: wrong PINS length, expected 128, got %d\n", length));
                return B_ERROR;
        }

        /* fill out the shared info si->ps struct */
        if (pins[39] == 0xff) si->ps.max_pixel_vco = 230;
        else si->ps.max_pixel_vco = 4 * pins[39];

        if (pins[38] == 0xff) si->ps.max_system_vco = si->ps.max_pixel_vco;
        else si->ps.max_system_vco = 4 * pins[38];

        if (pins[40] == 0xff) si->ps.max_dac1_clock_8 = si->ps.max_pixel_vco;
        else si->ps.max_dac1_clock_8 = 4 * pins[40];

        if (pins[41] == 0xff) si->ps.max_dac1_clock_16 = si->ps.max_dac1_clock_8;
        else si->ps.max_dac1_clock_16 = 4 * pins[41];

        if (pins[42] == 0xff) si->ps.max_dac1_clock_24 = si->ps.max_dac1_clock_16;
        else si->ps.max_dac1_clock_24 = 4 * pins[42];

        if (pins[43] == 0xff) si->ps.max_dac1_clock_32 = si->ps.max_dac1_clock_24;
        else si->ps.max_dac1_clock_32 = 4 * pins[43];

        if (pins[44] == 0xff) si->ps.max_dac2_clock_16 = si->ps.max_pixel_vco;
        else si->ps.max_dac2_clock_16 = 4 * pins[44];

        if (pins[45] == 0xff) si->ps.max_dac2_clock_32 = si->ps.max_dac2_clock_16;
        else si->ps.max_dac2_clock_32 = 4 * pins[45];

        /* verified against windows driver: */
        si->ps.std_engine_clock = 2 * pins[65];
        
        if (pins[92] & 0x01) si->ps.f_ref = 14.31818;
        else si->ps.f_ref = 27.00000;

        si->ps.memory_size = 4 << ((pins[92] >> 2) & 0x03);
        /* more memory specifics */
        si->ps.mctlwtst_reg = (pins[74] << 24) | (pins[73] << 16) | (pins[72] << 8) | pins [71];
        si->ps.option3_reg = (pins[70] << 24) | (pins[69] << 16) | (pins[68] << 8) | pins [67];
        /* mrsopcod field, msb is always zero.. */
        si->ps.memrdbk_reg = 
                (pins[86] & 0x0f) | ((pins[86] & 0xf0) << 1) | ((pins[87] & 0x03) << 22) | ((pins[87] & 0xf0) << 21);
        si->ps.sdram = (pins[92] & 0x10);

        /* setup via gathered info from pins */
        si->ps.option_reg = ((pins[53] & 0x38) << 7) | ((pins[53] & 0x40) << 22) | ((pins[53] & 0x80) << 15);
        /* calculate refresh timer info-bits for 15uS interval (or shorter). See G400 specs;
         * the 15uS value was confirmed by Mark Watson for both G400 and G400MAX */
        /* calculate std memory clock period (nS) */
        switch ((si->ps.option3_reg & 0x0000e000) >> 13)
        {
        case 0:
                mclk_period = 3000.0 / (si->ps.std_engine_clock * 1);
                break;
        case 1:
                mclk_period = 5000.0 / (si->ps.std_engine_clock * 2);
                break;
        case 2:
                mclk_period = 9000.0 / (si->ps.std_engine_clock * 4);
                break;
        case 3:
                mclk_period = 2000.0 / (si->ps.std_engine_clock * 1);
                break;
        case 4:
                mclk_period = 3000.0 / (si->ps.std_engine_clock * 2);
                break;
        case 5:
                mclk_period = 1000.0 / (si->ps.std_engine_clock * 1);
                break;
        default:
                /* we choose the lowest refreshcount that could be needed (so assuming slowest clocked memory) */
                mclk_period = 3000.0 / (si->ps.std_engine_clock * 1);
                LOG(8,("INFO: undefined/unknown memory clock divider select, using failsafe for refresh\n"));
                break;  
        }
        /* calculate needed setting, 'round-down' result! */
        rfhcnt = (uint32)(((15000 / mclk_period) - 1) / 64);
        /* check for register limit */
        if (rfhcnt > 0x3f) rfhcnt = 0x3f;
        /* add to option register */
        si->ps.option_reg |= (rfhcnt << 15);

        /* for cards using this version of PINS both functions are in maven */
        si->ps.tvout = !(pins[91] & 0x01);
        /* beware of TVout add-on boards: test for the MAVEN ourself! */
        if (i2c_maven_probe() == B_OK)
        {
                si->ps.tvout = true;
        }

        /* G200 and earlier cards are always singlehead cards */
        si->ps.secondary_head = false;
        if (si->ps.card_type >= G400) si->ps.secondary_head = !(pins[91] & 0x01);

        /* assuming the only possible panellink will be on the first head */
        si->ps.primary_dvi = !(pins[91] & 0x40);
        /* logical consequence of the above */
        si->ps.secondary_dvi = false;

        /* indirect logical consequences, see also G100, G200 and G400 specs */
        si->ps.max_dac1_clock = si->ps.max_dac1_clock_8;
        si->ps.max_dac2_clock = si->ps.max_dac2_clock_16;
        si->ps.max_dac1_clock_32dh = si->ps.max_dac1_clock_32;
        si->ps.max_dac2_clock_32dh = si->ps.max_dac2_clock_32;
        si->ps.std_engine_clock_dh = si->ps.std_engine_clock;
        /* not supported: */
        si->ps.max_dac2_clock_8 = 0;
        si->ps.max_dac2_clock_24 = 0;
        /* see G100, G200 and G400 specs */
        si->ps.min_system_vco = 50;
        si->ps.min_pixel_vco = 50;
        /* fixme: ehhh, no specs: confirm/tune these by testing?! */
        si->ps.max_video_vco = si->ps.max_pixel_vco;
        si->ps.min_video_vco = 50;

        /* not used here: */
        si->ps.option2_reg = 0;
        si->ps.option4_reg = 0;
        si->ps.v3_option2_reg = 0;
        si->ps.v3_clk_div = 0;
        si->ps.v3_mem_type = 0;
        si->ps.v5_mem_type = 0;

        /* check for a G400MAX card */
        /* fixme: use the PCI configspace ID method if it exists... */
        if (si->ps.max_dac1_clock > 300)
        {
                si->ps.card_type = G400MAX;
                LOG(2,("INFO: G400MAX detected\n"));
        }
        return B_OK;
}

/* pins v5 is used by G450 and G550 */
status_t pins5_read(uint8 *pins, uint8 length)
{
        unsigned int m_factor = 6;

        if (length != 128)
        {
                LOG(8,("INFO: wrong PINS length, expected 128, got %d\n", length));
                return B_ERROR;
        }

        /* fill out the shared info si->ps struct */
        if (pins[4] == 0x01) m_factor = 8;
        if (pins[4] >= 0x02) m_factor = 10;

        si->ps.max_system_vco = m_factor * pins[36];
        si->ps.max_video_vco = m_factor * pins[37];
        si->ps.max_pixel_vco = m_factor * pins[38];
        si->ps.min_system_vco = m_factor * pins[121];
        si->ps.min_video_vco = m_factor * pins[122];
        si->ps.min_pixel_vco = m_factor * pins[123];

        if (pins[39] == 0xff) si->ps.max_dac1_clock_8 = si->ps.max_pixel_vco;
        else si->ps.max_dac1_clock_8 = 4 * pins[39];

        if (pins[40] == 0xff) si->ps.max_dac1_clock_16 = si->ps.max_dac1_clock_8;
        else si->ps.max_dac1_clock_16 = 4 * pins[40];

        if (pins[41] == 0xff) si->ps.max_dac1_clock_24 = si->ps.max_dac1_clock_16;
        else si->ps.max_dac1_clock_24 = 4 * pins[41];

        if (pins[42] == 0xff) si->ps.max_dac1_clock_32 = si->ps.max_dac1_clock_24;
        else si->ps.max_dac1_clock_32 = 4 * pins[42];

        if (pins[124] == 0xff) si->ps.max_dac1_clock_32dh = si->ps.max_dac1_clock_32;
        else si->ps.max_dac1_clock_32dh = 4 * pins[124];

        if (pins[43] == 0xff) si->ps.max_dac2_clock_16 = si->ps.max_video_vco;
        else si->ps.max_dac2_clock_16 = 4 * pins[43];

        if (pins[44] == 0xff) si->ps.max_dac2_clock_32 = si->ps.max_dac2_clock_16;
        else si->ps.max_dac2_clock_32 = 4 * pins[44];

        if (pins[125] == 0xff) si->ps.max_dac2_clock_32dh = si->ps.max_dac2_clock_32;
        else si->ps.max_dac2_clock_32dh = 4 * pins[125];

        if (pins[118] == 0xff) si->ps.max_dac1_clock = si->ps.max_dac1_clock_8;
        else si->ps.max_dac1_clock = 4 * pins[118];

        if (pins[119] == 0xff) si->ps.max_dac2_clock = si->ps.max_dac1_clock;
        else si->ps.max_dac2_clock = 4 * pins[119];

        si->ps.std_engine_clock = 4 * pins[74];
        si->ps.std_engine_clock_dh = 4 * pins[92];

        si->ps.memory_size = ((pins[114] & 0x03) + 1) * 8;
        if ((pins[114] & 0x07) > 3)
        {
                LOG(8,("INFO: unknown RAM size, defaulting to 8Mb\n"));
                si->ps.memory_size = 8;
        }

        if (pins[110] & 0x01) si->ps.f_ref = 14.31818;
        else si->ps.f_ref = 27.00000;

        /* make sure SGRAM functions only get enabled if SGRAM mounted */
        if ((pins[114] & 0x18) == 0x08) si->ps.sdram = false;
        else si->ps.sdram = true;
        /* more memory specifics */
        si->ps.v5_mem_type = (pins[115] << 8) | pins [114];

        /* various registers */
        si->ps.option_reg = (pins[51] << 24) | (pins[50] << 16) | (pins[49] << 8) | pins [48];
        si->ps.option2_reg = (pins[55] << 24) | (pins[54] << 16) | (pins[53] << 8) | pins [52];
        si->ps.option3_reg = (pins[79] << 24) | (pins[78] << 16) | (pins[77] << 8) | pins [76];
        si->ps.option4_reg = (pins[87] << 24) | (pins[86] << 16) | (pins[85] << 8) | pins [84];
        si->ps.mctlwtst_reg = (pins[83] << 24) | (pins[82] << 16) | (pins[81] << 8) | pins [80];
        si->ps.memrdbk_reg = (pins[91] << 24) | (pins[90] << 16) | (pins[89] << 8) | pins [88];

        /* both the secondary head and MAVEN are on die, (so) no add-on boards exist */
        si->ps.secondary_head = (pins[117] & 0x70);
        si->ps.tvout = (pins[117] & 0x40);

        si->ps.primary_dvi = (pins[117] & 0x02);
        si->ps.secondary_dvi = (pins[117] & 0x20);

        /* not supported: */
        si->ps.max_dac2_clock_8 = 0;
        si->ps.max_dac2_clock_24 = 0;

        /* not used here: */
        si->ps.v3_option2_reg = 0;
        si->ps.v3_clk_div = 0;
        si->ps.v3_mem_type = 0;
        return B_OK;
}

/* fake_pins presumes the card was coldstarted by it's BIOS */
void fake_pins(void)
{
        LOG(8,("INFO: faking PINS\n"));

        switch (si->ps.card_type)
        {
                case MIL1:
                        pinsmil1_fake();
                        break;
                case MIL2:
                        pinsmil2_fake();
                        break;
                case G100:
                        pinsg100_fake();
                        break;
                case G200:
                        pinsg200_fake();
                        break;
                case G400:
                        pinsg400_fake();
                        break;
                case G400MAX:
                        pinsg400max_fake();
                        break;
                case G450:
                        pinsg450_fake();
                        break;
                case G550:
                        pinsg550_fake();
                        break;
        }

        /* find out if the card has a maven */
        si->ps.tvout = false;
        si->ps.secondary_head = false;
        /* only do I2C probe if the card has a chance */
        if (si->ps.card_type >= G100)
        {
                if (i2c_maven_probe() == B_OK)
                {
                        si->ps.tvout = true;
                        /* G200 and earlier cards are always singlehead cards */
                        if (si->ps.card_type >= G400) si->ps.secondary_head = true;
                }
        }

        /* not used because no coldstart will be attempted */
        si->ps.std_engine_clock = 0;
        si->ps.std_engine_clock_dh = 0;
        si->ps.mctlwtst_reg = 0;
        si->ps.memrdbk_reg = 0;
        si->ps.option_reg = 0;
        si->ps.option2_reg = 0;
        si->ps.option3_reg = 0;
        si->ps.option4_reg = 0;
        si->ps.v3_option2_reg = 0;
        si->ps.v3_clk_div = 0;
        si->ps.v3_mem_type = 0;
        si->ps.v5_mem_type = 0;
}

void pinsmil1_fake(void)
{
        /* 'worst case' scenario defaults, overrule-able via mga.settings if needed */

        si->ps.f_ref = 14.31818;
        /* see MIL1 specs */
        si->ps.max_system_vco = 220;
        si->ps.min_system_vco = 110;
        si->ps.max_pixel_vco = 220;
        si->ps.min_pixel_vco = 110;
        /* no specs, assuming these */
        si->ps.max_video_vco = 0;
        si->ps.min_video_vco = 0;
        /* see MIL1 specs */
        si->ps.max_dac1_clock = 220;
        si->ps.max_dac1_clock_8 = 220;
        si->ps.max_dac1_clock_16 = 200;
        /* 'failsave' values */
        si->ps.max_dac1_clock_24 = 180;
        si->ps.max_dac1_clock_32 = 136;
        si->ps.max_dac1_clock_32dh = 0;
        /* see specs */
        si->ps.max_dac2_clock = 0;
        si->ps.max_dac2_clock_8 = 0;
        si->ps.max_dac2_clock_16 = 0;
        si->ps.max_dac2_clock_24 = 0;
        si->ps.max_dac2_clock_32 = 0;
        /* 'failsave' value */
        si->ps.max_dac2_clock_32dh = 0;
        si->ps.primary_dvi = false;
        si->ps.secondary_dvi = false;
        /*  presume 2Mb RAM mounted */
        //fixme: see if we can get this from OPTION or so...
        si->ps.memory_size = 2;
        //fixme: should be overrule-able via mga.settings for MIL1.
        //fail-safe mode for now:
        si->ps.sdram = true;
}

void pinsmil2_fake(void)
{
        /* 'worst case' scenario defaults, overrule-able via mga.settings if needed */

        si->ps.f_ref = 14.31818;
        /* see MIL2 specs */
        si->ps.max_system_vco = 220;
        si->ps.min_system_vco = 110;
        si->ps.max_pixel_vco = 220;
        si->ps.min_pixel_vco = 110;
        /* no specs, assuming these */
        si->ps.max_video_vco = 0;
        si->ps.min_video_vco = 0;
        /* see MIL2 specs */
        si->ps.max_dac1_clock = 220;
        si->ps.max_dac1_clock_8 = 220;
        si->ps.max_dac1_clock_16 = 200;
        /* 'failsave' values */
        si->ps.max_dac1_clock_24 = 180;
        si->ps.max_dac1_clock_32 = 136;
        si->ps.max_dac1_clock_32dh = 0;
        /* see specs */
        si->ps.max_dac2_clock = 0;
        si->ps.max_dac2_clock_8 = 0;
        si->ps.max_dac2_clock_16 = 0;
        si->ps.max_dac2_clock_24 = 0;
        si->ps.max_dac2_clock_32 = 0;
        /* 'failsave' value */
        si->ps.max_dac2_clock_32dh = 0;
        si->ps.primary_dvi = false;
        si->ps.secondary_dvi = false;
        /*  presume 4Mb RAM mounted */
        //fixme: see if we can get this from OPTION or so...
        si->ps.memory_size = 4;
        //fixme: should be overrule-able via mga.settings for MIL2.
        //fail-safe mode for now:
        si->ps.sdram = true;
}

void pinsg100_fake(void)
{
        /* 'worst case' scenario defaults, overrule-able via mga.settings if needed */

        //fixme: should be overrule-able via mga.settings.
        si->ps.f_ref = 27.000;
        /* see G100 specs */
        si->ps.max_system_vco = 230;
        si->ps.min_system_vco = 50;
        si->ps.max_pixel_vco = 230;
        si->ps.min_pixel_vco = 50;
        /* no specs, assuming these */
        si->ps.max_video_vco = 230;
        si->ps.min_video_vco = 50;
        /* see G100 specs */
        si->ps.max_dac1_clock = 230;
        si->ps.max_dac1_clock_8 = 230;
        si->ps.max_dac1_clock_16 = 230;
        /* 'failsave' values */
        si->ps.max_dac1_clock_24 = 180;
        si->ps.max_dac1_clock_32 = 136;
        si->ps.max_dac1_clock_32dh = 136;
        /* see specs */
        si->ps.max_dac2_clock = 136;
        si->ps.max_dac2_clock_8 = 0;
        si->ps.max_dac2_clock_16 = 136;
        si->ps.max_dac2_clock_24 = 0;
        si->ps.max_dac2_clock_32 = 136;
        /* 'failsave' value */
        si->ps.max_dac2_clock_32dh = 136;
        /* assuming the only possible panellink will be on the first head */
        //fixme: primary_dvi should be overrule-able via mga.settings for G100.
        si->ps.primary_dvi = false;
        si->ps.secondary_dvi = false;
        /*  presume 2Mb RAM mounted */
        si->ps.memory_size = 2;
        //fixme: should be overrule-able via mga.settings for G100.
        //fail-safe mode for now:
        si->ps.sdram = true;
}

void pinsg200_fake(void)
{
        /* 'worst case' scenario defaults, overrule-able via mga.settings if needed */

        //fixme: should be overrule-able via mga.settings.
        si->ps.f_ref = 27.000;
        /* see G200 specs */
        si->ps.max_system_vco = 250;
        si->ps.min_system_vco = 50;
        si->ps.max_pixel_vco = 250;
        si->ps.min_pixel_vco = 50;
        /* no specs, assuming these */
        si->ps.max_video_vco = 250;
        si->ps.min_video_vco = 50;
        /* see G200 specs */
        si->ps.max_dac1_clock = 250;
        si->ps.max_dac1_clock_8 = 250;
        si->ps.max_dac1_clock_16 = 250;
        /* 'failsave' values */
        si->ps.max_dac1_clock_24 = 180;
        si->ps.max_dac1_clock_32 = 136;
        si->ps.max_dac1_clock_32dh = 136;
        /* see specs */
        si->ps.max_dac2_clock = 136;
        si->ps.max_dac2_clock_8 = 0;
        si->ps.max_dac2_clock_16 = 136;
        si->ps.max_dac2_clock_24 = 0;
        si->ps.max_dac2_clock_32 = 136;
        /* 'failsave' value */
        si->ps.max_dac2_clock_32dh = 136;
        /* assuming the only possible panellink will be on the first head */
        //fixme: primary_dvi should be overrule-able via mga.settings for G100.
        si->ps.primary_dvi = false;
        si->ps.secondary_dvi = false;
        /*  presume 2Mb RAM mounted */
        si->ps.memory_size = 2;
        /* ask the G200 what type of RAM it has been set to by it's BIOS */
        si->ps.sdram = !(CFGR(OPTION) & 0x00004000);
}

void pinsg400_fake(void)
{
        /* 'worst case' scenario defaults, overrule-able via mga.settings if needed */

        //fixme: should be overrule-able via mga.settings.
        si->ps.f_ref = 27.000;
        /* see G400 specs */
        si->ps.max_system_vco = 300;
        si->ps.min_system_vco = 50;
        si->ps.max_pixel_vco = 300;
        si->ps.min_pixel_vco = 50;
        /* no specs, assuming these */
        si->ps.max_video_vco = 300;
        si->ps.min_video_vco = 50;
        /* see G400 specs */
        si->ps.max_dac1_clock = 300;
        si->ps.max_dac1_clock_8 = 300;
        si->ps.max_dac1_clock_16 = 300;
        /* 'failsave' values */
        si->ps.max_dac1_clock_24 = 230;
        si->ps.max_dac1_clock_32 = 180;
        si->ps.max_dac1_clock_32dh = 136;
        /* see specs */
        si->ps.max_dac2_clock = 136;
        si->ps.max_dac2_clock_8 = 0;
        si->ps.max_dac2_clock_16 = 136;
        si->ps.max_dac2_clock_24 = 0;
        si->ps.max_dac2_clock_32 = 136;
        /* 'failsave' value */
        si->ps.max_dac2_clock_32dh = 136;
        /* assuming the only possible panellink will be on the first head */
        //fixme: primary_dvi should be overrule-able via mga.settings for G400.
        si->ps.primary_dvi = false;
        si->ps.secondary_dvi = false;
        /*  presume 4Mb RAM mounted */
        si->ps.memory_size = 4;
        /* ask the G400 what type of RAM it has been set to by it's BIOS */
        si->ps.sdram = !(CFGR(OPTION) & 0x00004000);
}

/* this routine is currently unused, because G400MAX is detected via pins! */
void pinsg400max_fake(void)
{
        /* 'worst case' scenario defaults, overrule-able via mga.settings if needed */

        //fixme: should be overrule-able via mga.settings.
        si->ps.f_ref = 27.000;
        /* see G400MAX specs */
        si->ps.max_system_vco = 360;
        si->ps.min_system_vco = 50;
        si->ps.max_pixel_vco = 360;
        si->ps.min_pixel_vco = 50;
        /* no specs, assuming these */
        si->ps.max_video_vco = 360;
        si->ps.min_video_vco = 50;
        /* see G400MAX specs */
        si->ps.max_dac1_clock = 360;
        si->ps.max_dac1_clock_8 = 360;
        si->ps.max_dac1_clock_16 = 360;
        /* 'failsave' values */
        si->ps.max_dac1_clock_24 = 280;
        si->ps.max_dac1_clock_32 = 230;
        si->ps.max_dac1_clock_32dh = 136;
        /* see specs */
        si->ps.max_dac2_clock = 136;
        si->ps.max_dac2_clock_8 = 0;
        si->ps.max_dac2_clock_16 = 136;
        si->ps.max_dac2_clock_24 = 0;
        si->ps.max_dac2_clock_32 = 136;
        /* 'failsave' value */
        si->ps.max_dac2_clock_32dh = 136;
        /* assuming the only possible panellink will be on the first head */
        //fixme: primary_dvi should be overrule-able via mga.settings for G400MAX.
        si->ps.primary_dvi = false;
        si->ps.secondary_dvi = false;
        /*  presume 4Mb RAM mounted */
        si->ps.memory_size = 4;
        /* ask the G400MAX what type of RAM it has been set to by it's BIOS */
        si->ps.sdram = !(CFGR(OPTION) & 0x00004000);
}

void pinsg450_fake(void)
{
        /* 'worst case' scenario defaults, overrule-able via mga.settings if needed */

        //fixme: should be overrule-able via mga.settings.
        si->ps.f_ref = 27.000;
        /* see G450 pins readouts for max ranges, then use a bit smaller ones */
        /* carefull not to take to high lower limits, and high should be >= 2x low. */
        si->ps.max_system_vco = 640;
        si->ps.min_system_vco = 320;
        si->ps.max_pixel_vco = 640;
        si->ps.min_pixel_vco = 320;
        si->ps.max_video_vco = 640;
        si->ps.min_video_vco = 320;
        si->ps.max_dac1_clock = 360;
        si->ps.max_dac1_clock_8 = 360;
        si->ps.max_dac1_clock_16 = 360;
        /* 'failsave' values */
        si->ps.max_dac1_clock_24 = 280;
        si->ps.max_dac1_clock_32 = 230;
        si->ps.max_dac1_clock_32dh = 180;
        /* see G450 pins readouts */
        si->ps.max_dac2_clock = 232;
        si->ps.max_dac2_clock_8 = 0;
        si->ps.max_dac2_clock_16 = 232;
        si->ps.max_dac2_clock_24 = 0;
        si->ps.max_dac2_clock_32 = 232;
        /* 'failsave' values */
        si->ps.max_dac2_clock_32dh = 180;
        //fixme: primary & secondary_dvi should be overrule-able via mga.settings for G450.
        si->ps.primary_dvi = false;
        si->ps.secondary_dvi = false;
        /*  presume 8Mb RAM mounted */
        si->ps.memory_size = 8;
        /* ask the G450 what type of RAM it has been set to by it's BIOS */
//todo:
//      si->ps.sdram = !(CFGR(OPTION) & 0x00004000);
//fail-safe mode for now:
        si->ps.sdram = true;
}

void pinsg550_fake(void)
{
        /* 'worst case' scenario defaults, overrule-able via mga.settings if needed */

        //fixme: should be overrule-able via mga.settings.
        si->ps.f_ref = 27.000;
        /* see G550 pins readouts for max ranges, then use a bit smaller ones */
        /* carefull not to take to high lower limits, and high should be >= 2x low. */
        si->ps.max_system_vco = 768;
        si->ps.min_system_vco = 384;
        si->ps.max_pixel_vco = 960;
        si->ps.min_pixel_vco = 320;
        si->ps.max_video_vco = 960;
        si->ps.min_video_vco = 320;
        si->ps.max_dac1_clock = 360;
        si->ps.max_dac1_clock_8 = 360;
        si->ps.max_dac1_clock_16 = 360;
        /* 'failsave' values */
        si->ps.max_dac1_clock_24 = 280;
        si->ps.max_dac1_clock_32 = 230;
        si->ps.max_dac1_clock_32dh = 180;
        /* see G550 pins readouts */
        si->ps.max_dac2_clock = 232;
        si->ps.max_dac2_clock_8 = 0;
        si->ps.max_dac2_clock_16 = 232;
        si->ps.max_dac2_clock_24 = 0;
        si->ps.max_dac2_clock_32 = 232;
        /* 'failsave' values */
        si->ps.max_dac2_clock_32dh = 180;
        //fixme: primary & secondary_dvi should be overrule-able via mga.settings for G550.
        si->ps.primary_dvi = false;
        si->ps.secondary_dvi = false;
        /*  presume 8Mb RAM mounted */
        si->ps.memory_size = 8;
        /* ask the G550 what type of RAM it has been set to by it's BIOS */
//todo:
//      si->ps.sdram = !(CFGR(OPTION) & 0x00004000);
//fail-safe mode for now:
        si->ps.sdram = true;
}

void dump_pins(void)
{
        LOG(2,("INFO: pinsdump follows:\n"));
        LOG(2,("f_ref: %fMhz\n", si->ps.f_ref));
        LOG(2,("max_system_vco: %dMhz\n", si->ps.max_system_vco));
        LOG(2,("min_system_vco: %dMhz\n", si->ps.min_system_vco));
        LOG(2,("max_pixel_vco: %dMhz\n", si->ps.max_pixel_vco));
        LOG(2,("min_pixel_vco: %dMhz\n", si->ps.min_pixel_vco));
        LOG(2,("max_video_vco: %dMhz\n", si->ps.max_video_vco));
        LOG(2,("min_video_vco: %dMhz\n", si->ps.min_video_vco));
        LOG(2,("std_engine_clock: %dMhz\n", si->ps.std_engine_clock));
        LOG(2,("std_engine_clock_dh: %dMhz\n", si->ps.std_engine_clock_dh));
        LOG(2,("max_dac1_clock: %dMhz\n", si->ps.max_dac1_clock));
        LOG(2,("max_dac1_clock_8: %dMhz\n", si->ps.max_dac1_clock_8));
        LOG(2,("max_dac1_clock_16: %dMhz\n", si->ps.max_dac1_clock_16));
        LOG(2,("max_dac1_clock_24: %dMhz\n", si->ps.max_dac1_clock_24));
        LOG(2,("max_dac1_clock_32: %dMhz\n", si->ps.max_dac1_clock_32));
        LOG(2,("max_dac1_clock_32dh: %dMhz\n", si->ps.max_dac1_clock_32dh));
        LOG(2,("max_dac2_clock: %dMhz\n", si->ps.max_dac2_clock));
        LOG(2,("max_dac2_clock_8: %dMhz\n", si->ps.max_dac2_clock_8));
        LOG(2,("max_dac2_clock_16: %dMhz\n", si->ps.max_dac2_clock_16));
        LOG(2,("max_dac2_clock_24: %dMhz\n", si->ps.max_dac2_clock_24));
        LOG(2,("max_dac2_clock_32: %dMhz\n", si->ps.max_dac2_clock_32));
        LOG(2,("max_dac2_clock_32dh: %dMhz\n", si->ps.max_dac2_clock_32dh));
        LOG(2,("secondary_head: "));
        if (si->ps.secondary_head) LOG(2,("present\n")); else LOG(2,("absent\n"));
        LOG(2,("tvout: "));
        if (si->ps.tvout) LOG(2,("present\n")); else LOG(2,("absent\n"));
        //fixme: probably only valid for pre-G400 cards...(?)
        if ((si->ps.tvout) && (si->ps.card_type < G450))
        {
                if (si->ps.card_type < G400)
                        LOG(2,("MGA_TVO version: "));
                else
                        LOG(2,("MAVEN version: "));
                if ((MAVR(VERSION)) < 20)
                        LOG(2,("rev. B\n"));
                else
                        LOG(2,("rev. C\n"));
        }
        LOG(2,("primary_dvi: "));
        if (si->ps.primary_dvi) LOG(2,("present\n")); else LOG(2,("absent\n"));
        LOG(2,("secondary_dvi: "));
        if (si->ps.secondary_dvi) LOG(2,("present\n")); else LOG(2,("absent\n"));
        LOG(2,("card memory_size: %dMb\n", si->ps.memory_size));
        LOG(2,("mctlwtst register: $%08x\n", si->ps.mctlwtst_reg));
        LOG(2,("memrdbk register: $%08x\n", si->ps.memrdbk_reg));
        LOG(2,("option register: $%08x\n", si->ps.option_reg));
        LOG(2,("option2 register: $%08x\n", si->ps.option2_reg));
        LOG(2,("option3 register: $%08x\n", si->ps.option3_reg));
        LOG(2,("option4 register: $%08x\n", si->ps.option4_reg));
        LOG(2,("v3_option2_reg: $%02x\n", si->ps.v3_option2_reg));
        LOG(2,("v3_clock_div: $%02x\n", si->ps.v3_clk_div));
        LOG(2,("v3_mem_type: $%02x\n", si->ps.v3_mem_type));
        LOG(2,("v5_mem_type: $%04x\n", si->ps.v5_mem_type));
        LOG(2,("sdram: "));
        if (si->ps.sdram) LOG(2,("SDRAM card\n")); else LOG(2,("SGRAM card\n"));
        LOG(2,("INFO: end pinsdump.\n"));
}