/* $XFree86$ */
/****************************************************************************
* mga_esc.c
*
* ESC call implementation
*
* (C) Matrox Graphics, Inc.
*****************************************************************************/

#ifdef USEMGAHAL

/* All drivers should typically include these */
#include "xf86.h"
#include "xf86_OSproc.h"
#include "xf86Resources.h"

/* All drivers need this */
#include "xf86_ansic.h"

#include "compiler.h"

/* Drivers for PCI hardware need this */
#include "xf86PciInfo.h"

/* Drivers that need to access the PCI config space directly need this */
#include "xf86Pci.h"

/* All drivers initialising the SW cursor need this */
#include "mipointer.h"

/* All drivers implementing backing store need this */
#include "mibstore.h"

#include "micmap.h"

#include "xf86DDC.h"
#include "xf86RAC.h"
#include "vbe.h"

#include "fb.h"
#include "cfb8_32.h"
#include "dixstruct.h"

#include "mga_reg.h"
#include "mga.h"
#include "mga_macros.h"

/* ESC */
LPMGAMODEINFO pMgaModeInfo[2] = {NULL};
MGAMODEINFO   TmpMgaModeInfo[2] = {{0}};

/* ESC Implementation */
static void EscHLeft(ScrnInfoPtr pScrn, unsigned long *param, char *sResult, DisplayModePtr pMode);
static void EscHRight(ScrnInfoPtr pScrn, unsigned long *param, char *sResult, DisplayModePtr pMode);
static void EscVUp(ScrnInfoPtr pScrn, unsigned long *param, char *sResult, DisplayModePtr pMode);
static void EscVDown(ScrnInfoPtr pScrn, unsigned long *param, char *sResult, DisplayModePtr pMode);
static void EscHLarger(ScrnInfoPtr pScrn, unsigned long *param, char *sResult, DisplayModePtr pMode);
static void EscHSmaller(ScrnInfoPtr pScrn, unsigned long *param, char *sResult, DisplayModePtr pMode);
static void EscVTaller(ScrnInfoPtr pScrn, unsigned long *param, char *sResult, DisplayModePtr pMode);
static void EscVSmaller(ScrnInfoPtr pScrn, unsigned long *param, char *sResult, DisplayModePtr pMode);
static void EscRefresh(ScrnInfoPtr pScrn, unsigned long *param, char *sResult, DisplayModePtr pMode);
static void EscRestoreVidParm(ScrnInfoPtr pScrn, unsigned long *param, char *sResult, DisplayModePtr pMode);
static void EscRead(ScrnInfoPtr pScrn, unsigned long *param, char *sResult, DisplayModePtr pMode);
static void EscWrite(ScrnInfoPtr pScrn, unsigned long *param, char *sResult, DisplayModePtr pMode);
static void EscHal(ScrnInfoPtr pScrn, unsigned long *param, char *sResult, DisplayModePtr pMode);
static void EscTest(ScrnInfoPtr pScrn, unsigned long *param, char *sResult, DisplayModePtr pMode);
static void EscMerged(ScrnInfoPtr pScrn, unsigned long *param, char *sResult, DisplayModePtr pMode);

static LPMGAMODEINFO  GetModeInfoPtr(ULONG ulScreen);
static void GetVideoParameterStr(LPMGAMODEINFO pModeInfo, char *sResult);
static Bool convertNumber(unsigned long *pulNumber, char *sNumber);


static MGAEscFuncRec FunctionTable[] = {
    {"hal",  EscHal},
    {"test",  EscTest},
    {"read",  EscRead},
	{"write", EscWrite},
    {"left",  EscHLeft},
    {"right", EscHRight},
    {"down",  EscVDown},
    {"up",    EscVUp},
    {"h+",    EscHLarger},
    {"h-",    EscHSmaller},
    {"v+",    EscVTaller},
    {"v-",    EscVSmaller},
    {"refresh", EscRefresh},
    {"undo",  EscRestoreVidParm},
    {"merged", EscMerged},
    {NULL,NULL}
};


