//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
// 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:

    surf.cpp

Abstract:

    surface allocation/manipulation/free routines

Functions:


Notes:


--*/

#include "precomp.h"
#ifdef USE_CMM_FOR_YUVSURFACE
#include "cmmapi.h"
#endif

#define ALIGN(x, align)        (((x) + ((align) - 1)) & ~((align) - 1))

#define MAX_PAC_MEM_SIZE    (1024*1024*8)

static DWORD dwSurfaceCount = 0;
static DWORD g_dwTotalPACSize = 0;

SCODE
SMDKDisp::AllocSurfacePACS(
                        GPESurf **ppSurf,
                        int width,
                        int height,
                        EGPEFormat format,
                        int stride,
                        EDDGPEPixelFormat pixelFormat
                        )
{
    // try to allocate physically linear address
    *ppSurf = new PACSurf(width, height, format, stride, pixelFormat);

    if (*ppSurf == NULL)
    {
        RETAIL_DISP_MSG(DISP_ZONE_ERROR,(_T("[DISPDRV:ERR] AllocSurface() : PACSurface allocate Failed -> Try to allocate Normal GPE Surface\n\r")));
        return E_OUTOFMEMORY;
    }
    else if ((*ppSurf)->Buffer() == NULL)
    {
        delete *ppSurf;
        *ppSurf = NULL;

        RETAIL_DISP_MSG(DISP_ZONE_ERROR,(_T("[DISPDRV:ERR] AllocSurface() : PACSurface Buffer is NULL -> Try to allocate Normal GPE Surface\n\r")));
    }
    else        /// PAC Allocation succeeded.
    {
        RETAIL_DISP_MSG(DISP_ZONE_CREATE,(_T("[DISPDRV] AllocSurface() : PACSurf() Allocated in System Memory\n\r")));
        return S_OK;
    }
    return E_FAIL;
}
//  This method is called for all normal surface allocations from ddgpe and gpe
SCODE
SMDKDisp::AllocSurface(
                        GPESurf **ppSurf,
                        int width,
                        int height,
                        EGPEFormat format,
                        int surfaceFlags)
{
    RETAIL_DISP_MSG(DISP_ZONE_CREATE,(_T("[AS] %dx%d FMT:%d F:%08x\n\r"), width, height,  format, surfaceFlags));

    // This method is only for surface in system memory
    if (surfaceFlags & GPE_REQUIRE_VIDEO_MEMORY)
    {
        RETAIL_DISP_MSG(DISP_ZONE_ERROR,(_T("[DISPDRV:ERR] AllocSurface() : Can not allocate GPE_REQUIRE_VIDEO_MEMORY Surface in system memory\n\r")));
        return E_OUTOFMEMORY;
    }

#if USE_VIDEOMEMORY_FORALL_SURFACES
    // This will allocate for all normal surface into videomemory
    SCODE rv = AllocSurfaceVideo(ppSurf, width, height, format);
    if (rv == S_OK)
    {
        return S_OK;
    }
#endif

    if(m_G2DControlArgs.UseHWAccel && m_G2DControlArgs.UsePACSurf)
    {
        /// Only Support 16bpp, 24bpp, 32bpp
        if( format == gpe16Bpp || format == gpe24Bpp || format == gpe32Bpp || format == gpeDeviceCompatible)
        {
            if(width*height*(EGPEFormatToBpp[format] >> 3) > PAC_ALLOCATION_BOUNDARY)
            {
                if(S_OK == AllocSurfacePACS(ppSurf, width, height, format))
                {
                    return S_OK;
                }
            }
        }
    }
    /// if allocation is failed or boundary condition is not met, just create GPESurf in normal system memory that can be non-linear physically.

    // Allocate surface from system memory
    *ppSurf = new GPESurf(width, height, format);

    if (*ppSurf != NULL)
    {
        if (((*ppSurf)->Buffer()) == NULL)
        {
            RETAIL_DISP_MSG(DISP_ZONE_ERROR,(_T("[DISPDRV:ERR] AllocSurface() : Surface Buffer is NULL\n\r")));
            delete *ppSurf;
            return E_OUTOFMEMORY;
        }
        else
        {
            RETAIL_DISP_MSG(DISP_ZONE_CREATE,(_T("[DISPDRV] AllocSurface() : GPESurf() Allocated in System Memory\n\r")));
            return S_OK;
        }
    }

    RETAIL_DISP_MSG(DISP_ZONE_ERROR,(_T("[DISPDRV:ERR] AllocSurface() : Surface allocate Failed\n\r")));
    return E_OUTOFMEMORY;
}


