//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//
// Copyright (c) Samsung Electronics. Co. LTD.  All rights reserved.
//
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.

Module Name:    blt.cpp
Abstract:       accelerated bitblt/rectangle for FIMG-2D & SW Optimize Code
Functions:
Notes:
--*/


#include "precomp.h"
#include <dispperf.h>

SCODE
SMDKDisp::BltPrepare(GPEBltParms *pBltParms)
{
    RECTL rectl;
    int   iSwapTmp;
    static bool bIsG2DReady = FALSE;

    RETAIL_2D_MSG(DISP_ZONE_ENTER, (TEXT("%s\r\n"), _T(__FUNCTION__)));

    DispPerfStart(pBltParms->rop4); //< This will be '0' when not defined DO_DISPPERF
    DispPerfParam(pBltParms);

    // default to base EmulatedBlt routine
    pBltParms->pBlt = &GPE::EmulatedBlt;        // catch all

    // see if we need to deal with cursor
    // check for destination overlap with cursor and turn off cursor if overlaps
    if (pBltParms->pDst == m_pPrimarySurface)    // only care if dest is main display surface
    {
        if (m_CursorVisible && !m_CursorDisabled)
        {
            if (pBltParms->prclDst != NULL)        // make sure there is a valid prclDst
            {
                rectl = *pBltParms->prclDst;        // if so, use it

                // There is no guarantee of a well ordered rect in blitParamters
                // due to flipping and mirroring.
                if(rectl.top > rectl.bottom)
                {
                    iSwapTmp     = rectl.top;
                    rectl.top    = rectl.bottom;
                    rectl.bottom = iSwapTmp;
                }
                if(rectl.left > rectl.right)
                {
                    iSwapTmp    = rectl.left;
                    rectl.left  = rectl.right;
                    rectl.right = iSwapTmp;
                }
            }
            else
            {
                rectl = m_CursorRect;                    // if not, use the Cursor rect - this forces the cursor to be turned off in this case
            }

            if (m_CursorRect.top <= rectl.bottom && m_CursorRect.bottom >= rectl.top &&
                m_CursorRect.left <= rectl.right && m_CursorRect.right >= rectl.left)
            {
                CursorOff();
                m_CursorForcedOff = TRUE;
            }
        }
    }

    // check for source overlap with cursor and turn off cursor if overlaps
    if (pBltParms->pSrc == m_pPrimarySurface)    // only care if source is main display surface
    {
        if (m_CursorVisible && !m_CursorDisabled)
        {
            if (pBltParms->prclSrc != NULL)        // make sure there is a valid prclSrc
            {
                rectl = *pBltParms->prclSrc;        // if so, use it
            }
            else
            {
                rectl = m_CursorRect;                    // if not, use the CUrsor rect - this forces the cursor to be turned off in this case
            }

            if (m_CursorRect.top < rectl.bottom && m_CursorRect.bottom > rectl.top &&
                m_CursorRect.left < rectl.right && m_CursorRect.right > rectl.left)
            {
                CursorOff();
                m_CursorForcedOff = TRUE;
            }
        }
    }

#if RETURNTOSW_FOR_PERFORMANCE
    if(pBltParms->rop4 == 0xAAF0)
    {
        // (ROP:0xAAF0) SW Font Blt is faster than HW.
        return S_OK;
    }
#endif

    if( m_VideoPowerState != VideoPowerOff && // to avoid hanging while bring up display H/W
        m_G2DControlArgs.UseHWAccel)
    {
        AcceleratedBltSelect(pBltParms);
    }

#if ((G2D_PROCESSINGTYPE == G2D_FASTRETURN_INT) || (G2D_PROCESSINGTYPE == G2D_FASTRETURN_POL))
    if (pBltParms->pBlt == &SMDKDisp::AcceleratedBlt)
    {
        if (m_oG2D->m_descDstSurface.dwPhyBaseaddr != NULL)
            m_pLastDstSurfUsingHW = pBltParms->pDst;
        if ((pBltParms->pSrc) && (m_oG2D->m_descSrcSurface.dwPhyBaseaddr != NULL))
            m_pLastSrcSurfUsingHW = pBltParms->pSrc;
        if (pBltParms->pBrush)
            m_pLastPatSurfUsingHW = pBltParms->pBrush;
        if (pBltParms->pMask)
            m_pLastMskSurfUsingHW = pBltParms->pMask;
            
        m_dwLastProcessType = m_oG2D->m_dwProcessType;    
    }
    else
    {
        if ((pBltParms->pDst && m_pLastDstSurfUsingHW == pBltParms->pDst) ||
            (pBltParms->pSrc && m_pLastSrcSurfUsingHW == pBltParms->pSrc) ||
            (pBltParms->pBrush && m_pLastPatSurfUsingHW == pBltParms->pBrush) ||
            (pBltParms->pMask && m_pLastMskSurfUsingHW == pBltParms->pMask))
        {
            WaitForNotBusy();
            m_pLastDstSurfUsingHW = NULL;
            m_pLastSrcSurfUsingHW = NULL;
            m_pLastPatSurfUsingHW = NULL;
            m_pLastMskSurfUsingHW = NULL;
            m_dwLastProcessType = 0;
        }
    }
#endif

#if USE_SECEMUL_LIBRARY
    // To extract 2d_accel_lib.lib totally, using preprocess statement
    if(m_G2DControlArgs.UseSWAccel)
    {
        SECEmulatedBltSelect16(pBltParms);            
        SECEmulatedBltSelect2416(pBltParms);
        SECEmulatedBltSelect1624(pBltParms);
    }
#endif

    RETAIL_2D_MSG(DISP_ZONE_ENTER, (_T("--%s\r\n"), _T(__FUNCTION__)));
    return S_OK;
}


