/******************************Module*Header***********************************\
*
*                           *******************
*                           * GDI SAMPLE CODE *
*                           *******************
*
* Module Name: hwinit.c
*
* This module contains the functions that enable and disable the hardware
*
* Copyright (c) 1994-1998 3Dlabs Inc. Ltd. All rights reserved.
* Copyright (c) 1995-1999 Microsoft Corporation.  All rights reserved.
\******************************************************************************/

#include "precomp.h"
#include "gdi.h"
#include "p2ctxt.h"
#include "tvp4020.h"
#include "p2rd.h"
#define ALLOC_TAG ALLOC_TAG_WH2P
//-----------------------------------------------------------------------------
//
//  vInitCoreRegisters
//
//  set all core Permedia registers to a known state
//
//-----------------------------------------------------------------------------

VOID
vInitCoreRegisters(PPDev ppdev)
{
    PERMEDIA_DECL;
    PERMEDIA_DEFS(ppdev);

    // tracks FBWindowBase for off-screen bitmaps
    permediaInfo->PixelOffset = 0; 
    permediaInfo->TextureAddressMode = __PERMEDIA_ENABLE;
    permediaInfo->TextureReadMode = __PERMEDIA_DISABLE;
                                    /*__FX_TEXREADMODE_2048HIGH |
                                    __FX_TEXREADMODE_2048WIDE |
                                    __FX_TEXREADMODE_TWRAP_REPEAT | 
                                    __FX_TEXREADMODE_SWRAP_REPEAT |
                                    __PERMEDIA_ENABLE;*/
    RESERVEDMAPTR( 41);
    SEND_PERMEDIA_DATA(DeltaMode, 0);
    SEND_PERMEDIA_DATA(ColorDDAMode, 0);
    SEND_PERMEDIA_DATA(ScissorMode, 0);
    SEND_PERMEDIA_DATA(TextureColorMode, 0);
    SEND_PERMEDIA_DATA(FogMode, 0);
    SEND_PERMEDIA_DATA(Window, 0);
    SEND_PERMEDIA_DATA(StencilMode, 0);
    SEND_PERMEDIA_DATA(DepthMode, 0);
    SEND_PERMEDIA_DATA(AlphaBlendMode, 0);
    SEND_PERMEDIA_DATA(DitherMode, 0);
    SEND_PERMEDIA_DATA(LBReadMode, 0);
    SEND_PERMEDIA_DATA(LBWriteMode, 0);
    SEND_PERMEDIA_DATA(RasterizerMode, 0);
    SEND_PERMEDIA_DATA(WindowOrigin, 0);
    SEND_PERMEDIA_DATA(StatisticMode, 0);
    SEND_PERMEDIA_DATA(FBSoftwareWriteMask, -1);
    SEND_PERMEDIA_DATA(FBHardwareWriteMask, -1);
    SEND_PERMEDIA_DATA(FilterMode, 0);
    SEND_PERMEDIA_DATA(FBWindowBase, 0);
    SEND_PERMEDIA_DATA(FBPixelOffset, 0);
    SEND_PERMEDIA_DATA(LogicalOpMode, 0);
    SEND_PERMEDIA_DATA(FBReadMode, 0);
    SEND_PERMEDIA_DATA(dXDom, 0);
    SEND_PERMEDIA_DATA(dXSub, 0);
    SEND_PERMEDIA_DATA(dY, INTtoFIXED(1));
    SEND_PERMEDIA_DATA(TextureAddressMode, 0);
    SEND_PERMEDIA_DATA(TextureReadMode, 0);
    SEND_PERMEDIA_DATA(TexelLUTMode, 0);
    SEND_PERMEDIA_DATA(Texel0, 0);
    SEND_PERMEDIA_DATA(YUVMode, 0);
    SEND_PERMEDIA_DATA(FBReadPixel, __PERMEDIA_32BITPIXEL);      // 32 bit pixels
    SEND_PERMEDIA_DATA(SStart, 0);
    SEND_PERMEDIA_DATA(dSdx, 1 << 20);
    SEND_PERMEDIA_DATA(dSdyDom, 0);
    SEND_PERMEDIA_DATA(TStart, 0);
    SEND_PERMEDIA_DATA(dTdx, 0);
    SEND_PERMEDIA_DATA(dTdyDom, 0);
    SEND_PERMEDIA_DATA(TextureDataFormat, __FX_TEXTUREDATAFORMAT_32BIT_RGBA | 
                                          __P2_TEXTURE_DATAFORMAT_FLIP);
    SEND_PERMEDIA_DATA(TextureColorMode, 
                    (_P2_TEXTURE_COPY << PM_TEXCOLORMODE_APPLICATION) 
                    | __PERMEDIA_ENABLE); // ignored by texture glyph rendering
    SEND_PERMEDIA_DATA(TextureAddressMode, permediaInfo->TextureAddressMode);
    SEND_PERMEDIA_DATA(TextureReadMode, permediaInfo->TextureReadMode);
    COMMITDMAPTR();
    FLUSHDMA();
}