//  This method is used for DirectDraw enabled surfaces
SCODE
SMDKDisp::AllocSurface(
                        DDGPESurf **ppSurf,
                        int width,
                        int height,
                        EGPEFormat format,
                        EDDGPEPixelFormat pixelFormat,
                        int surfaceFlags)
{
    RETAIL_DISP_MSG(DISP_ZONE_CREATE,(_T("[DISPDRV] %s(%d, %d, %d, %d, 0x%08x)\n\r"), _T(__FUNCTION__), width, height, format, pixelFormat, surfaceFlags));

    unsigned int bpp;
    unsigned int stride;
    unsigned int align_width;

    if (pixelFormat == ddgpePixelFormat_I420 || pixelFormat == ddgpePixelFormat_YV12 || pixelFormat == ddgpePixelFormat_NV12)
    {
        // in this case, stride can't be calculated. because of planar format (non-interleaved...)
        bpp = 12;
        align_width = ALIGN(width, 16);
//        align_width = ALIGN(width, 32);
    }
    else if (pixelFormat == ddgpePixelFormat_YVYU || pixelFormat == ddgpePixelFormat_VYUY)
    {
        bpp = 16;
        align_width = width;
    }
    else
    {
        bpp = EGPEFormatToBpp[format];
        align_width = width;
    }

    //DISPDRV_ERR((_T("[AS] %dx%d %dbpp FMT:%d F:%08x\n\r"), width, height, bpp, format, surfaceFlags));

    //--------------------------------------
    // Try to allocate surface from video memory
    //--------------------------------------

    // stride are all 32bit aligned for Video Memory
//    stride = ((bpp * align_width + 31) >> 5) << 2;
    // stride are all 64bit aligned for Video Memory
    stride = ((bpp * align_width + 63) >> 6) << 3;


    if ((surfaceFlags & GPE_REQUIRE_VIDEO_MEMORY)
        || (surfaceFlags & GPE_BACK_BUFFER)
        //|| ((surfaceFlags & GPE_PREFER_VIDEO_MEMORY) && (format == m_pMode->format)))
        || (surfaceFlags & GPE_PREFER_VIDEO_MEMORY) && (format == m_pMode->format))
    {
        SCODE rv = AllocSurfaceVideo(ppSurf, width, height, stride, format, pixelFormat);
        if (rv == S_OK)
        {
            return S_OK;
        }
        else
        {
            if (surfaceFlags & (GPE_REQUIRE_VIDEO_MEMORY|GPE_BACK_BUFFER))
            {
                RETAIL_DISP_MSG(DISP_ZONE_ERROR,(_T("[DISPDRV:ERR] AllocSurface() : AllocSurfaceVideo() failed\n\r")));
                return E_OUTOFMEMORY;
            }
        }
    }

    //--------------------------------------
    // Try to allocate surface from system memory
    //--------------------------------------

    // Use 64bit aligned region
    int surface_size = stride*height*(EGPEFormatToBpp[format] >> 3);

    if(m_G2DControlArgs.UseHWAccel && m_G2DControlArgs.UsePACSurf)
    {
        if(surface_size > PAC_ALLOCATION_BOUNDARY)
        {
            if(S_OK == AllocSurfacePACS((GPESurf**)ppSurf, width, height, format, stride, pixelFormat))
            {
                return S_OK;
            }
        }
        else
        {
            RETAIL_DISP_MSG(DISP_ZONE_WARNING, (_T("[DISPDRV:WARN] %s() : surface size %d < PAC_ALLOCATION_BOUNDARY (%d); HW acceleration disabled.\r\n"),
                _T(__FUNCTION__), surface_size, m_G2DControlArgs.AllocBoundSize));
        }
    }

    //
//    stride = ((bpp * width + 63) >> 6) << 3;

    // if allocation is failed or boundary condition is not met, just create DDGPESurf in normal system memory that can be non-linear physically.
    // Hardware acceleration will be disabled for this surface.
    *ppSurf = new DDGPESurf(width, height, format);

    if (*ppSurf != NULL)
    {
        if (((*ppSurf)->Buffer()) == NULL)
        {
            RETAIL_DISP_MSG(DISP_ZONE_ERROR,(_T("[DISPDRV:ERR] %s() : OUT OF MEMORY\r\n"), _T(__FUNCTION__)));
            delete *ppSurf;
            return E_OUTOFMEMORY;
        }
        else
        {
            RETAIL_DISP_MSG(DISP_ZONE_WARNING,(_T("[DISPDRV] %s() : Allocated SYSTEM surface in discontiguous memory.\r\n"), _T(__FUNCTION__)));
            RETAIL_DISP_MSG(DISP_ZONE_WARNING,(_T("[DISPDRV] %s() : HW acceleration disabled for this surface.\r\n"), _T(__FUNCTION__)));
            return S_OK;
        }
    }

    return S_OK;
}