// This function would be used to undo the setting of clip registers etc
SCODE
SMDKDisp::BltComplete(GPEBltParms *pBltParms)
{
    RETAIL_2D_MSG (DISP_ZONE_ENTER, (_T("++%s()\r\n"), _T(__FUNCTION__)));

    // see if cursor was forced off because of overlap with source or destination and turn back on
    if (m_CursorForcedOff)
    {
        m_CursorForcedOff = FALSE;
        
        if ((m_G2DControlArgs.UseHWAccel) && 
            ((m_oG2D->m_dwProcessType == G2D_FASTRETURN_INT) || 
            (m_oG2D->m_dwProcessType == G2D_FASTRETURN_POL)))
        {
            WaitForNotBusy();
        }
        CursorOn();
    }

    RETAIL_2D_MSG(DISP_ZONE_ENTER, (_T("--%s()\r\n"), _T(__FUNCTION__)));
    return S_OK;
}


// Do NOTHING.
SCODE SMDKDisp::NullBlt(GPEBltParms *pBltParms)
{
    return S_OK;
}


BOOL SMDKDisp::CompromizeForPerformance(GPEBltParms *pBltParms)
{
    if ((pBltParms->rop4 == 0xF0F0) && (pBltParms->solidColor != -1))
    {
        // (ROP:0xF0F0 PATCOPY with SolidFill) SW is faster than HW.
        return FALSE;
    }

    if (pBltParms->pSrc)
    {    
        long dwCompromizeSize = AREA_SIZE(pBltParms->prclDst, pBltParms->pDst);

        if (dwCompromizeSize < G2D_COMPROMISE_LIMIT)
        {
            return FALSE;
        }  

        if (pBltParms->pSrc->Rotate() != pBltParms->pDst->Rotate())
        {
            return TRUE;
        }

        if ((pBltParms->bltFlags & BLT_STRETCH) && (pBltParms->bltFlags & BLT_ALPHABLEND))
        {
            return TRUE;
        }

        if (pBltParms->bltFlags & BLT_ALPHABLEND)
        {
            if (pBltParms->blendFunction.AlphaFormat == 0)
            {
                if (pBltParms->pSrc->InVideoMemory())
                {
                    if (dwCompromizeSize < (long)G2D_COMPROMISE_LIMIT_CONSTANT_ALPHABLEND)
                    {
                        return FALSE;
                    }
                }       
                else
                {
                    if (dwCompromizeSize < (long)G2D_COMPROMISE_LIMIT_CONSTANT_ALPHABLEND_S)
                    {
                        return FALSE;
                    }            
                }
            }
            else
            {
                if (pBltParms->pSrc->InVideoMemory())
                {
                    if (dwCompromizeSize < (long)G2D_COMPROMISE_LIMIT_PERPIXEL_ALPHABLEND)
                    {
                        return FALSE;
                    }
                }       
                else
                {
                    if (dwCompromizeSize < (long)G2D_COMPROMISE_LIMIT_PERPIXEL_ALPHABLEND_S)
                    {
                        return FALSE;
                    }            
                }
            }
        }
        else if(pBltParms->bltFlags & BLT_STRETCH) 
        {
            if (pBltParms->pSrc->InVideoMemory())
            {
                if (dwCompromizeSize < (long)G2D_COMPROMISE_LIMIT_STRETCH)
                {
                    return FALSE;
                }
            }       
            else
            {
                if (dwCompromizeSize < (long)G2D_COMPROMISE_LIMIT_STRETCH_S)
                {
                    return FALSE;
                }            
            }
        }        
        else if ((pBltParms->pConvert) && !(pBltParms->bltFlags & BLT_TRANSPARENT)) 
        {
            if (pBltParms->pSrc->InVideoMemory())
            {
                if (dwCompromizeSize < (long)G2D_COMPROMISE_LIMIT_COLORCONVERSION)
                {
                    return FALSE;
                }
            }       
            else
            {
                if (dwCompromizeSize < (long)G2D_COMPROMISE_LIMIT_COLORCONVERSION_S)
                {
                    return FALSE;
                }            
            }
        }

        if (pBltParms->rop4 != 0xcccc)
        {    
            if (pBltParms->pSrc->InVideoMemory())
            {    
                if (dwCompromizeSize < (DWORD)G2D_COMPROMISE_LIMIT_NOTSRCCOPY)
                {    
                    return FALSE;
                }
            }       
            else
            {   
                if (dwCompromizeSize < (DWORD)G2D_COMPROMISE_LIMIT_NOTSRCCOPY_S)
                {    
                    return FALSE;
                }            
            }
        }
        else if((!(pBltParms->bltFlags & BLT_STRETCH)) &&
            (!(pBltParms->bltFlags & BLT_ALPHABLEND)) &&
            (!(pBltParms->bltFlags & BLT_TRANSPARENT)) &&
            (m_oG2D->m_descDstSurface.dwColorMode == G2D_COLOR_RGB_565) &&
            (m_oG2D->m_descSrcSurface.dwColorMode == G2D_COLOR_RGB_565))
        {    
            // RGB565 to RGB565 Blt(SRCCOPY) case, MS code is faster than HW.
            return FALSE;
        }
    }

    return TRUE;
}