//-----------------------------------------------------------------------------
//
//  bInitializeHW
//
//  Called to load the initial values into the chip. We assume the hardware
//  has been mapped. All the relevant stuff should be hanging off ppdev. We
//  also sort out all the hardware capabilities etc.
//
//-----------------------------------------------------------------------------

BOOL
bInitializeHW(PPDev ppdev)
{
    HwDataPtr permediaInfo;
    Surf*     psurf;
    LONG      i, j;
    ULONG     width;
    ULONG     ulValue;
    BOOL      bExists;
    ULONG     ulLength;
    ULONG     dmaBufferSize;
    PERMEDIA_DEFS(ppdev);

    DBG_GDI((7, "bInitializeHW: fbsize: 0x%x", ppdev->FrameBufferLength));

    // allocate and initialize ppdev->permediaInfo. We store hardware specific
    // stuff in this structure.
    //
    permediaInfo = (HwDataPtr)
        ENGALLOCMEM( FL_ZERO_MEMORY, sizeof(HwDataRec), ALLOC_TAG); 
    if ( permediaInfo == NULL )
    {
        DBG_GDI((0, "cannot allocate memory for permediaInfo struct"));
        return (FALSE);
    }
    
    ppdev->permediaInfo = permediaInfo;
    permediaInfo->pGDICtxt = NULL;
    permediaInfo->pCurrentCtxt = NULL;

    // retrieve the PCI configuration information and local buffer size
    ulLength = sizeof(Hw_Device_Info);
    if ( EngDeviceIoControl(ppdev->hDriver,
                            IOCTL_VIDEO_QUERY_DEVICE_INFO,
                            NULL,
                            0,
                            (PVOID)&(permediaInfo->deviceInfo),
                            ulLength,
                            &ulLength) )
    {
        DBG_GDI((1, "QUERY_DEVICE_INFO failed."));
        return (FALSE);
    }

    ulLength = sizeof(PINTERRUPT_CONTROL_BLOCK);
    if ( EngDeviceIoControl(ppdev->hDriver,
                            IOCTL_VIDEO_MAP_INTERRUPT_CMD_BUF,
                            NULL,
                            0,
                            (PVOID)&pP2dma,
                            ulLength,
                            &ulLength) )
    {
        DBG_GDI((1, "MAP_INTERRUPT_CMD_BUF failed."));
        return FALSE;
    }

    //
    // On NT4.0 the above IOCTL_VIDEO_MAP_INTERRUPT_CMD_BUF will fail and
    // pP2dma will be NULL. Hence we allocate it via ENGALLOCMEM.
    //
    if(g_bOnNT40)
    {
        ASSERTDD(pP2dma == 0, "bInitializeHW: pP2dma != 0");
        pP2dma = (P2DMA*) ENGALLOCMEM(FL_ZERO_MEMORY, sizeof(_P2DMA), ALLOC_TAG);
    }

    // Even if IOCtrl call succeeded, 
    // the allocation can still fail.
    if (pP2dma==NULL)
    {
        DBG_GDI((0,"allocation of memory for P2DMA from miniport failed"));
        return FALSE;
    }

    // store away shared memory area for later use in ppdev
    ppdev->pP2dma=pP2dma;

    DBG_GDI((7, "deviceInfo: VendorId: 0x%x, DevId %d, DeltaId 0x%x,"
                "RevId %d, SubId %d, SubVId %d, lbuf len 0x%x, lbuf width %d",
             permediaInfo->deviceInfo.VendorId,
             permediaInfo->deviceInfo.DeviceId,
             permediaInfo->deviceInfo.DeltaRevId,
             permediaInfo->deviceInfo.RevisionId,
             permediaInfo->deviceInfo.SubsystemId,
             permediaInfo->deviceInfo.SubsystemVendorId,
             permediaInfo->deviceInfo.LocalbufferLength,
             permediaInfo->deviceInfo.LocalbufferWidth));

    // collect flags as we initialize so zero it here
    permediaInfo->flags = 0;

    // set up default pointers to our low level rendering functions
    //
    ppdev->pgfnAlphaBlend           = vAlphaBlend;
    ppdev->pgfnConstantAlphaBlend   = vConstantAlphaBlend;
    ppdev->pgfnCopyBlt              = vCopyBlt;
    ppdev->pgfnGradientFillRect     = vGradientFillRect;
    ppdev->pgfnPatFill              = vPatFill;
    ppdev->pgfnMonoPatFill          = vMonoPatFill;
    ppdev->pgfnMonoOffset           = vMonoOffset;
    ppdev->pgfnPatRealize           = vPatRealize;
    ppdev->pgfnSolidFill            = vSolidFill;
    ppdev->pgfnSolidFillWithRop     = vSolidFillWithRop;
    ppdev->pgfnTransparentBlt       = vTransparentBlt;
    ppdev->pgfnInvert               = vInvert;

    ppdev->pulRamdacBase = (ULONG*) ppdev->pulCtrlBase[0] 
                         + P2_EXTERNALVIDEO / sizeof(ULONG);

    // safe pointers to Permedia 2 registers for later use
    //
    ppdev->pCtrlBase   = ((ULONG *)ppdev->pulCtrlBase[0])+CTRLBASE/sizeof(ULONG);
    ppdev->pGPFifo     = ((ULONG *)ppdev->pulCtrlBase[0])+GPFIFO/sizeof(ULONG);
    ppdev->pCoreBase   = ((ULONG *)ppdev->pulCtrlBase[0])+COREBASE/sizeof(ULONG);

    DBG_GDI((5, "Initialize: pCtrlBase=0x%p", ppdev->pCtrlBase));
    DBG_GDI((5, "Initialize: pGPFifo=0x%p", ppdev->pGPFifo));
    DBG_GDI((5, "Initialize: pCoreBase=0x%p", ppdev->pCoreBase));

    if (!bInitializeP2DMA( pP2dma,
                           ppdev->hDriver,
                           (ULONG *)ppdev->pulCtrlBase[0],
                           ppdev->dwAccelLevel
                         ))
    {
        DBG_GDI((0, "P2DMA initialization failed."));
        return FALSE;
    }

    // keep a copy of Permedia 2 ChipConfig, so we know
    // if we are running on a AGP card or not
    ppdev->dwChipConfig = P2_READ_CTRL_REG(PREG_CHIPCONFIG);

    //
    // If we have a gamma ramp saved in the registry then use that. Otherwise,
    // initialize the LUT with a gamma of 1.0
    //
    if ( !bRegistryRetrieveGammaLUT(ppdev, &permediaInfo->gammaLUT) ||
         !bInstallGammaLUT(ppdev, &permediaInfo->gammaLUT) )
    {
        vSetNewGammaValue(ppdev, 0x10000);
    }

    //
    // fill in the permediaInfo capability flags and block fill size.
    //
    permediaInfo->flags |= GLICAP_NT_CONFORMANT_LINES;

    //
    // reset all core registers
    //
    vInitCoreRegisters(ppdev);

    //
    // now initialize the non-zero core registers
    //
    RESERVEDMAPTR(20);    // reserve a reasonable amount until the all is setup

    // Rasterizer Mode
    // Fraction and Bias are used by the line drawing code. MirrorBitMask
    // is set as all the bits we download are interpreted from bit 31 to bit 0

    permediaInfo->RasterizerMode = __PERMEDIA_START_BIAS_ZERO << 4 |
                                   __PERMEDIA_FRACTION_ADJUST_ALMOST_HALF << 2 |
                                   __PERMEDIA_ENABLE << 0 | // mirror bit mask
                                   __PERMEDIA_ENABLE << 18; // limits enabled

    SEND_PERMEDIA_DATA(RasterizerMode, permediaInfo->RasterizerMode);

    SEND_PERMEDIA_DATA(YLimits, 2047 << 16);
    SEND_PERMEDIA_DATA(XLimits, 2047 << 16);
    
    // Disable screen scissor
    SEND_PERMEDIA_DATA(ScissorMode, __PERMEDIA_DISABLE);
    SEND_PERMEDIA_DATA(ScreenSize, ppdev->cyScreen << 16 | ppdev->cxScreen);

    ULONG ulPackedPP;

    DBG_GDI((7, "assuming screen stride is %d pixels\n", ppdev->cxMemory));
    vCalcPackedPP( ppdev->cxMemory, NULL, &ulPackedPP);

    // initialize FBReadMode for our default stride
    SEND_PERMEDIA_DATA(FBReadMode, ulPackedPP);

    // FB Write Mode
    permediaInfo->FBWriteMode = 1 | ((32/*permediaInfo->fastFillBlockSz*/ >> 4) << 1);

    SEND_PERMEDIA_DATA(FBWriteMode, permediaInfo->FBWriteMode);
    DBG_GDI((7, "setting FBWriteMode to 0x%x", (DWORD)permediaInfo->FBWriteMode));

    SEND_PERMEDIA_DATA(FBReadPixel, ppdev->cPelSize);

    //
    // do a probe to see if we support hardware writemasks. use the bottom
    // 8 bits only so the same code works for all depths. We also query a
    // registry variable which, if set, forces the use of software masking.
    //
    bExists = bRegistryQueryUlong(  ppdev,
                                    REG_USE_SOFTWARE_WRITEMASK,
                                    &ulValue);

    if ( !bExists || (ulValue == 0) )
    {
        // this code works as everything is little endian. i.e. the byte we
        // test is always at the lowest address regardless of the pixel depth.
        //
        WRITE_SCREEN_ULONG(ppdev->pjScreen, 0);   // quickest way to clear a pixel!!

        SEND_PERMEDIA_DATA(LogicalOpMode,       __PERMEDIA_CONSTANT_FB_WRITE);
        SEND_PERMEDIA_DATA(FBWriteData,         0xff);
        SEND_PERMEDIA_DATA(StartXDom,           0);
        SEND_PERMEDIA_DATA(StartY,              0);
        SEND_PERMEDIA_DATA(FBHardwareWriteMask, 0xa5);
        SEND_PERMEDIA_DATA(Render,              __RENDER_POINT_PRIMITIVE);
        COMMITDMAPTR();

        SYNC_WITH_PERMEDIA;

        ulValue = READ_SCREEN_ULONG(ppdev->pjScreen);
        if ( (ulValue & 0xff) == 0xa5 )
            permediaInfo->flags |= GLICAP_HW_WRITE_MASK;

        RESERVEDMAPTR(3);
    }

    DBG_GDI((7, "mode registers initialized"));

    SEND_PERMEDIA_DATA(FBHardwareWriteMask, -1);
    SEND_PERMEDIA_DATA(TextureAddressMode, __PERMEDIA_DISABLE);
    SEND_PERMEDIA_DATA(TextureColorMode, __PERMEDIA_DISABLE);
    COMMITDMAPTR();
    FLUSHDMA();
  
    // Initialize out InFifo cached information

    dmaBufferSize = INPUT_BUFFER_SIZE;

#if defined(_X86_)
    if(!g_bOnNT40 && !pP2dma->bDMAEmulation)
    {
        AllocateDMABuffer(ppdev->hDriver, 
                          (LONG *) &dmaBufferSize,
                          &ppdev->dmaBufferVirtualAddress,
                          &ppdev->dmaBufferPhysicalAddress);

        if(ppdev->dmaBufferVirtualAddress != NULL
           && dmaBufferSize < INPUT_BUFFER_SIZE)
        {
            FreeDMABuffer(ppdev->hDriver, ppdev->dmaBufferVirtualAddress);
            ppdev->dmaBufferVirtualAddress = NULL;
        }
    }
    else
    {
        ppdev->dmaBufferVirtualAddress = NULL;
    }
#else
    ppdev->dmaBufferVirtualAddress = NULL;
#endif

    if(ppdev->dmaBufferVirtualAddress != NULL)
    {
        ppdev->pulInFifoStart = ppdev->dmaBufferVirtualAddress;
        ppdev->pulInFifoEnd = ppdev->dmaBufferVirtualAddress 
                            + (INPUT_BUFFER_SIZE>>3);
        ppdev->dmaCurrentBufferOffset = 0;
    }
    else
    {
        ppdev->pulInFifoStart = (ULONG*) ENGALLOCMEM(0, INPUT_BUFFER_SIZE>>1, ALLOC_TAG);

        if(ppdev->pulInFifoStart == NULL)
        {
            DBG_GDI((0, "bInitializeHW: unable to allocate scratch buffer"));
            goto errExit;
        }
        
        ppdev->pulInFifoEnd = ppdev->pulInFifoStart + (INPUT_BUFFER_SIZE>>3);
    }

    ppdev->pulInFifoPtr = ppdev->pulInFifoStart;

#if DBG
    ppdev->ulReserved = 0;
#endif
    
    //
    // We are done setting up the GDI context state.
    //

    //
    // Allocate a hardware context for this PDEV saving the current context.
    //
    DBG_GDI((7, "allocating new context"));
    permediaInfo->pGDICtxt = P2AllocateNewContext(ppdev,
                                                    NULL,
                                                    0,
                                                    P2CtxtWriteOnly
                                                    );            

    if ( permediaInfo->pGDICtxt == NULL )
    {
        DBG_GDI((1, "failed to allocate Permedia context for display driver/GDI"));
        return (FALSE);
    }

    DBG_GDI((7, "got context id 0x%x for GDI context", permediaInfo->pGDICtxt));
    P2SwitchContext(ppdev, permediaInfo->pGDICtxt);

    return (TRUE);

errExit:
    return FALSE;

}// bInitializeHW()