void MGAFillDisplayModeStruct(DisplayModePtr pMode, LPMGAMODEINFO pModeInfo)
{
   pMode->Clock = pModeInfo->ulPixClock;

   pMode->HDisplay = pModeInfo->ulDispWidth;
   pMode->HSyncStart =  pModeInfo->ulDispWidth
                       + pModeInfo->ulHFPorch;
   pMode->HSyncEnd = pModeInfo->ulDispWidth
                       + pModeInfo->ulHFPorch
                        + pModeInfo->ulHSync;
   pMode->HTotal = pModeInfo->ulDispWidth
                       + pModeInfo->ulHFPorch
                       + pModeInfo->ulHSync
                       + pModeInfo->ulHBPorch;

   pMode->VDisplay = pModeInfo->ulDispHeight;
   pMode->VSyncStart =  pModeInfo->ulDispHeight
                       + pModeInfo->ulVFPorch;
   pMode->VSyncEnd = pModeInfo->ulDispHeight
                       + pModeInfo->ulVFPorch
                       + pModeInfo->ulVSync;
   pMode->VTotal = pModeInfo->ulDispHeight
                       + pModeInfo->ulVFPorch
                       + pModeInfo->ulVSync
                       + pModeInfo->ulVBPorch;

   pMode->VRefresh = pModeInfo->ulRefreshRate;
}

static LPMGAMODEINFO  GetModeInfoPtr(ULONG ulScreen)
{

    if ( !TmpMgaModeInfo[ulScreen].ulDispWidth )
    {
        TmpMgaModeInfo[ulScreen] = *pMgaModeInfo[ulScreen];
    }

    return &TmpMgaModeInfo[ulScreen];
}


static void GetVideoParameterStr(LPMGAMODEINFO pModeInfo, char *sResult)
{
    sprintf(sResult, "%d %d %d %d %d %d %d %d %d %d %d",
            pModeInfo->ulDispWidth,
            pModeInfo->ulDispHeight,
            pModeInfo->ulBpp,
            pModeInfo->ulPixClock,
            pModeInfo->ulHFPorch,
            pModeInfo->ulHSync,
            pModeInfo->ulHBPorch,
            pModeInfo->ulVFPorch,
            pModeInfo->ulVSync,
            pModeInfo->ulVBPorch,
            pModeInfo->flSignalMode);
}


static float  GetVRefresh(LPMGAMODEINFO pModeInfo)
{
    ULONG ulHTotal;
    ULONG ulVTotal;

    ulHTotal =
        pModeInfo->ulDispWidth +
        pModeInfo->ulHFPorch +
        pModeInfo->ulHSync +
        pModeInfo->ulHBPorch;

    ulVTotal =
        pModeInfo->ulDispHeight +
        pModeInfo->ulVFPorch    +
        pModeInfo->ulVSync      +
        pModeInfo->ulVBPorch;

    return ((float)pModeInfo->ulPixClock * 1000.0) / (ulHTotal * ulVTotal);
}

static void EscHal(ScrnInfoPtr pScrn, unsigned long *param, char *sResult, DisplayModePtr pMode)
{
	MGAPtr pMGA = MGAPTR(pScrn);

	if(pMGA->HALLoaded)
		strcpy(sResult, "YES");
	else
		strcpy(sResult, "NO");

}

static void EscTest(ScrnInfoPtr pScrn, unsigned long *param, char
*sResult, DisplayModePtr pMode)
{
	strcpy(sResult, "YES");
}

static void EscMerged(ScrnInfoPtr pScrn, unsigned long *param, char
*sResult, DisplayModePtr pMode)
{
	strcpy(sResult, "YES");
}