BOOL SMDKDisp::IsSurfLinear(GPEBltParms *pBltParms)
{
    if(pBltParms->pSrc)
    {
        if(pBltParms->pSrc->InVideoMemory())
        {
            m_oG2D->m_descSrcSurface.dwPhyBaseaddr = m_VideoMemoryPhysicalBase + pBltParms->pSrc->OffsetInVideoMemory();
        }
        else
        {
            m_oG2D->m_descSrcSurface.dwPhyBaseaddr = ValidatePAContinuityOfSurf(pBltParms->pSrc, NULL);
            if ((!m_G2DControlArgs.UseCloneBuffer && m_oG2D->m_descSrcSurface.dwPhyBaseaddr == NULL))
            {
                return FALSE;
            }
        }
    }

    if(pBltParms->pDst)
    {
        if(pBltParms->pDst->InVideoMemory())
        {
            m_oG2D->m_descDstSurface.dwPhyBaseaddr = m_VideoMemoryPhysicalBase + pBltParms->pDst->OffsetInVideoMemory();
        }
        else
        {
            m_oG2D->m_descDstSurface.dwPhyBaseaddr = ValidatePAContinuityOfSurf(pBltParms->pDst, NULL);
            if ((!m_G2DControlArgs.UseCloneBuffer && m_oG2D->m_descDstSurface.dwPhyBaseaddr == NULL))
            {
                return FALSE;
            }
        }
    }

    if(pBltParms->pBrush)
    {
        if(pBltParms->pBrush->InVideoMemory())
        {
            m_oG2D->m_descBrushSurface.dwPhyBaseaddr = m_VideoMemoryPhysicalBase + pBltParms->pBrush->OffsetInVideoMemory();
        }
        else
        {
            m_oG2D->m_descBrushSurface.dwPhyBaseaddr = ValidatePAContinuityOfSurf(pBltParms->pBrush, NULL);
            if (m_oG2D->m_descBrushSurface.dwPhyBaseaddr == NULL)
            {
                return FALSE;
            }
        }
    }

    if(pBltParms->pMask)
    {
        if(pBltParms->pMask->InVideoMemory())
        {
            m_oG2D->m_descMaskSurface.dwPhyBaseaddr = m_VideoMemoryPhysicalBase + pBltParms->pMask->OffsetInVideoMemory();
        }
        else
        {
            m_oG2D->m_descMaskSurface.dwPhyBaseaddr = ValidatePAContinuityOfSurf(pBltParms->pMask, pBltParms->prclMask);
            if (m_oG2D->m_descMaskSurface.dwPhyBaseaddr == NULL)
            {
                return FALSE;
            }
        }
    }

    m_oG2D->m_dwProcessType = G2D_PROCESSINGTYPE;

    if (m_oG2D->m_dwProcessType <= G2D_FASTRETURN_POL)
    {
        if ((pBltParms->pSrc) && (!pBltParms->pSrc->InVideoMemory()) && (m_oG2D->m_descSrcSurface.dwPhyBaseaddr))
        {
            m_oG2D->m_dwProcessType += 2;
        }
        else if ((!pBltParms->pDst->InVideoMemory()) || (!m_oG2D->m_descDstSurface.dwPhyBaseaddr))
        {
            m_oG2D->m_dwProcessType += 2;
        }
    }
    
    return TRUE;
}