SCODE
SMDKDisp::AllocSurfaceVideo(
                        DDGPESurf **ppSurf,
                        int width,
                        int height,
                        int stride,
                        EGPEFormat format,
                        EDDGPEPixelFormat pixelFormat)
{
    // align frame buffer size with 4-word unit
//    DWORD dwSize = ALIGN(stride * height, 16);
    DWORD dwSize = ALIGN(stride * height, 32);

#ifdef USE_CMM_FOR_YUVSURFACE
    if(m_hCmmFB != NULL)
    {
        // Try to allocate surface from video memory
        unsigned int virBuf = NULL;
        unsigned int phyBuf = 0;

        switch(pixelFormat)
        {
        case ddgpePixelFormat_I420:
        case ddgpePixelFormat_YV12:
        case ddgpePixelFormat_NV12:
        case ddgpePixelFormat_YUYV:
        case ddgpePixelFormat_YUY2:
        case ddgpePixelFormat_UYVY:
        case ddgpePixelFormat_YVYU:
        case ddgpePixelFormat_VYUY:
            if(AllocCmmMemory((unsigned int)dwSize, CMM_MEMAREA_FOR_M2M, \
                                &phyBuf, &virBuf))
            {
                *ppSurf = new SMDKSurf(width, height,phyBuf, (PVOID)virBuf, \
                                        stride, format, pixelFormat, m_hCmmFB);
                if (*ppSurf  != NULL)
                {
                    return S_OK;
                }
                else
                {
                    RETAIL_DISP_MSG(DISP_ZONE_ERROR,(_T("[DISPDRV:ERR] %s() : Create SMDKSurf() Failed\n\r"), _T(__FUNCTION__)));
                    ReleaseCmmMemory((PBYTE)virBuf);
                    //return E_OUTOFMEMORY;
                }
            }
            break;
        default:
            break;
        }
    }
#endif // USE_CMM_FOR_YUVSURFACE
    // Try to allocate surface from video memory
    SurfaceHeap *pHeap = m_pVideoMemoryHeap->Alloc(dwSize);
    if (pHeap != NULL)
    {
        DWORD dwVideoMemoryOffset = pHeap->Address() - (DWORD)m_VideoMemoryVirtualBase;
        RETAIL_DISP_MSG(DISP_ZONE_CREATE,(_T("[DISPDRV] %s() : Allocated PA = 0x%08x\n\r"), _T(__FUNCTION__), dwVideoMemoryOffset+m_VideoMemoryPhysicalBase));

        *ppSurf = new SMDKSurf(width, height, dwVideoMemoryOffset, (PVOID)pHeap->Address(), stride, format, pixelFormat, pHeap);
        if (*ppSurf  == NULL)
        {
            RETAIL_DISP_MSG(DISP_ZONE_ERROR,(_T("[DISPDRV:ERR] %s() : Create SMDKSurf() Failed\n\r"), _T(__FUNCTION__)));

            pHeap->Free();

            return E_OUTOFMEMORY;
        }

        RETAIL_DISP_MSG(DISP_ZONE_CREATE,(_T("[DISPDRV] %s() Allocated in Video Memory\n\r"), _T(__FUNCTION__)));

        return S_OK;
    }
    else
    {
        RETAIL_DISP_MSG(DISP_ZONE_ERROR,(_T("[DISPDRV:ERR] %s() : SurfaceHeap Alloc() Failed\n\r"), _T(__FUNCTION__)));

        *ppSurf = (DDGPESurf *)NULL;

        return E_OUTOFMEMORY;
    }
}