//-----------------------------------------------------------------------------
//
//  vDisableHW
//
//  do whatever needs to be done to disable the hardware and free resources
//  allocated in bInitializeHW
//
//-----------------------------------------------------------------------------

VOID
vDisableHW(PPDev ppdev)
{
    Surf*  psurf;
    PERMEDIA_DECL;

    if ( !permediaInfo )
    {
        return;
    }

    if(ppdev->dmaBufferVirtualAddress != NULL)
        FreeDMABuffer( ppdev->hDriver, ppdev->dmaBufferVirtualAddress);
    else if(ppdev->pulInFifoStart) // No DMA case..we allocated via ENGALLOCMEM
        ENGFREEMEM(ppdev->pulInFifoStart);

    //
    // Free up any contexts we allocated
    //
    if ( permediaInfo->pGDICtxt != NULL )
    {
        P2FreeContext(ppdev, permediaInfo->pGDICtxt);
        permediaInfo->pGDICtxt = NULL;
    }

    if ( permediaInfo->ContextTable )
    {
        ENGFREEMEM(permediaInfo->ContextTable);
    }
    permediaInfo->ContextTable=NULL;

    vFree(ppdev->pP2dma);
    ppdev->pP2dma = NULL;

    ENGFREEMEM(permediaInfo);

}// vDisableHW()

//-----------------------------------------------------------------------------
//
// VOID vAssertModeHW
//
// We're about to switch to/from full screen mode so do whatever we need to
// to save context etc.
//
//-----------------------------------------------------------------------------

VOID
vAssertModeHW(PPDev ppdev, BOOL bEnable)
{
    PERMEDIA_DECL;

    if (!permediaInfo)
        return;

    if (!bEnable)
    {
        
        if(ppdev->permediaInfo->pCurrentCtxt != NULL)
            P2SwitchContext(ppdev, NULL);
    
        //
        // Disable DMA
        //

        ASSERTDD(ppdev->pP2dma->bEnabled,
                 "vAssertModeHW: expected dma to be enabled");

        vSyncWithPermedia(ppdev->pP2dma);
        ppdev->pP2dma->bEnabled = FALSE;
    }
    else
    {
        //
        // Enable DMA
        //

        ppdev->pP2dma->bEnabled = TRUE;

        ASSERTDD(ppdev->permediaInfo->pCurrentCtxt == NULL,
                 "vAssertModeHW: expected no active context");

        //
        // Restore the current gamma LUT.
        //
        bInstallGammaLUT(ppdev, &permediaInfo->gammaLUT);
    }
}