/**
*   @fn     void SMDKDisp::AcceleratedBltSelect(GpeBltParms *pBltParms)
*   @brief  Select appropriate hardware acceleration function or software emulation function.
*           if there's no appropriate accelerated function,
*           Leave Blit funciton to intial setting, EmulatedBlt(generic Bit blit emulator)
*   @param  pBltParms   Blit Parameter Information Structure
*   @sa     GPEBltParms
*/
SCODE SMDKDisp::AcceleratedBltSelect( GPEBltParms *pBltParms )
{
    if(pBltParms->pDst)
    {
        memset(&m_oG2D->m_descDstSurface, 0, sizeof(m_oG2D->m_descDstSurface));
        m_oG2D->m_descDstSurface.dwColorMode = m_oG2D->GetHWColorFormat(pBltParms->pDst);
        if(m_oG2D->m_descDstSurface.dwColorMode == G2D_COLOR_UNUSED)
        {
            RETAIL_2D_MSG(DISP_ZONE_2D, (_T("[G2D] DST 2D HW does not support this color format\r\n")));
            return S_OK;
        }
    }
    else
    {
        return S_OK;
    }

    if(pBltParms->pSrc)
    {
        memset(&m_oG2D->m_descSrcSurface, 0, sizeof(m_oG2D->m_descSrcSurface));
        m_oG2D->m_descSrcSurface.dwColorMode = m_oG2D->GetHWColorFormat(pBltParms->pSrc);
        if(m_oG2D->m_descSrcSurface.dwColorMode == G2D_COLOR_UNUSED)
        {
            RETAIL_2D_MSG(DISP_ZONE_2D, (_T("[G2D] SRC 2D HW does not support this color format\r\n")));
            return S_OK;
        }
    }
    
#if RETURNTOSW_FOR_PERFORMANCE
    if (!CompromizeForPerformance(pBltParms))
    {
        return S_OK;
    }
#endif

    if(pBltParms->pBrush)
    {
        memset(&m_oG2D->m_descBrushSurface, 0, sizeof(m_oG2D->m_descBrushSurface));	
        if (pBltParms->pBrush->Rotate() != pBltParms->pDst->Rotate())
        {
            // FIMGv3.0 dose not support Pattern rotate.
            return S_OK;
        }
    }

    if(pBltParms->pMask)
    {
        memset(&m_oG2D->m_descMaskSurface, 0, sizeof(m_oG2D->m_descMaskSurface));	
        if(pBltParms->pMask->Format() != gpe1Bpp)
        {
            RETAIL_2D_MSG(DISP_ZONE_2D,(_T("[G2D] MASK 0xAAF0 but NOT 1BPP %x\r\n"),pBltParms->pMask->Format()));
            return S_OK;
        }
        if(pBltParms->pMask->Stride() == 0)
        {
            return S_OK;
        }
    }

    if(pBltParms->pConvert) 	//< Emulate if color conversion required
    {
        if(!pBltParms->pSrc)
        {
            return S_OK;
        }
    }

    if(pBltParms->prclClip && (pBltParms->prclClip->left == pBltParms->prclClip->right) && (pBltParms->prclClip->top == pBltParms->prclClip->bottom))
    {
        return S_OK;
    }
 
    if(pBltParms->pSrc && pBltParms->prclSrc)
    {
        if((pBltParms->prclSrc->right == pBltParms->prclSrc->left) || (pBltParms->prclSrc->bottom == pBltParms->prclSrc->top))
        {
            return S_OK;
        }
    }

    if ((pBltParms->prclDst) && 
        ((pBltParms->prclDst->right == pBltParms->prclDst->left) || 
        (pBltParms->prclDst->bottom == pBltParms->prclDst->top) ||
        (pBltParms->prclDst->right < 0) ||
        (pBltParms->prclDst->bottom < 0) ||
        (pBltParms->prclDst->top > pBltParms->pDst->Height()) ||
        (pBltParms->prclDst->left > SURFACE_WIDTH(pBltParms->pDst))))
    {
        return S_OK;
    } 
    
    // Stretch Bllitting with X or Y axis mirroring
    if (pBltParms->bltFlags & BLT_STRETCH)
    {
        if (BILINEAR == pBltParms->iMode || HALFTONE == pBltParms->iMode) 
        {
            RETAIL_2D_MSG(DISP_ZONE_2D, (_T("[G2D] BLT_STRETCH BILINEAR and HALFTONE are not supported\r\n")));
            return S_OK;
        }

        if(!pBltParms->prclDst || !pBltParms->prclSrc)
        {
            return S_OK;
        }
        else
        {   //  Check if source and destination regions' coordinates has positive value.
            if ((pBltParms->prclDst->left < 0) || (pBltParms->prclDst->right <0 ) || (pBltParms->prclDst->top <0 ) || (pBltParms->prclDst->bottom <0))
            {
                return S_OK;
            }
            if ((pBltParms->prclSrc->left < 0) || (pBltParms->prclSrc->right <0 ) || (pBltParms->prclSrc->top <0 ) || (pBltParms->prclSrc->bottom <0))
            {
                return S_OK;
            }
        }
    }

    if (!IsSurfLinear(pBltParms))
    {
        return S_OK;
    }
    
    if (pBltParms->pDst->Rotate() == DMDO_180)
    {
        if ((pBltParms->bltFlags & BLT_ALPHABLEND) && (pBltParms->bltFlags & BLT_STRETCH)  && (pBltParms->blendFunction.SourceConstantAlpha != 255))
        {
            if (pBltParms->pSrc == pBltParms->pDst)    
            {
                // In this case, CETK 231 could be fail.
                // Need to investigate, why this case makes CETK 231 fail.
                return S_OK;
            }
        }
    }
    
    pBltParms->pBlt = (SCODE (GPE::*)(struct GPEBltParms *)) &SMDKDisp::AcceleratedBlt;

    return S_OK;
}