#if USE_VIDEOMEMORY_FORALL_SURFACES
SCODE
SMDKDisp::AllocSurfaceVideo(GPESurf **ppSurf,
                                    int width,
                                    int height,
                                    EGPEFormat format
                                )
{
    DWORD    dwStrideBytes = ( (EGPEFormatToBpp[ format ] * width + 7 )/ 8 + 3 ) & ~3L;
    DWORD    dwSize = dwStrideBytes * height;
    SurfaceHeap *pHeap;

    pHeap = m_pVideoMemoryHeap->Alloc(dwSize);

    if(pHeap != NULL)
    {
        DWORD dwVidioMemOffset = pHeap->Address() - (DWORD)m_VideoMemoryVirtualBase;
        *ppSurf = new PACSurf(width,height,dwVidioMemOffset,(PVOID)pHeap->Address(),format,pHeap);
        if ((*ppSurf  == NULL) || ((GPESurf*)(*ppSurf)->Buffer() == NULL))
        {
            RETAIL_DISP_MSG(DISP_ZONE_ERROR,(_T("[DISPDRV:ERR] AllocSurfaceVideo() : Create PACSurf() Failed\n\r")));

            pHeap->Free();
            pHeap = NULL;

            if(*ppSurf)
            {
                delete *ppSurf;
            }

            return E_OUTOFMEMORY;
        }
        memset((GPESurf*)(*ppSurf)->Buffer(),0,dwSize);

        RETAIL_DISP_MSG(DISP_ZONE_TEMP,(_T("[DISPDRV] AllocSurfaceVideo() : PACSurf() Allocated in Video Memory\n\r")));

        return S_OK;

    }
    else
    {
        RETAIL_DISP_MSG(DISP_ZONE_ERROR,(_T("[DISPDRV:ERR] AllocSurfaceVideo() : SurfaceHeap Alloc() Failed\n\r")));

        *ppSurf = (GPESurf *)NULL;

        return E_OUTOFMEMORY;
    }
}
#endif


#ifdef USE_CMM_FOR_YUVSURFACE
BOOL
SMDKDisp::AllocCmmMemory
(unsigned int uiSize,
 DWORD Loc,
 unsigned int* puiPhy,
 unsigned int* puiVir)
{
    BOOL bResult = TRUE;

    CMM_ALLOC_PRAM_T CMMParam;
    char *virBuf = NULL;
    unsigned int phyBuf = 0;

    CMMParam.size = uiSize;
    CMMParam.dramLocation = (CMM_DRAM_LOCATION)Loc;       // DRAM 0 or DRAM 1 = 0;
    //RETAIL_DISP_MSG(DISP_ZONE_TEMP,(_T("\r\n\r\n[DISPDRV] %s() : CMM dwSize = %d\r\n"), _T(__FUNCTION__),uiSize));

    bResult = DeviceIoControl(m_hCmmFB, IOCTL_CODEC_MEM_ALLOC, (PBYTE)&CMMParam,
                        sizeof(CMM_ALLOC_PRAM_T *), &virBuf, sizeof(virBuf),
                        NULL, NULL);
    if(bResult == FALSE)
    {
        RETAIL_DISP_MSG(DISP_ZONE_ERROR,(_T("[DISPDRV:ERR] %s() : CMM IOCTL_CODEC_MEM_ALLOC failure\n"), _T(__FUNCTION__)));
        //DeviceIoControl(m_hCmmFB, IOCTL_CODEC_MEM_FREE, (PBYTE)virBuf,
        //                        sizeof(virBuf), NULL, 0, NULL, NULL);
        return bResult;
    }
    //RETAIL_DISP_MSG(DISP_ZONE_TEMP,(_T("[DISPDRV] %s() : CMM virBuf = 0x%x\r\n"), _T(__FUNCTION__),virBuf));

    bResult = DeviceIoControl(m_hCmmFB, IOCTL_CODEC_GET_PHY_ADDR, (PBYTE)virBuf,
                        sizeof(virBuf), &phyBuf, sizeof(phyBuf),
                        NULL, NULL);
    if(bResult == FALSE)
    {
        RETAIL_DISP_MSG(DISP_ZONE_ERROR,(_T("[DISPDRV:ERR] %s() : CMM IOCTL_CODEC_GET_PHY_ADDR failure\n"), _T(__FUNCTION__)));
        ReleaseCmmMemory((PBYTE)virBuf);
        return bResult;
    }

    *puiPhy = (DWORD)phyBuf;
    *puiVir = (DWORD)virBuf;

    //RETAIL_DISP_MSG(DISP_ZONE_TEMP,(_T("[DISPDRV] %s() : CMM phyBuf = 0x%x\r\n"), _T(__FUNCTION__),phyBuf));

    //RETAIL_DISP_MSG(DISP_ZONE_TEMP,(_T("[DISPDRV] %s() : CMM *puiVir = 0x%x\r\n"), _T(__FUNCTION__),*puiVir));
    //RETAIL_DISP_MSG(DISP_ZONE_TEMP,(_T("[DISPDRV] %s() : CMM *puiPhy = 0x%x\r\n\r\n\r\n"), _T(__FUNCTION__),*puiPhy));

    return bResult;
}