static void  EscRead(ScrnInfoPtr pScrn, unsigned long *param, char *sResult, DisplayModePtr pMode)
{
    MGAPtr pMga = MGAPTR(pScrn);
    ULONG  ulSource, ulAddr, ulData;
    UCHAR  ucIndex;

    if ( (param[0] & 0xffff) < 2 )
    {
        strcpy(sResult, "#error 1");
        return;
    }


    ulSource = param[1] >> 16;
    ulAddr = param[1] & 0xffff;


    switch( ulSource )
    {
    case 0:
        ulData = INREG(ulAddr);
        sprintf(sResult, "MGA[%04X] = 0x%08X", ulAddr, ulData);
        break;
    case 1:
        ucIndex = INREG8(0x3c00);
        OUTREG8(0x3c00, (UCHAR)ulAddr);
        ulData = (ULONG)INREG8(0x3c0a);
        OUTREG8(0x3c00, ucIndex);
        sprintf(sResult, "DAC[%02X] = 0x%02X", ulAddr, ulData);
        break;
    case 2:
        ucIndex = INREG8(0x1fd4);
        OUTREG8(0x1fd4, (UCHAR)ulAddr);
        ulData = (ULONG)INREG8(0x1fd5);
        OUTREG8(0x1fd4, ucIndex);
        sprintf(sResult, "CRTC[%02X] = 0x%02X", ulAddr, ulData);
        break;
    case 3:
        ucIndex = INREG8(0x1fde);
        OUTREG8(0x1fde, (UCHAR)ulAddr);
        ulData = (ULONG)INREG8(0x1fdf);
        OUTREG8(0x1fde, ucIndex);
        sprintf(sResult, "CRTCEXT[%02X] = 0x%02X", ulAddr, ulData);
        break;
    default:
        strcpy(sResult, "ERROR# 2");
        break;
    }
}

static void  EscWrite(ScrnInfoPtr pScrn, unsigned long *param, char *sResult, DisplayModePtr pMode)
{
    MGAPtr pMga = MGAPTR(pScrn);
    ULONG  ulSource, ulAddr, ulData;
    UCHAR  ucIndex;

    if ( (param[0] & 0xffff) < 3 )
    {
        strcpy(sResult, "#error 1");
        return;
    }

    ulSource = param[1] >> 16;
    ulAddr = param[1] & 0xffff;
    ulData = param[2];


    switch( ulSource )
    {
    case 0:
        OUTREG(ulAddr, ulData);
        strcpy(sResult, "OK");
        break;
    case 1:
        ucIndex = INREG8(0x3c00);
        OUTREG8(0x3c00, (UCHAR)ulAddr);
        OUTREG8(0x3c0a, (UCHAR)ulData);
        OUTREG8(0x3c00, ucIndex);
        strcpy(sResult, "OK");
        break;
    case 2:
        ucIndex = INREG8(0x1fd4);
        OUTREG8(0x1fd4, (UCHAR)ulAddr);
        OUTREG8(0x1fd5, (UCHAR)ulData);
        OUTREG8(0x1fd4, ucIndex);
        strcpy(sResult, "OK");
        break;
    case 3:
        ucIndex = INREG8(0x1fde);
        OUTREG8(0x1fde, (UCHAR)ulAddr);
        OUTREG8(0x1fdf, (UCHAR)ulData);
        OUTREG8(0x1fde, ucIndex);
        strcpy(sResult, "OK");
        break;
    default:
        strcpy(sResult, "ERROR# 2");
        break;
    }
}

static void  EscHLeft(ScrnInfoPtr pScrn, unsigned long *param, char *sResult, DisplayModePtr pMode)
{
    MGAPtr pMga = MGAPTR(pScrn);
    LPMGAMODEINFO pModeInfo;

    pModeInfo = GetModeInfoPtr(param[0] >> 16);

    if ( !pMgaModeInfo )
    {
        strcpy(sResult, "#error 1");
        return;
    }


    if (pModeInfo->ulHBPorch > (8 * param[1]) )
    {
        pModeInfo->ulHBPorch -=8 * param[1];
        pModeInfo->ulHFPorch +=8 * param[1];
        MGASetMode(pMga->pBoard, pModeInfo);
    }

    MGAFillDisplayModeStruct(pMode, pModeInfo);

    GetVideoParameterStr(pModeInfo, sResult);
}


static void  EscHRight(ScrnInfoPtr pScrn, unsigned long *param, char *sResult, DisplayModePtr pMode)
{
    MGAPtr pMga = MGAPTR(pScrn);
    LPMGAMODEINFO pModeInfo;

    pModeInfo = GetModeInfoPtr(param[0] >> 16);

    if ( !pMgaModeInfo )
    {
        strcpy(sResult, "#error 1");
        return;
    }

    if (pModeInfo->ulHFPorch > (8 * param[1]) )
    {
        pModeInfo->ulHFPorch -=8 * param[1];
        pModeInfo->ulHBPorch +=8 * param[1];
        MGASetMode(pMga->pBoard, pModeInfo);
    }

    MGAFillDisplayModeStruct(pMode, pModeInfo);

    GetVideoParameterStr(pModeInfo, sResult);
}



