root/src/add-ons/accelerants/matrox/engine/mga_general.c
/* Authors:
   Mark Watson 12/1999,
   Apsed,
   Rudolf Cornelissen 10/2002-10/2009
*/

#define MODULE_BIT 0x00008000

#include "mga_std.h"

static status_t test_ram(void);
static status_t mil_general_powerup (void);
static status_t g100_general_powerup (void);
static status_t g200_general_powerup (void);
static status_t g400_general_powerup (void);
static status_t g450_general_powerup (void);
static status_t gx00_general_bios_to_powergraphics(void);

static void mga_dump_configuration_space (void)
{
#define DUMP_CFG(reg, type) if (si->ps.card_type >= type) do { \
        uint32 value = CFGR(reg); \
        MSG(("configuration_space 0x%02x %20s 0x%08x\n", \
                MGACFG_##reg, #reg, value)); \
} while (0)
        DUMP_CFG (DEVID,     0);
        DUMP_CFG (DEVCTRL,   0);
        DUMP_CFG (CLASS,     0);
        DUMP_CFG (HEADER,    0);
        DUMP_CFG (MGABASE2,  0);
        DUMP_CFG (MGABASE1,  0);
        DUMP_CFG (MGABASE3,  MYST);
        DUMP_CFG (SUBSYSIDR, MYST);
        DUMP_CFG (ROMBASE,   0);
        DUMP_CFG (CAP_PTR,   MIL2);
        DUMP_CFG (INTCTRL,   0);
        DUMP_CFG (OPTION,    0);
        DUMP_CFG (MGA_INDEX, 0);
        DUMP_CFG (MGA_DATA,  0);
        DUMP_CFG (SUBSYSIDW, MYST);
        DUMP_CFG (OPTION2,   G100);
        DUMP_CFG (OPTION3,   G400);
        DUMP_CFG (OPTION4,   G400);
        DUMP_CFG (PM_IDENT,  G100);
        DUMP_CFG (PM_CSR,    G100);
        DUMP_CFG (AGP_IDENT, MIL2);
        DUMP_CFG (AGP_STS,   MIL2);
        DUMP_CFG (AGP_CMD,   MIL2);
#undef DUMP_CFG
}

status_t gx00_general_powerup()
{
        status_t status;
        uint8 card_class;

        LOG(1,("POWERUP: Haiku Matrox Accelerant 0.33 running.\n"));

        /* log VBLANK INT usability status */
        if (si->ps.int_assigned)
                LOG(4,("POWERUP: Usable INT assigned to HW; Vblank semaphore enabled\n"));
        else
                LOG(4,("POWERUP: No (usable) INT assigned to HW; Vblank semaphore disabled\n"));

        /* WARNING:
         * _adi.name_ and _adi.chipset_ can contain 31 readable characters max.!!! */

        /* detect card type and power it up */
        switch(CFGR(DEVID))
        {
        case 0x051a102b: //MGA-1064 Mystique PCI
                sprintf(si->adi.name, "Matrox Mystique PCI");
                sprintf(si->adi.chipset, "MGA-1064");
                LOG(8,("POWERUP: Unimplemented Matrox device %08x\n",CFGR(DEVID)));
                return B_ERROR;
        case 0x0519102b: //MGA-2064 Millenium PCI
                si->ps.card_type = MIL1;
                sprintf(si->adi.name, "Matrox Millennium I");
                sprintf(si->adi.chipset, "MGA-2064");
                status = mil_general_powerup();
                break;
        case 0x051b102b:case 0x051f102b: //MGA-2164 Millenium II PCI/AGP
                si->ps.card_type = MIL2;
                sprintf(si->adi.name, "Matrox Millennium II");
                sprintf(si->adi.chipset, "MGA-2164");
                status = mil_general_powerup();
                break;
        case 0x1000102b:case 0x1001102b: //G100
                si->ps.card_type = G100;
                sprintf(si->adi.name, "Matrox MGA G100");
                sprintf(si->adi.chipset, "G100");
                status = g100_general_powerup();
                break;
        case 0x0520102b:case 0x0521102b: //G200
                si->ps.card_type = G200;
                sprintf(si->adi.name, "Matrox MGA G200");
                sprintf(si->adi.chipset, "G200");
                status = g200_general_powerup();
                break;
        case 0x0525102b: //G400, G400MAX or G450
                /* get classinfo to distinguish different types */
                card_class = CFGR(CLASS) & 0xff;
                if (card_class & 0x80)
                {
                        /* G450 */
                        si->ps.card_type = G450;
                        sprintf(si->adi.name, "Matrox MGA G450");
                        sprintf(si->adi.chipset, "G450 revision %x", (card_class & 0x7f));
                        LOG(4, ("50 revision %x\n", card_class & 0x7f));
                        status = g450_general_powerup();
                }
                else
                {
                        /* standard G400, G400MAX */
                        /* the only difference is the max RAMDAC speed, accounted for via pins. */
                        si->ps.card_type = G400;
                        sprintf(si->adi.name, "Matrox MGA G400");
                        sprintf(si->adi.chipset, "G400 revision %x", (card_class & 0x7f));
                        status = g400_general_powerup();
                }
                break;
        case 0x2527102b://G550 patch from Jean-Michel Batto
                si->ps.card_type = G450;
                sprintf(si->adi.name, "Matrox MGA G550");
                sprintf(si->adi.chipset, "G550");
                status = g450_general_powerup();
                break;  
        default:
                LOG(8,("POWERUP: Failed to detect valid card 0x%08x\n",CFGR(DEVID)));
                return B_ERROR;
        }

        /* override memory detection if requested by user */
        if (si->settings.memory != 0)
                si->ps.memory_size = si->settings.memory;

        return status;
}

static status_t test_ram()
{
        uint32 value, offset;
        status_t result = B_OK;

        /* make sure we don't corrupt the hardware cursor by using fbc.frame_buffer. */
        if (si->fbc.frame_buffer == NULL)
        {
                LOG(8,("INIT: test_ram detected NULL pointer.\n"));
                return B_ERROR;
        }

        for (offset = 0, value = 0x55aa55aa; offset < 256; offset++)
        {
                /* write testpattern to cardRAM */
                ((vuint32 *)si->fbc.frame_buffer)[offset] = value;
                /* toggle testpattern */
                value = 0xffffffff - value;
        }

        for (offset = 0, value = 0x55aa55aa; offset < 256; offset++)
        {
                /* readback and verify testpattern from cardRAM */
                if (((vuint32 *)si->fbc.frame_buffer)[offset] != value) result = B_ERROR;
                /* toggle testpattern */
                value = 0xffffffff - value;
        }
        return result;
}

/* NOTE:
 * This routine *has* to be done *after* SetDispplayMode has been executed,
 * or test results will not be representative!
 * (CAS latency is dependant on MGA setup on some (DRAM) boards) */
status_t mga_set_cas_latency()
{
        status_t result = B_ERROR;
        uint8 latency = 0;

        /* check current RAM access to see if we need to change anything */
        if (test_ram() == B_OK)
        {
                LOG(4,("INIT: RAM access OK.\n"));
                return B_OK;
        }

        /* check if we read PINS at starttime so we have valid registersettings at our disposal */
        if (si->ps.pins_status != B_OK)
        {
                LOG(4,("INIT: RAM access errors; not fixable: PINS was not read from cardBIOS.\n"));
                return B_ERROR;
        }

        /* OK. We might have a problem, try to fix it now.. */
        LOG(4,("INIT: RAM access errors; tuning CAS latency if prudent...\n"));

        switch(si->ps.card_type)
        {
        case G100:
                        if (!si->ps.sdram)
                        {
                                LOG(4,("INIT: G100 SGRAM CAS tuning not permitted, aborting.\n"));
                                return B_OK;
                        }
                        /* SDRAM card */
                        for (latency = 4; latency >= 2; latency-- )
                        {
                                /* MCTLWTST is a write-only register! */
                                ACCW(MCTLWTST, ((si->ps.mctlwtst_reg & 0xfffffffc) | (latency - 2)));
                                result = test_ram();
                                if (result == B_OK) break;
                        }
                        break;
        case G200:
                        /* fixme: implement this */
                        LOG(4,("INIT: G200 RAM CAS tuning not implemented, aborting.\n"));
                        return B_OK;
                        break;
        case G400:
        case G400MAX:
                        /* fixme: implement this if needed */
                        LOG(4,("INIT: G400/G400MAX RAM CAS tuning not implemented, aborting.\n"));
                        return B_OK;
                        break;
        case G450:
        case G550:
                        /* fixme: implement this if needed */
                        LOG(4,("INIT: G450/G550 RAM CAS tuning not implemented, aborting.\n"));
                        return B_OK;
                        break;
        default:
                        /* fixme: Millenium2 and others if needed */
                        LOG(4,("INIT: RAM CAS tuning not implemented for this card, aborting.\n"));
                        return B_OK;
                        break;
        }
        if (result == B_OK)
                LOG(4,("INIT: RAM access OK. CAS latency set to %d cycles.\n", latency));
        else
                LOG(4,("INIT: RAM access not fixable. CAS latency set to %d cycles.\n", latency));

        return result;
}

static 
status_t mil_general_powerup()
{
        status_t result;

        LOG(4, ("INIT: Millenium I/II powerup\n"));
        LOG(4, ("INIT: Detected %s (%s)\n", si->adi.name, si->adi.chipset));
        if (si->settings.logmask & 0x80000000) mga_dump_configuration_space();
        
        /* initialize the shared_info PINS struct */
        result = parse_pins();
        if (result != B_OK) fake_pins();

        /* log the PINS struct settings */
        dump_pins();

//remove this:
        fake_pins();
        LOG(2, ("INIT: Using faked PINS for now:\n"));
        dump_pins();
//end remove this.

        /* if the user doesn't want a coldstart OR the BIOS pins info could not be found warmstart */
//restore this line:
//      if (si->settings.usebios || (result != B_OK)) return gx00_general_bios_to_powergraphics();

        //set to powergraphics etc.
        LOG(2, ("INIT: Skipping card coldstart!\n"));
        mil2_dac_init();

//ok:
        /* disable overscan, select 0 IRE, select straight-through sync signals from CRTC */
        DXIW (GENCTRL, (DXIR (GENCTRL) & 0x0c));
        /* fixme: checkout if we need this sync inverting stuff: already done via CRTC!?!       
                | (vsync_pos?  0x00:0x02) 
                | (hsync_pos?  0x00:0x01)); */

        /* 8-bit DAC, enable DAC */
        DXIW(MISCCTRL, 0x0c);
//

        VGAW_I(SEQ,1,0x00);
        /*enable screen*/

        return B_OK;
}

static 
status_t g100_general_powerup()
{
        status_t result;
        
        LOG(4, ("INIT: G100 powerup\n"));
        LOG(4, ("INIT: Detected %s (%s)\n", si->adi.name, si->adi.chipset));
        if (si->settings.logmask & 0x80000000) mga_dump_configuration_space();
        
        /* initialize the shared_info PINS struct */
        result = parse_pins();
        if (result != B_OK) fake_pins();

        /* log the PINS struct settings */
        dump_pins();

        /* if the user doesn't want a coldstart OR the BIOS pins info could not be found warmstart */
        if (si->settings.usebios || (result != B_OK)) return gx00_general_bios_to_powergraphics();

        /*power up the PLLs,LUT,DAC*/
        LOG(2,("INIT: PLL/LUT/DAC powerup\n"));
        /* turn off both displays and the hardcursor (also disables transfers) */
        gx00_crtc_dpms(false, false, false);
        /* on singlehead cards with TVout program the MAVEN as well */
        if (si->ps.tvout) gx00_maven_dpms(false, false, false);
        gx00_crtc_cursor_hide();
        /* G100 SGRAM and SDRAM use external pix and dac refs, do *not* activate internals!
         * (this would create electrical shortcuts,
         * resulting in extra chip heat and distortions visible on screen */
        /* set voltage reference - using DAC reference block partly */
        DXIW(VREFCTRL,0x03);
        /* wait for 100ms for voltage reference to stabilize */
        delay(100000);
        /* power up the SYSPLL */
        CFGW(OPTION,CFGR(OPTION)|0x20);
        /* power up the PIXPLL */
        DXIW(PIXCLKCTRL,0x08);

        /* disable pixelclock oscillations before switching on CLUT */
        DXIW(PIXCLKCTRL, (DXIR(PIXCLKCTRL) | 0x04));
        /* disable 15bit mode CLUT-overlay function */
        DXIW(GENCTRL, DXIR(GENCTRL & 0xfd));
        /* CRTC2->MAFC, 8-bit DAC, CLUT enabled, enable DAC */
        DXIW(MISCCTRL,0x1b);
        snooze(250);
        /* re-enable pixelclock oscillations */
        DXIW(PIXCLKCTRL, (DXIR(PIXCLKCTRL) & 0xfb));

        /* setup i2c bus */
        i2c_init();

        /*make sure card is in powergraphics mode*/
        VGAW_I(CRTCEXT,3,0x80);      

        /*set the system clocks to powergraphics speed*/
        LOG(2,("INIT: Setting system PLL to powergraphics speeds\n"));
        g100_dac_set_sys_pll();

        /* 'official' RAM initialisation */
        LOG(2,("INIT: RAM init\n"));
        /* disable plane write mask (needed for SDRAM): actual change needed to get it sent to RAM */
        ACCW(PLNWT,0x00000000);
        ACCW(PLNWT,0xffffffff);
        /* program memory control waitstates */
        ACCW(MCTLWTST,si->ps.mctlwtst_reg);
        /* set memory configuration including:
         * - no split framebuffer.
         * - Mark says b14 (G200) should be done also though not defined for G100 in spec,
         * - b3 v3_mem_type was included by Mark for memconfig setup: but looks like not defined */
        CFGW(OPTION,(CFGR(OPTION)&0xFFFF8FFF) | ((si->ps.v3_mem_type & 0x04) << 10));
        /* set memory buffer type:
         * - Mark says: if((v3_mem_type & 0x03) == 0x03) then do not or-in bits in option2;
         *   but looks like v3_mem_type b1 is not defined,
         * - Mark also says: place v3_mem_type b1 in option2 bit13 (if not 0x03) but b13 = reserved. */
        CFGW(OPTION2,(CFGR(OPTION2)&0xFFFFCFFF)|((si->ps.v3_mem_type & 0x01) << 12));
        /* set RAM read tap delay */
        CFGW(OPTION2,(CFGR(OPTION2)&0xFFFFFFF0) | ((si->ps.v3_mem_type & 0xf0) >> 4));
        /* wait 200uS minimum */
        snooze(250);

        /* reset memory (MACCESS is a write only register!) */
        ACCW(MACCESS, 0x00000000);
        /* select JEDEC reset method */
        ACCW(MACCESS, 0x00004000);
        /* perform actual RAM reset */
        ACCW(MACCESS, 0x0000c000);
        snooze(250);
        /* start memory refresh */
        CFGW(OPTION,(CFGR(OPTION)&0xffe07fff) | (si->ps.option_reg & 0x001f8000));
        /* set memory control waitstate again AFTER the RAM reset */
        ACCW(MCTLWTST,si->ps.mctlwtst_reg);
        /* end 'official' RAM initialisation. */

        /* Bus parameters: enable retries, use advanced read */
        CFGW(OPTION,(CFGR(OPTION)|(1<<22)|(0<<29)));

        /*enable writing to crtc registers*/
        VGAW_I(CRTC,0x11,0);

        return B_OK;
}

static 
status_t g200_general_powerup()
{
        status_t result;
        
        LOG(4, ("INIT: G200 powerup\n"));
        LOG(4, ("INIT: Detected %s (%s)\n", si->adi.name, si->adi.chipset));
        if (si->settings.logmask & 0x80000000) mga_dump_configuration_space();
        
        /* initialize the shared_info PINS struct */
        result = parse_pins();
        if (result != B_OK) fake_pins();

        /* log the PINS struct settings */
        dump_pins();

        /* if the user doesn't want a coldstart OR the BIOS pins info could not be found warmstart */
        if (si->settings.usebios || (result != B_OK)) return gx00_general_bios_to_powergraphics();

        /*power up the PLLs,LUT,DAC*/
        LOG(2,("INIT: PLL/LUT/DAC powerup\n"));
        /* turn off both displays and the hardcursor (also disables transfers) */
        gx00_crtc_dpms(false, false, false);
        /* on singlehead cards with TVout program the MAVEN as well */
        if (si->ps.tvout) gx00_maven_dpms(false, false, false);
        gx00_crtc_cursor_hide();
        /* G200 SGRAM and SDRAM use external pix and dac refs, do *not* activate internals!
         * (this would create electrical shortcuts,
         * resulting in extra chip heat and distortions visible on screen */
        /* set voltage reference - using DAC reference block partly */
        DXIW(VREFCTRL,0x03);
        /* wait for 100ms for voltage reference to stabilize */
        delay(100000);
        /* power up the SYSPLL */
        CFGW(OPTION,CFGR(OPTION)|0x20);
        /* power up the PIXPLL */
        DXIW(PIXCLKCTRL,0x08);

        /* disable pixelclock oscillations before switching on CLUT */
        DXIW(PIXCLKCTRL, (DXIR(PIXCLKCTRL) | 0x04));
        /* disable 15bit mode CLUT-overlay function */
        DXIW(GENCTRL, DXIR(GENCTRL & 0xfd));
        /* CRTC2->MAFC, 8-bit DAC, CLUT enabled, enable DAC */
        DXIW(MISCCTRL,0x1b);
        snooze(250);
        /* re-enable pixelclock oscillations */
        DXIW(PIXCLKCTRL, (DXIR(PIXCLKCTRL) & 0xfb));

        /* setup i2c bus */
        i2c_init();

        /*make sure card is in powergraphics mode*/
        VGAW_I(CRTCEXT,3,0x80);      

        /*set the system clocks to powergraphics speed*/
        LOG(2,("INIT: Setting system PLL to powergraphics speeds\n"));
        g200_dac_set_sys_pll();

        /* 'official' RAM initialisation */
        LOG(2,("INIT: RAM init\n"));
        /* disable hardware plane write mask if SDRAM card */
        if (si->ps.sdram) CFGW(OPTION,(CFGR(OPTION) & 0xffffbfff));
        /* disable plane write mask (needed for SDRAM): actual change needed to get it sent to RAM */
        ACCW(PLNWT,0x00000000);
        ACCW(PLNWT,0xffffffff);
        /* program memory control waitstates */
        ACCW(MCTLWTST,si->ps.mctlwtst_reg);
        /* set memory configuration including:
         * - SDRAM / SGRAM special functions select. */
        CFGW(OPTION,(CFGR(OPTION)&0xFFFF83FF) | ((si->ps.v3_mem_type & 0x07) << 10));
        if (!si->ps.sdram) CFGW(OPTION,(CFGR(OPTION) | (0x01 << 14)));
        /* set memory buffer type */
        CFGW(OPTION2,(CFGR(OPTION2)&0xFFFFCFFF)|((si->ps.v3_option2_reg & 0x03) << 12));
        /* set mode register opcode and streamer flow control */        
        ACCW(MEMRDBK,(ACCR(MEMRDBK)&0x0000FFFF)|(si->ps.memrdbk_reg & 0xffff0000));
        /* set RAM read tap delays */
        ACCW(MEMRDBK,(ACCR(MEMRDBK)&0xFFFF0000)|(si->ps.memrdbk_reg & 0x0000ffff));
        /* wait 200uS minimum */
        snooze(250);

        /* reset memory (MACCESS is a write only register!) */
        ACCW(MACCESS, 0x00000000);
        /* perform actual RAM reset */
        ACCW(MACCESS, 0x00008000);
        snooze(250);
        /* start memory refresh */
        CFGW(OPTION,(CFGR(OPTION)&0xffe07fff) | (si->ps.option_reg & 0x001f8000));
        /* set memory control waitstate again AFTER the RAM reset */
        ACCW(MCTLWTST,si->ps.mctlwtst_reg);
        /* end 'official' RAM initialisation. */

        /* Bus parameters: enable retries, use advanced read */
        CFGW(OPTION,(CFGR(OPTION)|(1<<22)|(0<<29)));

        /*enable writing to crtc registers*/
        VGAW_I(CRTC,0x11,0);

        return B_OK;
}

static 
status_t g400_general_powerup()
{
        status_t result;
        
        LOG(4, ("INIT: G400/G400MAX powerup\n"));
        LOG(4, ("INIT: Detected %s (%s)\n", si->adi.name, si->adi.chipset));
        if (si->settings.logmask & 0x80000000) mga_dump_configuration_space();
        
        /* initialize the shared_info PINS struct */
        result = parse_pins();
        if (result != B_OK) fake_pins();

        /* log the PINS struct settings */
        dump_pins();

        /* if the user doesn't want a coldstart OR the BIOS pins info could not be found warmstart */
        if (si->settings.usebios || (result != B_OK)) return gx00_general_bios_to_powergraphics();

        /* reset MAVEN so we know the sync polarity is at reset situation (Hpos, Vpos) */
        if (si->ps.tvout)
        {
                ACCW(RST, 0x00000002);
                snooze(1000);
                ACCW(RST, 0x00000000);
        }

        /*power up the PLLs,LUT,DAC*/
        LOG(4,("INIT: PLL/LUT/DAC powerup\n"));
        /* turn off both displays and the hardcursor (also disables transfers) */
        gx00_crtc_dpms(false, false, false);
        g400_crtc2_dpms(false, false, false);
        gx00_crtc_cursor_hide();

        /* set voltage reference - not using DAC reference block */
        DXIW(VREFCTRL,0x00);
        /* wait for 100ms for voltage reference to stabilize */
        delay(100000);
        /* power up the SYSPLL */
        CFGW(OPTION,CFGR(OPTION)|0x20);
        /* power up the PIXPLL */
        DXIW(PIXCLKCTRL,0x08);

        /* disable pixelclock oscillations before switching on CLUT */
        DXIW(PIXCLKCTRL, (DXIR(PIXCLKCTRL) | 0x04));
        /* disable 15bit mode CLUT-overlay function */
        DXIW(GENCTRL, DXIR(GENCTRL & 0xfd));
        /* CRTC2->MAFC, 8-bit DAC, CLUT enabled, enable DAC */
        DXIW(MISCCTRL,0x9b);
        snooze(250);
        /* re-enable pixelclock oscillations */
        DXIW(PIXCLKCTRL, (DXIR(PIXCLKCTRL) & 0xfb));

        DXIW(MAFCDEL,0x02);                 /*makes CRTC2 stable! Matrox specify 8, but use 4 - grrrr!*/
        DXIW(PANELMODE,0x00);               /*eclipse panellink*/

        /* setup i2c bus */
        i2c_init();

        /* make sure card is in powergraphics mode */
        VGAW_I(CRTCEXT,3,0x80);      

        /* set the system clocks to powergraphics speed */
        LOG(2,("INIT: Setting system PLL to powergraphics speeds\n"));
        g400_dac_set_sys_pll();

        /* 'official' RAM initialisation */
        LOG(2,("INIT: RAM init\n"));
        /* disable hardware plane write mask if SDRAM card */
        if (si->ps.sdram) CFGW(OPTION,(CFGR(OPTION) & 0xffffbfff));
        /* disable plane write mask (needed for SDRAM): actual change needed to get it sent to RAM */
        ACCW(PLNWT,0x00000000);
        ACCW(PLNWT,0xffffffff);
        /* program memory control waitstates */
        ACCW(MCTLWTST, si->ps.mctlwtst_reg);
        /* set memory configuration including:
         * - SDRAM / SGRAM special functions select. */
        CFGW(OPTION,(CFGR(OPTION)&0xFFFF83FF) | (si->ps.option_reg & 0x00001c00));
        if (!si->ps.sdram) CFGW(OPTION,(CFGR(OPTION) | (0x01 << 14)));
        /* set mode register opcode and streamer flow control */        
        ACCW(MEMRDBK,(ACCR(MEMRDBK)&0x0000FFFF)|(si->ps.memrdbk_reg & 0xffff0000));
        /* set RAM read tap delays */
        ACCW(MEMRDBK,(ACCR(MEMRDBK)&0xFFFF0000)|(si->ps.memrdbk_reg & 0x0000ffff));
        /* wait 200uS minimum */
        snooze(250);

        /* reset memory (MACCESS is a write only register!) */
        ACCW(MACCESS, 0x00000000);
        /* perform actual RAM reset */
        ACCW(MACCESS, 0x00008000);
        snooze(250);
        /* start memory refresh */
        CFGW(OPTION,(CFGR(OPTION)&0xffe07fff) | (si->ps.option_reg & 0x001f8000));
        /* set memory control waitstate again AFTER the RAM reset */
        ACCW(MCTLWTST,si->ps.mctlwtst_reg);
        /* end 'official' RAM initialisation. */

        /* 'advance read' busparameter and 'memory priority' enable/disable setup */
        CFGW(OPTION, ((CFGR(OPTION) & 0xefbfffff) | (si->ps.option_reg & 0x10400000)));

        /*enable writing to crtc registers*/
        VGAW_I(CRTC,0x11,0);
        if (si->ps.secondary_head)
        {
                MAVW(LOCK,0x01);
                CR2W(DATACTL,0x00000000);
        }

        return B_OK;
}

static 
status_t g450_general_powerup()
{
        status_t result;
        uint32 pwr_cas[] = {0, 1, 5, 6, 7, 5, 2, 3};

        /* used for convenience: MACCESS is a write only register! */
        uint32 maccess = 0x00000000;

        LOG(4, ("INIT: G450/G550 powerup\n"));
        LOG(4, ("INIT: Detected %s (%s)\n", si->adi.name, si->adi.chipset));
        if (si->settings.logmask & 0x80000000) mga_dump_configuration_space();
        
        /* initialize the shared_info PINS struct */
        result = parse_pins();
        if (result != B_OK) fake_pins();

        /* log the PINS struct settings */
        dump_pins();

        /* check output connector setup */
        if (si->ps.primary_dvi && si->ps.secondary_head &&
                si->ps.tvout && (i2c_sec_tv_adapter() != B_OK))
        {
                /* signal CRTC2 DPMS which connector to program or readout */
                si->crossed_conns = true;
        }

        /* if the user doesn't want a coldstart OR the BIOS pins info could not be found warmstart */
        if (si->settings.usebios || (result != B_OK)) return gx00_general_bios_to_powergraphics();

        /* power up the PLLs,LUT,DAC */
        LOG(4,("INIT: PLL/LUT/DAC powerup\n"));
        /* disable outputs */
        DXIW(OUTPUTCONN,0x00); 
        /* turn off both displays and the hardcursor (also disables transfers) */
        gx00_crtc_dpms(false, false, false);
        g400_crtc2_dpms(false, false, false);
        gx00_crtc_cursor_hide();

        /* power up everything except DVI electronics (for now) */
        DXIW(PWRCTRL,0x1b); 
        /* set voltage reference - not using DAC reference block */
        DXIW(VREFCTRL,0x00);
        /* wait for 100ms for voltage reference to stabilize */
        delay(100000);
        /* power up the SYSPLL */
        CFGW(OPTION,CFGR(OPTION)|0x20);
        /* power up the PIXPLL */
        DXIW(PIXCLKCTRL,0x08);

        /* disable pixelclock oscillations before switching on CLUT */
        DXIW(PIXCLKCTRL, (DXIR(PIXCLKCTRL) | 0x04));
        /* disable 15bit mode CLUT-overlay function */
        DXIW(GENCTRL, DXIR(GENCTRL & 0xfd));
        /* CRTC2->MAFC, 8-bit DAC, CLUT enabled, enable DAC */
        DXIW(MISCCTRL,0x9b);
        snooze(250);

        /* re-enable pixelclock oscillations */
        DXIW(PIXCLKCTRL, (DXIR(PIXCLKCTRL) & 0xfb));

        //fixme:
        DXIW(MAFCDEL,0x02);                 /*makes CRTC2 stable! Matrox specify 8, but use 4 - grrrr!*/
        DXIW(PANELMODE,0x00);               /*eclipse panellink*/

        /* setup i2c bus */
        i2c_init();

        /* make sure card is in powergraphics mode */
        VGAW_I(CRTCEXT,3,0x80);      

        /* set the system clocks to powergraphics speed */
        LOG(2,("INIT: Setting system PLL to powergraphics speeds\n"));
        g450_dac_set_sys_pll();

        /* 'official' RAM initialisation */
        LOG(2,("INIT: RAM init\n"));
        /* stop memory refresh, and setup b9, memconfig, b13, sgram planemask function, b21 fields,
         * and don't touch the rest */
        CFGW(OPTION, ((CFGR(OPTION) & 0xf8400164) | (si->ps.option_reg & 0x00207e00)));
        /* setup b10-b15 unknown field */
        CFGW(OPTION2, ((CFGR(OPTION2) & 0xffff0200) | (si->ps.option2_reg & 0x0000fc00)));

        /* program memory control waitstates */
        ACCW(MCTLWTST, si->ps.mctlwtst_reg);
        /* program option4 b0-3 and b29-30 fields, reset the rest: stop memory clock */
        CFGW(OPTION4, (si->ps.option4_reg & 0x6000000f));
        /* set RAM read tap delays and mode register opcode / streamer flow control */
        ACCW(MEMRDBK, si->ps.memrdbk_reg);
        /* b7 v5_mem_type = done by Mark Watson. fixme: still confirm! (unknown bits) */
        maccess = ((((uint32)si->ps.v5_mem_type) & 0x80) >> 1);
        ACCW(MACCESS, maccess);
        /* clear b0-1 and 3, and set b31 in option4: re-enable memory clock */
        CFGW(OPTION4, ((si->ps.option4_reg & 0x60000004) | 0x80000000));
        snooze(250);

        /* if DDR RAM */
        if ((si->ps.v5_mem_type & 0x0060) == 0x0020) 
        {
                /* if not 'EMRSW RAM-option' available */
                if (!(si->ps.v5_mem_type & 0x0100))
                {
                        /* clear unknown bits */
                        maccess = 0x00000000;
                        ACCW(MACCESS, maccess);
                        /* clear b12: unknown bit */
                        ACCW(MEMRDBK, (si->ps.memrdbk_reg & 0xffffefff));
                }
                else
                        /* if not 'DLL RAM-option' available */
                        if (!(si->ps.v5_mem_type & 0x0200))
                        {
                                /* clear b12: unknown bit */
                                ACCW(MEMRDBK, (si->ps.memrdbk_reg & 0xffffefff));
                        }
        }

        /* create positive flank to generate memory reset */
        //fixme: G550 is still noted as a G450, fix upon trouble..
        if (si->ps.card_type == G450) {
                ACCW(MACCESS, (maccess & 0xffff3fff));
                ACCW(MACCESS, (maccess | 0x0000c000));
        } else { //G550
                ACCW(MACCESS, (maccess & 0xffff7fff));
                ACCW(MACCESS, (maccess | 0x00008000));
        }
        snooze(250);

        /* start memory refresh */
        CFGW(OPTION,(CFGR(OPTION)&0xffe07fff) | (si->ps.option_reg & 0x001f8000));

        /* disable plane write mask (needed for SDRAM): actual change needed to get it sent to RAM */
        ACCW(PLNWT,0x00000000);
        ACCW(PLNWT,0xffffffff);

        /* if not 'MEMCASLT RAM-option' available */
        if (!(si->ps.v5_mem_type & 0x0400))
        {
                /* calculate powergraphics CAS-latency from pins CAS-latency, and update register setting */
                ACCW(MCTLWTST,
                        ((si->ps.mctlwtst_reg & 0xfffffff8) | pwr_cas[(si->ps.mctlwtst_reg & 0x07)]));

        }

        /*enable writing to crtc registers*/
        VGAW_I(CRTC,0x11,0);
        //fixme..
        if (si->ps.secondary_head)
        {
                //MAVW(LOCK,0x01);
                CR2W(DATACTL,0x00000000);
        }

        /* enable primary analog output */
        gx50_general_output_select();

        /* enable 'straight-through' sync outputs on both analog output connectors and
         * make sure CRTC1 sync outputs are patched through! */
        DXIW(SYNCCTRL,0x00); 

        return B_OK;
}

status_t gx50_general_output_select()
{
        /* make sure this call is warranted */
        if ((si->ps.card_type != G450) && (si->ps.card_type != G550)) return B_ERROR;

        /* choose primary analog outputconnector */
        if (si->ps.primary_dvi && si->ps.secondary_head && si->ps.tvout)
        {
                if (i2c_sec_tv_adapter() == B_OK)
                {
                        LOG(4,("INIT: secondary TV-adapter detected, using primary connector\n"));
                        DXIW(OUTPUTCONN,0x01); 
                        /* signal CRTC2 DPMS which connector to program */
                        si->crossed_conns = false;
                }
                else
                {
                        LOG(4,("INIT: no secondary TV-adapter detected, using secondary connector\n"));
                        DXIW(OUTPUTCONN,0x04); 
                        /* signal CRTC2 DPMS which connector to program */
                        si->crossed_conns = true;
                }
        }
        else
        {
                LOG(4,("INIT: using primary connector\n"));
                DXIW(OUTPUTCONN,0x01); 
                /* signal CRTC2 DPMS which connector to program */
                si->crossed_conns = false;
        }
        return B_OK;
}

/* connect CRTC(s) to the specified DAC(s) */
status_t gx00_general_dac_select(int dac)
{
        /*MISCCTRL, clock src,...*/
        switch(dac)
        {
                /* G100 - G400 */
                case DS_CRTC1DAC:
                case DS_CRTC1DAC_CRTC2MAVEN:
                        /* connect CRTC1 to pixPLL */
                        DXIW(PIXCLKCTRL,(DXIR(PIXCLKCTRL)&0xc)|0x1);
                        /* connect CRTC2 to vidPLL, connect CRTC1 to internal DAC and
                         * enable CRTC2 external video timing reset signal.
                         * (Setting for MAVEN 'master mode' TVout signal generation.) */
                        if (si->ps.secondary_head) CR2W(CTL,(CR2R(CTL)&0xffe00779)|0xD0000002);
                        /* disable CRTC1 external video timing reset signal */
                        VGAW_I(CRTCEXT,1,(VGAR_I(CRTCEXT,1)&0x77));
                        /* select CRTC2 RGB24 MAFC mode: connects CRTC2 to MAVEN DAC */ 
                        DXIW(MISCCTRL,(DXIR(MISCCTRL)&0x19)|0x82);
                        break;
                case DS_CRTC1MAVEN:
                case DS_CRTC1MAVEN_CRTC2DAC:
                        /* connect CRTC1 to vidPLL */
                        DXIW(PIXCLKCTRL,(DXIR(PIXCLKCTRL)&0xc)|0x2);
                        /* connect CRTC2 to pixPLL and internal DAC and
                         * disable CRTC2 external video timing reset signal */
                        if (si->ps.secondary_head) CR2W(CTL,(CR2R(CTL)&0x2fe00779)|0x4|(0x1<<20));
                        /* enable CRTC1 external video timing reset signal.
                         * note: this is nolonger used as G450/G550 cannot do TVout on CRTC1 */
                        VGAW_I(CRTCEXT,1,(VGAR_I(CRTCEXT,1)|0x88));
                        /* select CRTC1 RGB24 MAFC mode: connects CRTC1 to MAVEN DAC */
                        DXIW(MISCCTRL,(DXIR(MISCCTRL)&0x19)|0x02);
                        break;
                /* G450/G550 */
                case DS_CRTC1CON1_CRTC2CON2:
                        if (si->ps.card_type < G450) return B_ERROR;
                        /* connect CRTC1 to pixPLL */
                        DXIW(PIXCLKCTRL,(DXIR(PIXCLKCTRL)&0xc)|0x1);
                        /* connect CRTC2 to vidPLL, connect CRTC1 to DAC1, disable CRTC2
                         * external video timing reset signal, set CRTC2 progressive scan mode
                         * and disable TVout mode (b12).
                         * (Setting for MAVEN 'slave mode' TVout signal generation.) */
                        //fixme: enable timing resets if TVout is used in master mode!
                        //otherwise keep it disabled.
                        CR2W(CTL,(CR2R(CTL)&0x2de00779)|0x6|(0x0<<20));
                        /* connect DAC1 to CON1, CRTC2/'DAC2' to CON2 (monitor mode) */
                        DXIW(OUTPUTCONN,0x09); 
                        /* Select 1.5 Volt MAVEN DAC ref. for monitor mode */
                        DXIW(GENIOCTRL, DXIR(GENIOCTRL) & ~0x40);
                        DXIW(GENIODATA, 0x00);
                        /* signal CRTC2 DPMS which connector to program */
                        si->crossed_conns = false;
                        break;
                //fixme: toggle PLL's below if possible: 
                //       otherwise toggle PLL's for G400 2nd case?
                case DS_CRTC1CON2_CRTC2CON1:
                        if (si->ps.card_type < G450) return B_ERROR;
                        /* connect CRTC1 to pixPLL */
                        DXIW(PIXCLKCTRL,(DXIR(PIXCLKCTRL)&0xc)|0x1);
                        /* connect CRTC2 to vidPLL and DAC1, disable CRTC2 external
                         * video timing reset signal, and set CRTC2 progressive scan mode and
                         * disable TVout mode (b12). */
                        CR2W(CTL,(CR2R(CTL)&0x2de00779)|0x6|(0x1<<20));
                        /* connect DAC1 to CON2 (monitor mode), CRTC2/'DAC2' to CON1 */
                        DXIW(OUTPUTCONN,0x05); 
                        /* Select 1.5 Volt MAVEN DAC ref. for monitor mode */
                        DXIW(GENIOCTRL, DXIR(GENIOCTRL) & ~0x40);
                        DXIW(GENIODATA, 0x00);
                        /* signal CRTC2 DPMS which connector to program */
                        si->crossed_conns = true;
                        break;
                default:
                        return B_ERROR;
        }
        return B_OK;
}

/* basic change of card state from VGA to powergraphics -> should work from BIOS init state*/
static 
status_t gx00_general_bios_to_powergraphics()
{
        LOG(2, ("INIT: Skipping card coldstart!\n"));

        //set to powergraphics etc.
        CFGW(DEVCTRL,(2|CFGR(DEVCTRL))); 
        /*enable device response (already enabled here!)*/

        VGAW_I(CRTC,0x11,0);
        /*allow me to change CRTC*/

        VGAW_I(CRTCEXT,3,0x80);
        /*use powergraphix (+ trash other bits, they are set later)*/

        VGAW(MISCW,0x08);
        /*set only MGA pixel clock in MISC - I don't want to map VGA stuff under this OS*/

        switch (si->ps.card_type)
        {
                case G400:
                case G400MAX:
                        /* reset MAVEN so we know the sync polarity is at reset situation (Hpos, Vpos) */
                        if (si->ps.tvout)
                        {
                                ACCW(RST, 0x00000002);
                                snooze(1000);
                                ACCW(RST, 0x00000000);
                        }
                        /* makes CRTC2 stable! Matrox specify 8, but use 4 - grrrr! */
                        DXIW(MAFCDEL,0x02);
                        break;
                case G450:
                case G550:
                        /* power up everything except DVI electronics (for now) */
                        DXIW(PWRCTRL,0x1b); 
                        /* enable 'straight-through' sync outputs on both analog output
                         * connectors and make sure CRTC1 sync outputs are patched through! */
                        DXIW(SYNCCTRL,0x00); 
                        break;
                default:
                        break;
        }

        if (si->ps.card_type >= G100)
        {
                DXIW(MISCCTRL,0x9b);
                /*CRTC2->MAFC, 8-bit DAC, CLUT enabled, enable DAC*/

                DXIW(MULCTRL,0x4);
                /*RGBA direct mode*/
        }
        else
        { 
                LOG(8, ("INIT: < G100 DAC powerup badly implemented, MISC 0x%02x\n", VGAR(MISCR)));
        } // apsed TODO MIL2

        VGAW_I(SEQ,1,0x00);
        /*enable screen*/

        return B_OK;
}

/* Check if mode virtual_size adheres to the cards _maximum_ contraints, and modify
 * virtual_size to the nearest valid maximum for the mode on the card if not so.
 * Then: check if virtual_width adheres to the cards _multiple_ constraints, and
 * create mode slopspace if not so.
 * We use acc multiple constraints here if we expect we can use acceleration, because
 * acc constraints are worse than CRTC constraints.
 *
 * Mode slopspace is reflected in fbc->bytes_per_row BTW. */
//fixme: seperate heads for real dualhead modes:
//CRTC1 and 2 constraints differ!
status_t gx00_general_validate_pic_size (display_mode *target, uint32 *bytes_per_row, bool *acc_mode)
{
        /* Note:
         * This routine assumes that the CRTC memory pitch granularity is 'smaller than',
         * or 'equals' the acceleration engine memory pitch granularity! */

        uint32 video_pitch;
        uint32 acc_mask, crtc_mask;
        uint8 depth = 8;

        /* determine pixel multiple based on 2D/3D engine constraints */
        switch (si->ps.card_type)
        {
        case MIL1:
        case MIL2:
                /* see MIL1/2 specs:
                 * these cards always use a 64bit RAMDAC (TVP3026) and interleaved memory */
                switch (target->space)
                {
                        case B_CMAP8: acc_mask = 0x7f; depth =  8; break;
                        case B_RGB15: acc_mask = 0x3f; depth = 16; break;
                        case B_RGB16: acc_mask = 0x3f; depth = 16; break;
                        case B_RGB24: acc_mask = 0x7f; depth = 24; break;
                        case B_RGB32: acc_mask = 0x1f; depth = 32; break;
                        default:
                                LOG(8,("INIT: unknown color space: 0x%08x\n", target->space));
                                return B_ERROR;
                }
                break;
        default:
                /* see G100 and up specs:
                 * these cards can do 2D as long as multiples of 32 are used.
                 * (Note: don't mix this up with adress linearisation!) */
                switch (target->space)
                {
                        case B_CMAP8: depth =  8; break;
                        case B_RGB15: depth = 16; break;
                        case B_RGB16: depth = 16; break;
                        case B_RGB24: depth = 24; break;
                        case B_RGB32: depth = 32; break;
                        default:
                                LOG(8,("INIT: unknown color space: 0x%08x\n", target->space));
                                return B_ERROR;
                }
                acc_mask = 0x1f;
                break;
        }

        /* determine pixel multiple based on CRTC memory pitch constraints.
         * (Note: Don't mix this up with CRTC timing contraints! Those are
         *        multiples of 8 for horizontal, 1 for vertical timing.) */
        switch (si->ps.card_type)
        {
        case MIL1:
        case MIL2:
                /* see MIL1/2 specs:
                 * these cards always use a 64bit RAMDAC and interleaved memory */
                switch (target->space)
                {
                        case B_CMAP8: crtc_mask = 0x7f; break;
                        case B_RGB15: crtc_mask = 0x3f; break;
                        case B_RGB16: crtc_mask = 0x3f; break;
                        /* for B_RGB24 crtc_mask 0x7f is worst case scenario (MIL2 constraint) */
                        case B_RGB24: crtc_mask = 0x7f; break; 
                        case B_RGB32: crtc_mask = 0x1f; break;
                        default:
                                LOG(8,("INIT: unknown color space: 0x%08x\n", target->space));
                                return B_ERROR;
                }
                break;
        default:
                /* see G100 and up specs */
                switch (target->space)
                {
                        case B_CMAP8: crtc_mask = 0x0f; break;
                        case B_RGB15: crtc_mask = 0x07; break;
                        case B_RGB16: crtc_mask = 0x07; break;
                        case B_RGB24: crtc_mask = 0x0f; break; 
                        case B_RGB32: crtc_mask = 0x03; break;
                        default:
                                LOG(8,("INIT: unknown color space: 0x%08x\n", target->space));
                                return B_ERROR;
                }
                /* see G400 specs: CRTC2 has different constraints */
                /* Note:
                 * set for RGB and B_YCbCr422 modes. Other modes need larger multiples! */
                if (target->flags & DUALHEAD_BITS)
                {
                        switch (target->space)
                        {
                                case B_RGB16: crtc_mask = 0x1f; break;
                                case B_RGB32: crtc_mask = 0x0f; break;
                                default:
                                        LOG(8,("INIT: illegal DH color space: 0x%08x\n", target->space));
                                        return B_ERROR;
                        }
                }
                break;
        }

        /* check if we can setup this mode with acceleration:
         * Max sizes need to adhere to both the acceleration engine _and_ the CRTC constraints! */
        *acc_mode = true;
        /* check virtual_width */
        switch (si->ps.card_type)
        {
        case MIL1:
        case MIL2:
        case G100:
                /* acc constraint: */
                if (target->virtual_width > 2048) *acc_mode = false;
                break;
        default:
                /* G200-G550 */
                /* acc constraint: */
                if (target->virtual_width > 4096) *acc_mode = false;
                /* for 32bit mode a lower CRTC1 restriction applies! */
                if ((target->space == B_RGB32_LITTLE) && (target->virtual_width > (4092 & ~acc_mask)))
                        *acc_mode = false;
                break;
        }
        /* virtual_height */
        if (target->virtual_height > 2048) *acc_mode = false;

        /* now check virtual_size based on CRTC constraints,
         * making sure virtual_width stays within the 'mask' constraint: which is only
         * nessesary because of an extra constraint in MIL1/2 cards that exists here. */
        {
                /* virtual_width */
                //fixme for CRTC2 (identical on all G400+ cards):
                //16bit mode: max. virtual_width == 16352 (no extra mask needed);
                //32bit mode: max. virtual_width == 8176 (no extra mask needed);
                //other colordepths are unsupported on CRTC2.
                switch(target->space)
                {
                case B_CMAP8:
                        if (target->virtual_width > (16368 & ~crtc_mask))
                                target->virtual_width = (16368 & ~crtc_mask);
                        break;
                case B_RGB15_LITTLE:
                case B_RGB16_LITTLE:
                        if (target->virtual_width > (8184 & ~crtc_mask))
                                target->virtual_width = (8184 & ~crtc_mask);
                        break;
                case B_RGB24_LITTLE:
                        if (target->virtual_width > (5456 & ~crtc_mask))
                                target->virtual_width = (5456 & ~crtc_mask);
                        break;
                case B_RGB32_LITTLE:
                        if (target->virtual_width > (4092 & ~crtc_mask))
                                target->virtual_width = (4092 & ~crtc_mask);
                        break;
                }

                /* virtual_height: The only constraint here is the cards memory size which is
                 * checked later on in ProposeMode: virtual_height is adjusted then if needed.
                 * 'Limiting here' to the variable size that's at least available (uint16). */
                if (target->virtual_height > 65535) target->virtual_height = 65535;
        }

        /* OK, now we know that virtual_width is valid, and it's needing no slopspace if
         * it was confined above, so we can finally calculate safely if we need slopspace
         * for this mode... */
        if (*acc_mode)
                video_pitch = ((target->virtual_width + acc_mask) & ~acc_mask);
        else
                video_pitch = ((target->virtual_width + crtc_mask) & ~crtc_mask);

        LOG(2,("INIT: memory pitch will be set to %d pixels for colorspace 0x%08x\n",
                                                                                                                video_pitch, target->space)); 
        if (target->virtual_width != video_pitch)
                LOG(2,("INIT: effective mode slopspace is %d pixels\n", 
                                                                                        (video_pitch - target->virtual_width)));

        /* now calculate bytes_per_row for this mode */
        *bytes_per_row = video_pitch * (depth >> 3);

        return B_OK;
}