void
SMDKDisp::ReleaseCmmMemory
(PBYTE pbVir)
{
    RETAIL_DISP_MSG(DISP_ZONE_ENTER,(_T("[DISPDRV] %s() : CMM pbVir = 0x%x\r\n"), _T(__FUNCTION__),pbVir));

    if(!DeviceIoControl(m_hCmmFB, IOCTL_CODEC_MEM_FREE, pbVir, sizeof(pbVir), \
        NULL, 0, NULL, NULL))
    {
        RETAIL_DISP_MSG(DISP_ZONE_ERROR,(_T("[DISPDRV:ERR] %s() : Cannot Free CMM Memory(0x%x)\r\n"), _T(__FUNCTION__),pbVir));
    }
}
#endif
//-----------------------------------------------------------------------------

#ifdef USE_CMM_FOR_YUVSURFACE
SMDKSurf::SMDKSurf(int width, int height, DWORD offset, VOID *pBits, int stride,
            EGPEFormat format, EDDGPEPixelFormat pixelFormat, HANDLE hCmmFB)
            : DDGPESurf(width, height, pBits, stride, format, pixelFormat)
{
    dwSurfaceCount++;

    m_fInVideoMemory = TRUE;
    m_nOffsetInVideoMemory = offset;
    m_hCmmFB = hCmmFB;
    m_pSurfHeap = NULL;

    if (pixelFormat == ddgpePixelFormat_I420)       // 3Plane
    {
        m_uiOffsetCb = width*height;
        m_uiOffsetCr = m_uiOffsetCb+width*height/4;
    }
    else if (pixelFormat == ddgpePixelFormat_YV12)  // 3Plane
    {
        m_uiOffsetCr = width*height;
        m_uiOffsetCb = m_uiOffsetCr+width*height/4;
    }
    else if (pixelFormat == ddgpePixelFormat_NV12)  // 2Plane
    {
        m_uiOffsetCr = width*height;
        m_uiOffsetCb = m_uiOffsetCr;
    }
    else
    {
        m_uiOffsetCr = 0;
        m_uiOffsetCb = 0;
    }

    RETAIL_DISP_MSG(DISP_ZONE_CREATE,(_T("[DISPDRV:INF] %S() : @ 0x%08x, %d\n\r"), _T(__FUNCTION__), m_nOffsetInVideoMemory, dwSurfaceCount));
}
#endif //USE_CMM_FOR_YUVSURFACE

SMDKSurf::SMDKSurf(int width, int height, DWORD offset, VOID *pBits, int stride,
            EGPEFormat format, EDDGPEPixelFormat pixelFormat, SurfaceHeap *pHeap)
            : DDGPESurf(width, height, pBits, stride, format, pixelFormat)
{
    dwSurfaceCount++;

    m_fInVideoMemory = TRUE;
    m_nOffsetInVideoMemory = offset;
    m_pSurfHeap = pHeap;
#ifdef USE_CMM_FOR_YUVSURFACE
    m_hCmmFB = NULL;
#endif //USE_CMM_FOR_YUVSURFACE

    if (pixelFormat == ddgpePixelFormat_I420)       // 3Plane
    {
        m_uiOffsetCb = width*height;
        m_uiOffsetCr = m_uiOffsetCb+width*height/4;
    }
    else if (pixelFormat == ddgpePixelFormat_YV12)  // 3Plane
    {
        m_uiOffsetCr = width*height;
        m_uiOffsetCb = m_uiOffsetCr+width*height/4;
    }
    else if (pixelFormat == ddgpePixelFormat_NV12)  // 2Plane
    {
        m_uiOffsetCr = width*height;
        m_uiOffsetCb = m_uiOffsetCr;
    }
    else
    {
        m_uiOffsetCr = 0;
        m_uiOffsetCb = 0;
    }

    RETAIL_DISP_MSG(DISP_ZONE_CREATE,(_T("[DISPDRV:INF] %S() : @ 0x%08x, %d\n\r"), _T(__FUNCTION__), m_nOffsetInVideoMemory, dwSurfaceCount));
}