static void  EscVUp(ScrnInfoPtr pScrn, unsigned long *param, char *sResult, DisplayModePtr pMode)
{
    MGAPtr pMga = MGAPTR(pScrn);
    LPMGAMODEINFO pModeInfo;

    pModeInfo = GetModeInfoPtr(param[0] >> 16);

    if ( !pMgaModeInfo )
    {
        strcpy(sResult, "#error 1");
        return;
    }

    if (pModeInfo->ulVBPorch > (param[1]) )
    {
        pModeInfo->ulVBPorch -= param[1];
        pModeInfo->ulVFPorch += param[1];
        MGASetMode(pMga->pBoard, pModeInfo);
    }

    MGAFillDisplayModeStruct(pMode, pModeInfo);

    GetVideoParameterStr(pModeInfo, sResult);
}


static void  EscVDown(ScrnInfoPtr pScrn, unsigned long *param, char *sResult, DisplayModePtr pMode)
{
    MGAPtr pMga = MGAPTR(pScrn);
    LPMGAMODEINFO pModeInfo;

    pModeInfo = GetModeInfoPtr(param[0] >> 16);

    if ( !pMgaModeInfo )
    {
        strcpy(sResult, "#error 1");
        return;
    }

    if (pModeInfo->ulVFPorch >= (param[1]) )
    {
        pModeInfo->ulVFPorch -= param[1];
        pModeInfo->ulVBPorch += param[1];
        MGASetMode(pMga->pBoard, pModeInfo);
    }

    MGAFillDisplayModeStruct(pMode, pModeInfo);

    GetVideoParameterStr(pModeInfo, sResult);
}


static void EscHLarger(ScrnInfoPtr pScrn, unsigned long *param, char *sResult, DisplayModePtr pMode)
{
    MGAPtr pMga = MGAPTR(pScrn);
    LPMGAMODEINFO pModeInfo;
    float fRefresh, fPixelClock;
    ULONG ulStep;

    pModeInfo = GetModeInfoPtr(param[0] >> 16);

    if ( !pMgaModeInfo )
    {
        strcpy(sResult, "#error 1");
        return;
    }

    if ((param[0] & 0xffff) > 1)
    {

        ulStep = param[1] * 8;
    }
    else
    {

        ulStep = 8;
    }

    fRefresh = GetVRefresh(pModeInfo);
    fPixelClock = (float)pModeInfo->ulPixClock;
    if (pModeInfo->ulHBPorch >= ulStep )
    {
        pModeInfo->ulHBPorch -= ulStep;
    }
    else
    {
        pModeInfo->ulHBPorch = 0;
    }
    pModeInfo->ulPixClock = (ULONG)( (fRefresh * fPixelClock) / GetVRefresh(pModeInfo));
    MGASetMode(pMga->pBoard, pModeInfo);

    MGAFillDisplayModeStruct(pMode, pModeInfo);

    GetVideoParameterStr(pModeInfo, sResult);
}


static void EscHSmaller(ScrnInfoPtr pScrn, unsigned long *param, char *sResult, DisplayModePtr pMode)
{
    MGAPtr pMga = MGAPTR(pScrn);
    LPMGAMODEINFO pModeInfo;
    float fRefresh, fPixelClock;
    ULONG ulStep;

    pModeInfo = GetModeInfoPtr(param[0] >> 16);

    if ( !pMgaModeInfo )
    {
        strcpy(sResult, "#error 1");
        return;
    }

    if ((param[0] & 0xffff) > 1)
    {

        ulStep = param[1] * 8;
    }
    else
    {

        ulStep = 8;
    }


    fRefresh = GetVRefresh(pModeInfo);
    fPixelClock = (float)pModeInfo->ulPixClock;
    pModeInfo->ulHBPorch += ulStep;
    pModeInfo->ulPixClock = (ULONG)( (fRefresh * fPixelClock) / GetVRefresh(pModeInfo));

    MGASetMode(pMga->pBoard, pModeInfo);

    MGAFillDisplayModeStruct(pMode, pModeInfo);

    GetVideoParameterStr(pModeInfo, sResult);
}