SCODE
SMDKDisp::AcceleratedBlt(GPEBltParms *pBltParms)
{
    if(m_oG2D->BitBlt(pBltParms, m_G2DControlArgs))
    {
        return EmulatedBlt(pBltParms);
    }
    
    return S_OK;
}


BOOL
SMDKDisp::CreateCloneSurf(void)
{    
    // DirectDrawSurface has Align limitation
    if (SrcCloneSurf == NULL)
    {
        if (FAILED(AllocSurface((DDGPESurf**)&SrcCloneSurf, G2D_CLONESURF_WIDTH,
                                    G2D_CLONESURF_HEIGHT,
                                    gpe32Bpp,
                                    ddgpePixelFormat_8888,
                                    GPE_REQUIRE_VIDEO_MEMORY)))
        {
            RETAIL_2D_MSG(DISP_ZONE_2D,(_T("[G2D] %s() : SrcCloneSurf AllocSurface() Fail\n\r"), _T(__FUNCTION__)));
            return FALSE;
        }
        m_oG2D->m_sCloneBuffer.pdwSrc_Vir = (DWORD *)SrcCloneSurf->Buffer();
        m_oG2D->m_sCloneBuffer.dwSrc_Phy = (m_VideoMemoryPhysicalBase + SrcCloneSurf->OffsetInVideoMemory());
    }
    if(DstCloneSurf == NULL)
    {       
        if (FAILED(AllocSurface((DDGPESurf**)&DstCloneSurf, G2D_CLONESURF_WIDTH,
                                G2D_CLONESURF_HEIGHT,
                                gpe32Bpp,
                                ddgpePixelFormat_8888,
                                GPE_REQUIRE_VIDEO_MEMORY)))
        {
            RETAIL_2D_MSG(DISP_ZONE_2D,(_T("[G2D] %s() : DstCloneSurf AllocSurface() Fail\n\r"), _T(__FUNCTION__)));
            return FALSE;
        }    
        m_oG2D->m_sCloneBuffer.pdwDst_Vir = (DWORD *)DstCloneSurf->Buffer();
        m_oG2D->m_sCloneBuffer.dwDst_Phy = (m_VideoMemoryPhysicalBase + DstCloneSurf->OffsetInVideoMemory());
    }
    return TRUE;
}