SMDKSurf::~SMDKSurf()
{
    dwSurfaceCount--;

#ifdef USE_CMM_FOR_YUVSURFACE
    if(m_hCmmFB != NULL)
    {
        if(!DeviceIoControl(m_hCmmFB, IOCTL_CODEC_MEM_FREE, (PBYTE)m_pVirtAddr, \
                            sizeof(m_pVirtAddr), NULL, 0, NULL, NULL))
        {
            RETAIL_DISP_MSG(DISP_ZONE_ERROR,(_T("[DISPDRV:ERR] %s() : Cannot Free CMM Memory(0x%x)\r\n"), _T(__FUNCTION__),m_pVirtAddr));
        }
    }
#endif //USE_CMM_FOR_YUVSURFACE

    if(m_pSurfHeap)
    {
        RETAIL_DISP_MSG(DISP_ZONE_CREATE,(_T("[DISPDRV:INF] %s() : Heap 0x%08x Addr:0x%x, Avail:%d, Size:%d\n\r"),
        m_pSurfHeap, m_pSurfHeap->Address(), m_pSurfHeap->Available(), m_pSurfHeap->Size()));

        m_pSurfHeap->Free();
        RETAIL_DISP_MSG(DISP_ZONE_CREATE,(_T("[DISPDRV:INF] %s() : @ 0x%08x, %d\n\r"), _T(__FUNCTION__), m_nOffsetInVideoMemory, dwSurfaceCount));
    }
#ifndef USE_CMM_FOR_YUVSURFACE
    else
    {
        RETAIL_DISP_MSG(DISP_ZONE_ERROR, (_T("ERROR, Invalid SurfaceHeap Address")));
    }
#else

    if((m_hCmmFB == NULL) & !m_pSurfHeap)
    {
        RETAIL_DISP_MSG(DISP_ZONE_ERROR, (_T("ERROR, Invalid SurfaceHeap Address")));
    }
#endif //USE_CMM_FOR_YUVSURFACE
}