static void EscVTaller(ScrnInfoPtr pScrn, unsigned long *param, char *sResult, DisplayModePtr pMode)
{
    MGAPtr pMga = MGAPTR(pScrn);
    LPMGAMODEINFO pModeInfo;
    float fRefresh, fPixelClock;
    ULONG ulStep;

    pModeInfo = GetModeInfoPtr(param[0] >> 16);

    if ( !pMgaModeInfo )
    {
        strcpy(sResult, "#error 1");
        return;
    }

    if ((param[0] & 0xffff) > 1)
    {

        ulStep = param[1];
    }
    else
    {

        ulStep = 1;
    }

    fRefresh = GetVRefresh(pModeInfo);
    fPixelClock = (float)pModeInfo->ulPixClock;

    if (pModeInfo->ulVBPorch >= ulStep )
    {
        pModeInfo->ulVBPorch -= ulStep;
    }
    else
    {
        pModeInfo->ulVBPorch = 0;
    }

    pModeInfo->ulPixClock = (ULONG)( (fRefresh * fPixelClock) / GetVRefresh(pModeInfo));
    MGASetMode(pMga->pBoard, pModeInfo);

    MGAFillDisplayModeStruct(pMode, pModeInfo);

    GetVideoParameterStr(pModeInfo, sResult);
}

static void EscVSmaller(ScrnInfoPtr pScrn, unsigned long *param, char *sResult, DisplayModePtr pMode)
{
    MGAPtr pMga = MGAPTR(pScrn);
    LPMGAMODEINFO pModeInfo;
    float fRefresh, fPixelClock;
    ULONG ulStep;

    pModeInfo = GetModeInfoPtr(param[0] >> 16);

    if ( !pMgaModeInfo )
    {
        strcpy(sResult, "#error 1");
        return;
    }

    if ((param[0] & 0xffff) > 1)
    {

        ulStep = param[1];
    }
    else
    {

        ulStep = 1;
    }


    fRefresh = GetVRefresh(pModeInfo);
    fPixelClock = (float)pModeInfo->ulPixClock;
    pModeInfo->ulVFPorch += ulStep;
    pModeInfo->ulPixClock = (ULONG)( (fRefresh * fPixelClock) / GetVRefresh(pModeInfo));
    MGASetMode(pMga->pBoard, pModeInfo);

    MGAFillDisplayModeStruct(pMode, pModeInfo);

    GetVideoParameterStr(pModeInfo, sResult);
}


static void EscRefresh(ScrnInfoPtr pScrn, unsigned long *param, char *sResult, DisplayModePtr pMode)
{
    MGAPtr pMga = MGAPTR(pScrn);
    LPMGAMODEINFO pModeInfo;
    float fRefresh, fPixelClock;

    pModeInfo = GetModeInfoPtr(param[0] >> 16);

    if ( !pMgaModeInfo )
    {
        strcpy(sResult, "#error 1");
        return;
    }

    if ((param[0] & 0xffff) < 2)
    {
        strcpy(sResult, "#error 1");
        return;
    }

    fRefresh = GetVRefresh(pModeInfo);

    fPixelClock = (float)pModeInfo->ulPixClock;
    pModeInfo->ulPixClock = (ULONG)( ((float)param[1] * fPixelClock) / fRefresh);

    pModeInfo->ulRefreshRate = param[1];

    MGASetMode(pMga->pBoard, pModeInfo);

    MGAFillDisplayModeStruct(pMode, pModeInfo);

    GetVideoParameterStr(pModeInfo, sResult);
}

static void EscRestoreVidParm(ScrnInfoPtr pScrn, unsigned long *param, char *sResult, DisplayModePtr pMode)
{
    MGAPtr pMga = MGAPTR(pScrn);

    TmpMgaModeInfo[param[0] >> 16].ulDispWidth = 0;
    MGASetMode(pMga->pBoard, pMgaModeInfo[param[0] >> 16]);

    MGAFillDisplayModeStruct(pMode, pMgaModeInfo[param[0] >> 16]);

    GetVideoParameterStr(pMgaModeInfo[param[0] >> 16], sResult);
}