BOOL
SMDKDisp::ReleaseCloneSurf(void)
{
    if (SrcCloneSurf)
    {
        m_oG2D->m_sCloneBuffer.pdwSrc_Vir = NULL;
        m_oG2D->m_sCloneBuffer.dwSrc_Phy = NULL;
        delete SrcCloneSurf;
        SrcCloneSurf = NULL;
    }

    if (DstCloneSurf)
    {
        m_oG2D->m_sCloneBuffer.pdwDst_Vir = NULL;
        m_oG2D->m_sCloneBuffer.dwDst_Phy = NULL;
        delete DstCloneSurf;
        DstCloneSurf = NULL;
    }
    
    return TRUE;
}


#define PFN_SHIEFT UserKInfo[KINX_PFN_SHIFT]
#define MAX_SUPPORTED_PFN (MAXDWORD>>PFN_SHIEFT)
#define PAGE_MASK (PAGE_SIZE-1)
//#define DBGMSG_SRCC    (FALSE)
#define DBGMSG_SRCC    (DISP_ZONE_TEMP)


/**
*    @fn    SCODE SMDKDisp::ValidatePAContinuityOfSurf(GPESurf *pTargetSurf)
*    @brief    If possible, clean dcahce for Source Rectangle
*    @param    pTargetSurf       Surface pointer of Target Surface
*    @sa        GPESurf
*    @note    for support cb to ncnb bitblt
*    @note    Bitmap image has top-down or bottom-up style memory region,
*            we can determine that by stride as positive(top-down) or as negative(bottom-up)
*            bottom-up bitmap mean buffer's start address is last addres of image buffer
*            image's start address is calculated as (Buffer address + Stride(negative) * height)
*    @return DWORD PhysAddressofSurface
*/
DWORD SMDKDisp::ValidatePAContinuityOfSurf(GPESurf *pTargetSurf, RECTL *pTargetRectl)
{
    BOOL        m_fLocked;
    LPVOID      m_lpvLockedAddress;
    DWORD       m_dwLockedSize;
    DWORD       dwLength = 0;
    DWORD       dwSrcSurfaceSize = 0;
    PDWORD      m_pdwPhysAddress = 0;
    PDWORD      m_pdwPhysLength;
    PBYTE *     m_ppVirtAddress;
    PVOID       pUseBufferPtr = 0;
    PVOID       pVirtualStartPtr = 0;

    ASSERT(m_pdwPhysAddress==0);
    ASSERT((PAGE_MASK & PAGE_SIZE) == 0 );

    if (pTargetSurf->Height() != 0)
    {
        dwLength = pTargetSurf->Height() * ABS(pTargetSurf->Stride());
    }
    else
    {
        if (pTargetRectl)
        {
            dwLength = RECT_HEIGHT(pTargetRectl) * ABS(pTargetSurf->Stride());
        }
        else
        {
            return NULL;
        }
    }

    RETAIL_2D_MSG(DISP_ZONE_2D,(_T("[G2D] TargetSurf 0x%x, Height : %d, Stride : %d\r\n"),pTargetSurf->Buffer(), pTargetSurf->Height(), pTargetSurf->Stride())); //< XPositive, yPositive

#if (_WIN32_WCE < 600)
    if(pTargetSurf->Stride() < 0)
    {
        pUseBufferPtr = MapPtrToProcess((PDWORD)((DWORD)pTargetSurf->Buffer() - dwLength + ABS(pTargetSurf->Stride())), (HANDLE)GetCurrentProcessId());
        pVirtualStartPtr = MapPtrToProcess(pUseBufferPtr, (HANDLE)GetCurrentProcessId());
    }
    else
    {
        pUseBufferPtr = MapPtrToProcess((PDWORD)pTargetSurf->Buffer(), (HANDLE)GetCurrentProcessId());
        pVirtualStartPtr = MapPtrToProcess(pUseBufferPtr, (HANDLE)GetCurrentProcessId());;
    }
#else
    if(pTargetSurf->Stride() < 0)
    {
        pUseBufferPtr = (PDWORD)((DWORD)pTargetSurf->Buffer() - dwLength + ABS(pTargetSurf->Stride()));
        pVirtualStartPtr = pUseBufferPtr;
    }
    else
    {
        pUseBufferPtr = (PDWORD)pTargetSurf->Buffer();
        pVirtualStartPtr = pUseBufferPtr;
    }
#endif

    UINT nPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES( pUseBufferPtr, dwLength );

    m_pdwPhysAddress = new DWORD[3*nPages];        // It's really sturcture {m_pdwPhysAddress, m_pdwPhysLength, m_ppVirtAddress}

    if (m_pdwPhysAddress) 
    {
        m_pdwPhysLength = m_pdwPhysAddress + nPages;
        m_ppVirtAddress = (PBYTE *)(m_pdwPhysAddress + 2*nPages);	
        RETAIL_2D_MSG(DISP_ZONE_2D,(_T("[G2D] pUseBufferPtr:0x%x, dwLength:%d, m_pdwPhysAddress:0x%x.\r\n"), pUseBufferPtr, dwLength, m_pdwPhysAddress));
        m_fLocked = LockPages( pUseBufferPtr, dwLength, m_pdwPhysAddress, LOCKFLAG_QUERY_ONLY);        // Src to Dst

        if (!m_fLocked) 
        {   // Create table for Physical Address and length.
            RETAIL_2D_MSG(DISP_ZONE_2D,(_T("[G2D] LockPages is Failed : %d\r\n"), GetLastError()));
            FreePhysAddress(m_pdwPhysAddress);
            return NULL;
        }

        m_lpvLockedAddress = pUseBufferPtr;
        m_dwLockedSize = dwLength;

        /// Get each Physical address pages from pagelocked information
        for (DWORD dwIndex = 0; dwIndex< nPages; dwIndex++) 
        {
            if (m_pdwPhysAddress[dwIndex] > MAX_SUPPORTED_PFN) 
            {
                RETAIL_2D_MSG(DISP_ZONE_2D,(_T("[G2D] MAX_SUPPORTED_PFN\r\n")));
                ASSERT(FALSE);
                FreePhysAddress(m_pdwPhysAddress);
                return NULL;  //< NULL is FAIL
            }

            DWORD dwSize = min((PAGE_SIZE - ((DWORD)pUseBufferPtr & PAGE_MASK)),dwLength) ;

            m_pdwPhysAddress[dwIndex] = (m_pdwPhysAddress[dwIndex]<<PFN_SHIEFT) + ((DWORD)pUseBufferPtr & PAGE_MASK);
            m_pdwPhysLength[dwIndex] = dwSize;
            m_ppVirtAddress[dwIndex] = (PBYTE)pUseBufferPtr;
            dwLength -= dwSize;
            pUseBufferPtr = (PBYTE)pUseBufferPtr+dwSize;
            RETAIL_2D_MSG(DISP_ZONE_2D,(_T("[G2D] dwIndex : %d, m_pdwPhysAddress[%d]:0x%x, m_pdwPhysLength[%d]:%d, dwSize:%d, pUseBufferPtr(PageEnd):0x%x.\r\n"),
                dwIndex,
                dwIndex, m_pdwPhysAddress[dwIndex],
                dwIndex, m_pdwPhysLength[dwIndex],
                dwSize,
                pUseBufferPtr));
            
        }

        /// Check if Source Pages is contiguous in Physical memory address.
        DWORD dwRead = 1;
        while (dwRead < nPages) 
        {
            if (m_pdwPhysAddress[dwRead - 1] + m_pdwPhysLength[dwRead - 1] == m_pdwPhysAddress[dwRead]) 
            {
                // m_dwBlocks and m_dwBlocks+1 is contiguous.
                dwRead++;
            }
            else 
            {   // No match, We cannot use HW
                RETAIL_2D_MSG(DISP_ZONE_2D,(_T("[G2D] Source Memory Blocks is not congiuous : Go Emul path\r\n")));
                FreePhysAddress(m_pdwPhysAddress);
                return NULL;    //< NULL is Fail
            }
        }
        // Merge to one big contiguous memory block
        if(nPages > 1)
        {
            for(dwRead = 1 ; dwRead < nPages; dwRead++) 
            {
                m_pdwPhysLength[0] += m_pdwPhysLength[dwRead];
            }
        }
    }
    else
    {
        RETAIL_2D_MSG(DISP_ZONE_2D,(_T("[G2D] Not Enough Memory for m_pdwPhysAddress\r\n")));
        FreePhysAddress(m_pdwPhysAddress);
        return NULL;        //< NULL is Fail
    }

    FreePhysAddress(m_pdwPhysAddress);

    return TRUE;
}


void SMDKDisp::FreePhysAddress(DWORD *m_pdwPhysAddress)
{
    if(m_pdwPhysAddress)
    {
        delete [] m_pdwPhysAddress;
        m_pdwPhysAddress = NULL;
    }
}


void SMDKDisp::ClipDestDrawRect(GPEBltParms *pBltParms)
{
    if(pBltParms->pDst->InVideoMemory())
    {
        if(pBltParms->prclDst->left < 0)
        {
            pBltParms->prclSrc->left -= pBltParms->prclDst->left;
            pBltParms->prclDst->left = 0;

        }
        if(pBltParms->prclDst->top < 0)
        {
            pBltParms->prclSrc->top -= pBltParms->prclDst->top;
            pBltParms->prclDst->top = 0;
        }
    }
}