/**
*    @class    PACSurf
*    @desc    This Surface will try to allocate physically linear address
*
**/
/**
*    @fn       PACSurf::PACSurf
*    @brief    try to allocate memory region that is physically linear
*    @param    GPESurf **ppSurf, INT width, INT height, EGPEFormat format, int surfaceFlags
*    @sa       GPESurf
*    @note     This Surface format is compatible to GPESurf
**/
PACSurf::PACSurf(int width, int height, EGPEFormat format, int stride, EDDGPEPixelFormat pixelFormat)
    : DDGPESurf( width, height, NULL, stride, format, pixelFormat )
{
    RETAIL_DISP_MSG(DISP_ZONE_CREATE, (_T("[DISPDRV]  %s(%dx%d, %d, FMT:%d, PFMT:%08x)\r\n"), _T(__FUNCTION__), width, height, stride, format, pixelFormat));

    // Even though "width" and "height" are int's, they must be positive.
    ASSERT(width > 0);
    ASSERT(height > 0);

    memset( &m_Format, 0, sizeof ( m_Format ) );

    m_pPhysAddr            = NULL;
    m_pVirtAddr            = NULL;
    m_pKernelVirtAddr      = NULL;
    m_hUserProcess         = NULL;
    m_nStrideBytes         = 0;
    m_eFormat              = gpeUndefined;
    m_fInVideoMemory       = 0;
    m_fInUserMemory        = FALSE;
    m_fOwnsBuffer          = 0;
    m_nWidth               = 0;
    m_nHeight              = 0;
    m_nOffsetInVideoMemory = 0;
    m_iRotate              = DMDO_0;
    m_ScreenWidth          = 0;
    m_ScreenHeight         = 0;
    m_BytesPixel           = 0;
    m_nHandle              = NULL;


    if (width > 0 && height > 0)
    {
        m_nWidth               = width;
        m_nHeight              = height;
        m_eFormat              = format;
        m_nStrideBytes         = ( (EGPEFormatToBpp[ format ] * width + 7 )/ 8 + 3 ) & ~3L;
        m_fOwnsBuffer          = 1;
        m_BytesPixel           = EGPEFormatToBpp[m_eFormat] >> 3;

        if(stride == 0 && pixelFormat == ddgpePixelFormat_UnknownFormat)
        {
            // Ensure the PACSurf object will be cleaned up, and a 2nd allocation from system memory attempted.
            m_pVirtAddr = m_pKernelVirtAddr = m_pPhysAddr = NULL;
            return;
        }
#if USE_VIDEOMEMORY_FORALL_SURFACES
        if(g_dwTotalPACSize >= MAX_PAC_MEM_SIZE)
        {
            return;
        }
#endif
        m_dwSurfaceSize     = m_nStrideBytes * height;

        // try to allocate physically linear address
        m_pKernelVirtAddr  = (ADDRESS) AllocPhysMem( m_dwSurfaceSize, PAGE_READWRITE, 0, 0, &m_pPhysAddr );

        if(m_pKernelVirtAddr != NULL)
        {
            m_fPLAllocated = 1;
#if (_WIN32_WCE < 600)
            m_pVirtAddr = m_pKernelVirtAddr;
#else
            if (GetCurrentProcessId() != GetDirectCallerProcessId())
            {
                // Map it into the caller's process so they can access the pixel data.
                m_hUserProcess = (HANDLE)GetDirectCallerProcessId();
                // This is safe because m_pVirtAddr is page-aligned and a multiple of pages
                // So we're mapping no more and no less than we intend into the caller's process
                m_pVirtAddr = (ADDRESS)VirtualAllocCopyEx(
                    (HANDLE)GetCurrentProcessId(),
                    m_hUserProcess,
                    (LPVOID)m_pKernelVirtAddr,
                    m_dwSurfaceSize,
                    PAGE_READWRITE);

                if (NULL == m_pVirtAddr)
                {
                    RETAIL_DISP_MSG(DISP_ZONE_ERROR,(TEXT("[DISPDRV:ERR] Mapping PAC Surf to caller process failed: 0x%x\r\n"),
                        GetLastError()));

                    FreePhysMem( (LPVOID)m_pKernelVirtAddr );
                    // Ensure the PACSurf object will be cleaned up, and a 2nd allocation from system memory attempted.
                    m_pVirtAddr = m_pKernelVirtAddr = m_pPhysAddr = NULL;
                    return;
                }
            }
            else
            {
                m_pVirtAddr = m_pKernelVirtAddr;
            }
#endif
            RETAIL_DISP_MSG(DISP_ZONE_CREATE,(TEXT("\n%s(): size : %d, PAC Surf PA Base : 0x%x KVA Base : 0x%x UVA Base : 0x%x STRIDE : %d, PL:%d, Own:%d"),
                _T(__FUNCTION__), m_dwSurfaceSize, m_pPhysAddr, m_pKernelVirtAddr, m_pVirtAddr, m_nStrideBytes, m_fPLAllocated, m_fOwnsBuffer));

        }
        else
        {
            // Ensure the PACSurf object will be cleaned up, and a 2nd allocation from system memory attempted.
            m_pVirtAddr = m_pKernelVirtAddr = m_pPhysAddr = NULL;

            RETAIL_DISP_MSG(DISP_ZONE_WARNING,(TEXT("[DISPDRV:ERR] PAC Surf: AllocPhysMem() failed.  Will alloc from (potentially discontiguous) RAM.")));
            return;
        }
    }
}