static Bool convertNumber(unsigned long *pulNumber, char *sNumber)
{
    unsigned long i, ulDigit, shiftHex;
    Bool bResult = TRUE;

    if (sNumber == NULL)
    {
        return FALSE;
    }


    /* Convert number */
    if ( (sNumber[0] == '0') && (sNumber[1] == 'x') )
    {
        shiftHex = 0;
        *pulNumber = 0;

        for (i = strlen(sNumber) - 1; i > 1; i--)
        {
            if (shiftHex > 28)
            {
                bResult = FALSE;
                break;
            }

            if ( !isxdigit(sNumber[i]) )
            {
                bResult = FALSE;
                break;
            }

            ulDigit = toupper(sNumber[i]) - '0';
            if (ulDigit > 9)
            {
                ulDigit -= 7;
            }
            *pulNumber += ulDigit << shiftHex;
            shiftHex += 4;
        }
    }
    else
    {
        for (i = 0; i < strlen(sNumber); i++)
        {
            if ( !isdigit(sNumber[i]) )
            {
                bResult = FALSE;
                break;
            }
        }
        *pulNumber = atoi(sNumber);
    }

    return bResult;
}

static Bool GetEscCommand(char *cmdline, EscCmdStruct *escCmd)
{
    unsigned long i, paramIndex, ulHI;
    Bool bResult;
    char *pParameter, *function;

    bResult = TRUE; /* success */

    function = strtok(cmdline, " \t\n,");


    escCmd->parameters[0] = 0;
    if (function)
    {
        /* Find Screen */
        if (function[1] == ':' )
        {
            escCmd->parameters[0] = (unsigned long)(function[0] - '0') << 16;
            strncpy(escCmd->function, function+2, 32);
        }
        else
        {
            strncpy(escCmd->function, function, 32);
        }

    }
    else
    {
        strcpy(escCmd->function, "#ERROR -1");
        escCmd->parameters[0] = 0;
        return FALSE;
    }

    paramIndex = 1;
    while ( (pParameter = strtok(NULL, " \t\n,")) != NULL )
    {
        if (paramIndex > 31)
        {
            /* 32 parameters supported */
            break;
        }

        i = 0;
        while(pParameter[i] && pParameter[i] != ':')
        {
            i++;
        }

        if ( pParameter[i] )
        {
            pParameter[i] = '\0';
            bResult  = convertNumber(&escCmd->parameters[paramIndex], &pParameter[i+1]);
            bResult |= convertNumber(&ulHI, pParameter);
            escCmd->parameters[paramIndex] &= 0xffff;
            escCmd->parameters[paramIndex] += ulHI << 16;
            pParameter[i] = ':';
        }
        else
        {
            bResult = convertNumber(&escCmd->parameters[paramIndex], pParameter);
        }


        if (!bResult)
        {
            break;
        }
        paramIndex++;
    }

    escCmd->parameters[0] += paramIndex;
    return bResult;

}

void MGAExecuteEscCmd(ScrnInfoPtr pScrn, char *cmdline , char *sResult, DisplayModePtr pMode)
{
    int i = 0;
    int ulScreen = 0;
    MGAPtr pMga = MGAPTR(pScrn);
    EscCmdStruct EscCmd;

    if (pMga->SecondCrtc)
    {
	ulScreen = 1;
    }
    else
    {
	ulScreen = 0;
    }


    if (FunctionTable[0].function &&  GetEscCommand(cmdline,  &EscCmd) )
    {
        i = 0;

        while ( FunctionTable[i].function && strcmp(FunctionTable[i].function, EscCmd.function) )
        {
            i++;
        }

        if (FunctionTable[i].function)
        {
	    EscCmd.parameters[0] &= 0xffff;
	    EscCmd.parameters[0] |= ulScreen << 16;

            FunctionTable[i].funcptr(pScrn, EscCmd.parameters, sResult, pMode);
        }
        else
        {
            strcpy(sResult, "error# -1");
        }
    }
    else
    {
        strcpy(sResult, "error# -1");
    }
}
#else
int mga_foo;
#endif