PACSurf::~PACSurf()
{
    SMDKDisp    *pDDGPE;
    pDDGPE = (SMDKDisp *)GetDDGPE();
    if (pDDGPE)
    {
        // If this surface was being used in a H/W blit, let's make sure it is done
        pDDGPE->WaitForNotBusy();
    }
    if(m_fInVideoMemory)
    {
        if(m_fOwnsBuffer && m_pVirtAddr)
        {
            if( STATUS_SUCCESS != FreePhysMem((LPVOID)m_pVirtAddr) )
            {
                RETAIL_DISP_MSG(DISP_ZONE_ERROR,(TEXT("PACSurface deallocation is failed:m_pVirtAddr:0x%08x\r\n"), m_pVirtAddr));
            }
            m_pVirtAddr = NULL;
            m_fOwnsBuffer = 0;
        }

        RETAIL_DISP_MSG(DISP_ZONE_2D,(TEXT("PACSurface deallocation is succeeded\r\n")));

        if(NULL != m_pSurfHeap)
        {
#if USE_VIDEOMEMORY_FORALL_SURFACES
            g_dwTotalPACSize -= m_pSurfHeap->NodeSize();
#endif
            m_pSurfHeap->Free();
            m_pSurfHeap = NULL;
        }

        #ifdef SHOW_DISP_MEM
        g_nTotalSurf--;
        #endif
    }
    else
    {
        if(m_fPLAllocated)
        {
            // For System Memory Allocation
            // Unmap the pixel buffer from caller's address space, if it was mapped
#if (_WIN32_WCE >= 600)
            if ( m_hUserProcess == (HPROCESS)GetDirectCallerProcessId() )
            {
                if (!VirtualFreeEx( m_hUserProcess, (LPVOID)m_pVirtAddr, 0, MEM_RELEASE ))
                {
                    RETAIL_DISP_MSG(DISP_ZONE_ERROR,(TEXT("\nPACSurface unmap from caller process failed\r\n")));
                    ASSERT( 0 );
                }
                RETAIL_DISP_MSG(FALSE,(TEXT("\nPACSurface unmap from caller process is succeed: m_pVirtAddr:0x%x\r\n"), m_pVirtAddr));
                m_pVirtAddr = NULL;
            }
#endif

            // Free the physical memory
            if(m_pKernelVirtAddr)
            {
                if ( !FreePhysMem((LPVOID)m_pKernelVirtAddr) )
                {
                    RETAIL_DISP_MSG(DISP_ZONE_ERROR,(TEXT("\nPACSurface deallocation is failed:0x%08x, Err=0x%08x\r\n"), m_pKernelVirtAddr, GetLastError()));
                }
                else
                {
                    RETAIL_DISP_MSG(DISP_ZONE_CREATE,(TEXT("\n%s(): size: %d, VA: 0x%08x, PA: 0x%08x\r\n"),
                        _T(__FUNCTION__), m_dwSurfaceSize, m_pKernelVirtAddr, m_pPhysAddr));
                    RETAIL_DISP_MSG(FALSE,(TEXT("\n%s(): size: %d, VA: 0x%08x, PA: 0x%08x\r\n"),
                        _T(__FUNCTION__), m_dwSurfaceSize, m_pKernelVirtAddr, m_pPhysAddr));

                    m_pVirtAddr = m_pKernelVirtAddr = NULL;
                    m_fOwnsBuffer = 0;
                }
            }
        }
        else
        {
            if( m_fOwnsBuffer )
            {
                if( m_pVirtAddr )
                {
//                    delete [] (void *)m_pVirtAddr;
                    delete [] (ADDRESS *)m_pVirtAddr;
                }
            }
        }
    }
}
#if USE_VIDEOMEMORY_FORALL_SURFACES
PACSurf::PACSurf(
        int                    width,
        int                    height,
        DWORD                dwOffset,
        void *                pBits,            // virtual address of allocated bits
        EGPEFormat            format,
        SurfaceHeap *pHeap
        )
{
    DEBUGMSG(0, (_T("[DISPDRV] PACSurf Constructor(%d, %d, %d, 0x%08x)\n\r"), width, height, format));

    // Even though "width" and "height" are int's, they must be positive.
    ASSERT(width > 0);
    ASSERT(height > 0);

    memset( &m_Format, 0, sizeof ( m_Format ) );

    m_pVirtAddr            = NULL;
    m_nStrideBytes         = 0;
    m_eFormat              = gpeUndefined;
    m_fInVideoMemory       = 0;
    m_fInUserMemory        = FALSE;
    m_fOwnsBuffer          = 0;
    m_nWidth               = 0;
    m_nHeight              = 0;
    m_nOffsetInVideoMemory = 0;
    m_iRotate              = DMDO_0;
    m_ScreenWidth          = 0;
    m_ScreenHeight         = 0;
    m_BytesPixel           = 0;
    m_nHandle              = NULL;
    m_fPLAllocated         = 0;
    m_pSurfHeap            = NULL;

    // This will just wrap the already allocated video memory.
    if (width > 0 && height > 0 && pHeap != NULL)
    {
        m_nWidth               = width;
        m_nHeight              = height;
        m_eFormat              = format;
        m_nStrideBytes         = ( (EGPEFormatToBpp[ format ] * width + 7 )/ 8 + 3 ) & ~3L;
        m_pVirtAddr  = (ADDRESS)pBits;
        if(m_pVirtAddr != NULL)
        {
            m_fInVideoMemory = 1;
            m_fOwnsBuffer    = 1;
            m_nOffsetInVideoMemory = dwOffset;
            m_pSurfHeap = pHeap;
            m_BytesPixel      = EGPEFormatToBpp[m_eFormat] >> 3;
            g_dwTotalPACSize += m_pSurfHeap->NodeSize();
        }
    }

    #ifdef SHOW_DISP_MEM
    g_nTotalSurf++;
    #endif

}
#endif

//-----------------------------------------------------------------------------

