/*
* Copyright (c) 2009-2022, Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
//!
//! \file      media_libva_util.cpp
//! \brief     libva(and its extension) utility
//!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dlfcn.h>
#include <errno.h>
#include "inttypes.h"

#include "media_libva_util.h"
#include "mos_utilities.h"
#include "mos_os.h"
#include "mos_defs.h"
#include "hwinfo_linux.h"
#include "media_ddi_decode_base.h"
#include "media_ddi_encode_base.h"
#include "media_libva_decoder.h"
#include "media_libva_encoder.h"
#include "media_libva_caps.h"
#include "memory_policy_manager.h"
#include "drm_fourcc.h"

// default protected surface tag
#define PROTECTED_SURFACE_TAG   0x3000f

#ifdef DEBUG
static int32_t         frameCountFps   = -1;
static struct timeval  tv1;
static pthread_mutex_t fpsMutex        = PTHREAD_MUTEX_INITIALIZER;
static int32_t         vaFpsSampleSize = 100;

#define LENGTH_OF_FPS_FILE_NAME 128

#ifdef ANDROID
#define FPS_FILE_NAME   "/mnt/sdcard/fps.txt"
#else
#define FPS_FILE_NAME   "./fps.txt"
#endif
#endif
#ifdef DEBUG
void DdiMediaUtil_MediaPrintFps()
{
    struct timeval tv2;

    if (0 == vaFpsSampleSize)
    {
        return;
    }
    gettimeofday(&tv2, 0);

    pthread_mutex_lock(&fpsMutex);
    if (-1 == frameCountFps)
    {
        gettimeofday(&tv1, 0);
    }

    if (++frameCountFps >= vaFpsSampleSize)
    {
        char   fpsFileName[LENGTH_OF_FPS_FILE_NAME];
        FILE   *fp = nullptr;
        char   temp[LENGTH_OF_FPS_FILE_NAME];

        int64_t diff  = (tv2.tv_sec - tv1.tv_sec)*1000000 + tv2.tv_usec - tv1.tv_usec;
        float fps     = frameCountFps / (diff / 1000000.0);
        DDI_NORMALMESSAGE("FPS:%6.4f, Interval:%11lu.", fps,((uint64_t)tv2.tv_sec)*1000 + (tv2.tv_usec/1000));
        sprintf(temp,"FPS:%6.4f, Interval:%" PRIu64"\n", fps,((uint64_t)tv2.tv_sec)*1000 + (tv2.tv_usec/1000));

        MOS_ZeroMemory(fpsFileName,LENGTH_OF_FPS_FILE_NAME);
        sprintf(fpsFileName, FPS_FILE_NAME);
        if ((fp = fopen(fpsFileName, "wb")) == nullptr)
        {
            pthread_mutex_unlock(&fpsMutex);
            DDI_ASSERTMESSAGE("Unable to open fps file.");
        }

        fwrite(temp, 1, strlen(temp), fp);
        fclose(fp);
        frameCountFps = -1;
    }
    pthread_mutex_unlock(&fpsMutex);
}
#else
void DdiMediaUtil_MediaPrintFps()
{
    return;
}
#endif

/*
 * DdiMediaUtil_IsExternalSurface
 *    Descripion: if the bo of media surface was allocated from App,
 *                should return true, otherwise, false. In current implemeation
 *                external buffer passed with pSurfDesc.
*/
bool DdiMediaUtil_IsExternalSurface(PDDI_MEDIA_SURFACE surface)
{
    if ( nullptr == surface )
    {
        return false;
    }
    else if ( surface->pSurfDesc == nullptr )
    {
        return false;
    }
    else
    {
        if (surface->pSurfDesc->uiVaMemType == VA_SURFACE_ATTRIB_MEM_TYPE_KERNEL_DRM ||
            surface->pSurfDesc->uiVaMemType == VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME ||
            surface->pSurfDesc->uiVaMemType == VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2 ||
#if VA_CHECK_VERSION(1, 21, 0)
            surface->pSurfDesc->uiVaMemType == VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_3 ||
#endif
            surface->pSurfDesc->uiVaMemType == VA_SURFACE_ATTRIB_MEM_TYPE_USER_PTR)
        {
            return true;
        }else
        {
            return false;
        }
    }
}

//!
//! \brief  Allocate surface
//!
//! \param  [in] format
//!         Ddi media format
//! \param  [in] width
//!         Width of the region
//! \param  [in] height
//!         Height of the region
//! \param  [out] mediaSurface
//!         Pointer to ddi media surface
//! \param  [in] mediaDrvCtx
//!         Pointer to ddi media context
//!
//! \return VAStatus
//!     VA_STATUS_SUCCESS if success, else fail reason
//!
VAStatus DdiMediaUtil_AllocateSurface(
    DDI_MEDIA_FORMAT            format,
    int32_t                     width,
    int32_t                     height,
    PDDI_MEDIA_SURFACE          mediaSurface,
    PDDI_MEDIA_CONTEXT          mediaDrvCtx)
{
    uint32_t                    pitch = 0;
    MOS_LINUX_BO               *bo = nullptr;
    GMM_RESCREATE_PARAMS        gmmParams;
    GMM_RESCREATE_CUSTOM_PARAMS_2 gmmCustomParams;
    GMM_RESOURCE_INFO          *gmmResourceInfo = nullptr;

    DDI_CHK_NULL(mediaSurface, "mediaSurface is nullptr", VA_STATUS_ERROR_INVALID_BUFFER);
    DDI_CHK_NULL(mediaDrvCtx, "mediaDrvCtx is nullptr", VA_STATUS_ERROR_INVALID_BUFFER);
    DDI_CHK_NULL(mediaDrvCtx->pGmmClientContext, "mediaDrvCtx->pGmmClientContext is nullptr", VA_STATUS_ERROR_INVALID_BUFFER);

    int32_t size          = 0;
    uint32_t tileformat   = TILING_NONE;
    VAStatus hRes         = VA_STATUS_SUCCESS;
    int32_t alignedWidth  = width;
    int32_t alignedHeight = height;
    uint32_t cpTag        = 0;
    int mem_type          = mediaSurface->memType;
#ifdef _MMC_SUPPORTED
    bool bMemCompEnable   = true;
#else
    bool bMemCompEnable   = false;
#endif
    bool bMemCompRC       = false;

    switch (format)
    {
        case Media_Format_X8R8G8B8:
        case Media_Format_X8B8G8R8:
        case Media_Format_A8B8G8R8:
        case Media_Format_R8G8B8A8:
        case Media_Format_R5G6B5:
        case Media_Format_R8G8B8:
        case Media_Format_R10G10B10A2:
        case Media_Format_B10G10R10A2:
        case Media_Format_R10G10B10X2:
        case Media_Format_B10G10R10X2:
        case Media_Format_A16R16G16B16:
        case Media_Format_A16B16G16R16:
            if (VA_SURFACE_ATTRIB_USAGE_HINT_ENCODER != mediaSurface->surfaceUsageHint     &&
                !(mediaSurface->surfaceUsageHint & VA_SURFACE_ATTRIB_USAGE_HINT_VPP_WRITE))
            {
                tileformat = TILING_NONE;
                break;
            }
        case Media_Format_YV12:
        case Media_Format_I420:
        case Media_Format_IYUV:
            if (VA_SURFACE_ATTRIB_USAGE_HINT_ENCODER != mediaSurface->surfaceUsageHint   &&
                !(mediaSurface->surfaceUsageHint & VA_SURFACE_ATTRIB_USAGE_HINT_VPP_WRITE))
            {
                tileformat = TILING_NONE;
                alignedWidth = MOS_ALIGN_CEIL(width, 2);
                alignedHeight = MOS_ALIGN_CEIL(height, 2);
                break;
            }

        case Media_Format_RGBP:
        case Media_Format_BGRP:
            if (VA_SURFACE_ATTRIB_USAGE_HINT_ENCODER != mediaSurface->surfaceUsageHint   &&
                !(mediaSurface->surfaceUsageHint & VA_SURFACE_ATTRIB_USAGE_HINT_DECODER) &&
                !(mediaSurface->surfaceUsageHint & VA_SURFACE_ATTRIB_USAGE_HINT_VPP_WRITE))
            {
                tileformat = TILING_NONE;
                break;
            }
        case Media_Format_A8R8G8B8:
            if (VA_SURFACE_ATTRIB_USAGE_HINT_ENCODER != mediaSurface->surfaceUsageHint     &&
                !(mediaSurface->surfaceUsageHint & VA_SURFACE_ATTRIB_USAGE_HINT_DECODER)   &&
                !(mediaSurface->surfaceUsageHint & VA_SURFACE_ATTRIB_USAGE_HINT_VPP_WRITE) &&
                !(MEDIA_IS_SKU(&mediaDrvCtx->SkuTable, FtrRenderCompressionOnly)           &&
                  MEDIA_IS_SKU(&mediaDrvCtx->SkuTable, FtrE2ECompression)))
            {
                tileformat = TILING_NONE;
                break;
            }
        case Media_Format_NV12:
        case Media_Format_NV21:
        case Media_Format_444P:
        case Media_Format_422H:
        case Media_Format_411P:
        case Media_Format_422V:
        case Media_Format_IMC3:
        case Media_Format_400P:
        case Media_Format_P010:
        case Media_Format_P012:
        case Media_Format_P016:
        case Media_Format_YUY2:
        case Media_Format_Y210:
#if VA_CHECK_VERSION(1, 9, 0)
        case Media_Format_Y212:
#endif
        case Media_Format_Y216:
        case Media_Format_AYUV:
#if VA_CHECK_VERSION(1, 13, 0)
        case Media_Format_XYUV:
#endif
        case Media_Format_Y410:
#if VA_CHECK_VERSION(1, 9, 0)
        case Media_Format_Y412:
#endif
        case Media_Format_Y416:
        case Media_Format_Y8:
        case Media_Format_Y16S:
        case Media_Format_Y16U:
        case Media_Format_VYUY:
        case Media_Format_YVYU:
        case Media_Format_UYVY:
            if (VA_SURFACE_ATTRIB_USAGE_HINT_ENCODER != mediaSurface->surfaceUsageHint &&
                !(mediaSurface->surfaceUsageHint & VA_SURFACE_ATTRIB_USAGE_HINT_VPP_WRITE))
            {
#if UFO_GRALLOC_NEW_FORMAT
                //Planar type surface align 64 to improve performance.
                alignedHeight = MOS_ALIGN_CEIL(height, 64);
#else
                //Planar type surface align 32 to improve performance.
                alignedHeight = MOS_ALIGN_CEIL(height, 32);
#endif
            }
            alignedWidth = MOS_ALIGN_CEIL(width, 8);
            if (mediaSurface->surfaceUsageHint & VA_SURFACE_ATTRIB_USAGE_HINT_VPP_WRITE)
            {
                if ((format == Media_Format_YV12) ||
                    (format == Media_Format_I420))
                {
                    alignedWidth = MOS_ALIGN_CEIL(width, 128);
                }

                if ((format == Media_Format_NV12) ||
                    (format == Media_Format_P010) ||
                    (format == Media_Format_RGBP) ||
                    (format == Media_Format_BGRP))
                {
#if UFO_GRALLOC_NEW_FORMAT
                    //Planar type surface align 64 to improve performance.
                    alignedHeight = MOS_ALIGN_CEIL(height, 64);
#else
                    //Planar type surface align 32 to improve performance.
                    alignedHeight = MOS_ALIGN_CEIL(height, 32);
#endif
                }
            }
            tileformat = TILING_Y;
            break;
        case Media_Format_Buffer:
            tileformat = TILING_NONE;
            break;
        default:
            DDI_ASSERTMESSAGE("Unsupported format");
            hRes = VA_STATUS_ERROR_UNSUPPORTED_RT_FORMAT;
            goto finish;
    }
    //different alignment requirement for different codec and different platform
    //for MPEG2 and AVC , it should be 16
    //for HEVC on the platform pre-gen12 (include), it is 64
    //may need api change for to convey different codec usage.
    if(VA_SURFACE_ATTRIB_USAGE_HINT_ENCODER & mediaSurface->surfaceUsageHint)
    {
        alignedWidth = MOS_ALIGN_CEIL(alignedWidth, 16);
        alignedHeight = MOS_ALIGN_CEIL(alignedHeight, 16);
    }

    if (DdiMediaUtil_IsExternalSurface(mediaSurface))
    {
        // Default set as compression not supported, surface compression import only support from Memory Type VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2 or VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_3
        bMemCompEnable   = false;
        bMemCompRC       = false;
        pitch            = mediaSurface->pSurfDesc->uiPitches[0];
        DDI_CHK_CONDITION(pitch == 0, "Invalid pich.", VA_STATUS_ERROR_INVALID_PARAMETER);
        // DRM buffer allocated by Application, No need to re-allocate new DRM buffer
        if ((mediaSurface->pSurfDesc->uiVaMemType == VA_SURFACE_ATTRIB_MEM_TYPE_KERNEL_DRM) ||
            (mediaSurface->pSurfDesc->uiVaMemType == VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME))
        {
            if (mediaSurface->pSurfDesc->uiVaMemType == VA_SURFACE_ATTRIB_MEM_TYPE_KERNEL_DRM)
            {
                bo = mos_bo_create_from_name(mediaDrvCtx->pDrmBufMgr, "MEDIA", mediaSurface->pSurfDesc->ulBuffer);
            }
            else
            {
                struct mos_drm_bo_alloc_prime alloc_prime;
                alloc_prime.name = "prime";
                alloc_prime.prime_fd = mediaSurface->pSurfDesc->ulBuffer;
                alloc_prime.size = mediaSurface->pSurfDesc->uiSize;
                alloc_prime.pat_index = PAT_INDEX_INVALID; //Setting with default pat index 0 in legacy ddi
                bo = mos_bo_create_from_prime(mediaDrvCtx->pDrmBufMgr, &alloc_prime);
            }

            if (bo != nullptr)
            {
                uint32_t swizzle_mode;
                //Overwrite the tile format that matches the exteral buffer
                mos_bo_get_tiling(bo, &tileformat, &swizzle_mode);
                if(tileformat == 0)
                {
                    tileformat = mediaSurface->pSurfDesc->uiFlags & VA_SURFACE_EXTBUF_DESC_ENABLE_TILING? TILING_Y:TILING_NONE;
                }
            }
            else
            {
                DDI_ASSERTMESSAGE("Failed to create drm buffer object according to input buffer descriptor.");
                return VA_STATUS_ERROR_ALLOCATION_FAILED;
            }
        }
        else if (
#if VA_CHECK_VERSION(1, 21, 0)
                mediaSurface->pSurfDesc->uiVaMemType == VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_3 ||
#endif
                mediaSurface->pSurfDesc->uiVaMemType == VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2)
        {
            struct mos_drm_bo_alloc_prime alloc_prime;
            alloc_prime.name = "prime";
            alloc_prime.prime_fd = mediaSurface->pSurfDesc->ulBuffer;
            alloc_prime.size = mediaSurface->pSurfDesc->uiSize;
            alloc_prime.pat_index = PAT_INDEX_INVALID; //Setting with default pat index 0 in legacy ddi
            bo = mos_bo_create_from_prime(mediaDrvCtx->pDrmBufMgr, &alloc_prime);
            if( bo != nullptr )
            {
                pitch = mediaSurface->pSurfDesc->uiPitches[0];

                DDI_CHK_NULL(mediaDrvCtx->m_caps, "nullptr m_caps", VA_STATUS_ERROR_INVALID_CONTEXT);
                if (VA_STATUS_SUCCESS != mediaDrvCtx->m_caps->SetExternalSurfaceTileFormat(mediaSurface, tileformat, bMemCompEnable, bMemCompRC)) {
                    DDI_ASSERTMESSAGE("Unsupported modifier.");
                    hRes = VA_STATUS_ERROR_INVALID_PARAMETER;
                    goto finish;
                }
            }
            else
            {
                DDI_ASSERTMESSAGE("Failed to create drm buffer object according to input buffer descriptor.");
                return VA_STATUS_ERROR_ALLOCATION_FAILED;
            }
        }
        else if( mediaSurface->pSurfDesc->uiVaMemType == VA_SURFACE_ATTRIB_MEM_TYPE_USER_PTR )
        {
            struct mos_drm_bo_alloc_userptr alloc_uptr;
            alloc_uptr.name = "SysSurface";
            alloc_uptr.addr = (void *)mediaSurface->pSurfDesc->ulBuffer;
            alloc_uptr.tiling_mode = mediaSurface->pSurfDesc->uiTile;
            alloc_uptr.stride = pitch;
            alloc_uptr.size = mediaSurface->pSurfDesc->uiBuffserSize;

            bo = mos_bo_alloc_userptr( mediaDrvCtx->pDrmBufMgr, &alloc_uptr);

            if (bo != nullptr)
            {
                uint32_t swizzle_mode;
                //Overwrite the tile format that matches the exteral buffer
                mos_bo_get_tiling(bo, &tileformat, &swizzle_mode);
            }
            else
            {
                DDI_ASSERTMESSAGE("Failed to create drm buffer vmap.");
                return VA_STATUS_ERROR_ALLOCATION_FAILED;
            }
        }
        else
        {
            DDI_ASSERTMESSAGE("Unsupported external surface memory type.");
            return VA_STATUS_ERROR_ALLOCATION_FAILED;
        }

        // Set cp flag to indicate the secure surface
        if (mediaSurface->pSurfDesc->uiFlags & VA_SURFACE_EXTBUF_DESC_PROTECTED)
        {
            cpTag = PROTECTED_SURFACE_TAG;
        }

        int32_t baseHeight = 0;
        DDI_CHK_CONDITION(mediaSurface->pSurfDesc->uiPlanes == 0,
            "Invalid plane number.",
            VA_STATUS_ERROR_INVALID_PARAMETER);
        if (mediaSurface->pSurfDesc->uiPlanes == 1)
        {
            DDI_CHK_CONDITION(mediaSurface->pSurfDesc->uiSize == 0,
                "Invalid Size.",
                VA_STATUS_ERROR_INVALID_PARAMETER);
            baseHeight = mediaSurface->pSurfDesc->uiSize / pitch;
        }
        else
        {
            DDI_CHK_CONDITION(mediaSurface->pSurfDesc->uiOffsets[1] == 0,
                "Invalid offset.",
                VA_STATUS_ERROR_INVALID_PARAMETER);
            baseHeight = mediaSurface->pSurfDesc->uiOffsets[1] / pitch;
        }
        // Create GmmResourceInfo
        MOS_ZeroMemory(&gmmCustomParams, sizeof(gmmCustomParams));
        gmmCustomParams.Type          = RESOURCE_2D;
        gmmCustomParams.Format        = mediaDrvCtx->m_caps->ConvertMediaFmtToGmmFmt(format);
        if ((format == Media_Format_YV12) || \
            (format == Media_Format_I420) || \
            (format == Media_Format_IYUV) || \
            (format == Media_Format_NV12) || \
            (format == Media_Format_NV21)) {
            // Align width to 2 for specific planar formats to handle
            // odd dimensions for external non-compressible surfaces
            gmmCustomParams.BaseWidth64 = MOS_ALIGN_CEIL(width, 2);
        } else {
            gmmCustomParams.BaseWidth64 = width;
        }
        gmmCustomParams.BaseHeight    = baseHeight;
        gmmCustomParams.Pitch         = pitch;
        gmmCustomParams.Size          = mediaSurface->pSurfDesc->uiSize;
        gmmCustomParams.BaseAlignment = 4096;
        gmmCustomParams.NoOfPlanes    = mediaSurface->pSurfDesc->uiPlanes;
        gmmCustomParams.CpTag         = cpTag;
        switch (tileformat)
        {
            case TILING_Y:
                gmmCustomParams.Flags.Info.TiledY = true;
                gmmCustomParams.Flags.Gpu.MMC    = false;
                if (MEDIA_IS_SKU(&mediaDrvCtx->SkuTable, FtrE2ECompression) &&
                    (!MEDIA_IS_WA(&mediaDrvCtx->WaTable, WaDisableVPMmc)    &&
                    !MEDIA_IS_WA(&mediaDrvCtx->WaTable, WaDisableCodecMmc)) &&
                    bMemCompEnable)
                {
                    gmmCustomParams.Flags.Gpu.MMC               = true;
                    gmmCustomParams.Flags.Info.MediaCompressed  = 1;
                    gmmCustomParams.Flags.Info.RenderCompressed = 0;
                    gmmCustomParams.Flags.Gpu.CCS               = 1;
                    gmmCustomParams.Flags.Gpu.RenderTarget      = 1;
                    gmmCustomParams.Flags.Gpu.UnifiedAuxSurface = 1;

                    if (bMemCompRC)
                    {
                        gmmCustomParams.Flags.Info.MediaCompressed  = 0;
                        gmmCustomParams.Flags.Info.RenderCompressed = 1;
                    }

                    if(MEDIA_IS_SKU(&mediaDrvCtx->SkuTable, FtrRenderCompressionOnly))
                    {
                        gmmCustomParams.Flags.Info.MediaCompressed = 0;

                        if (format == Media_Format_X8R8G8B8 ||
                            format == Media_Format_X8B8G8R8 ||
                            format == Media_Format_A8B8G8R8 ||
                            format == Media_Format_A8R8G8B8 ||
                            format == Media_Format_R8G8B8A8)
                        {
                            gmmCustomParams.Flags.Info.MediaCompressed  = 0;
                            gmmCustomParams.Flags.Info.RenderCompressed = 1;
                        }
                    }

                    if(MEDIA_IS_SKU(&mediaDrvCtx->SkuTable, FtrFlatPhysCCS))
                    {
                        gmmCustomParams.Flags.Gpu.UnifiedAuxSurface = 0;
                    }
                }
                break;
            case TILING_X:
                gmmCustomParams.Flags.Info.TiledX = true;
                break;
            case TILING_NONE:
            default:
                gmmCustomParams.Flags.Info.Linear = true;
        }

        if(bMemCompEnable)
        {
            gmmCustomParams.AuxSurf.BaseAlignment = {0};
            gmmCustomParams.NoOfPlanes = mediaSurface->pSurfDesc->uiPlanes/2;
            gmmCustomParams.Size = (gmmCustomParams.NoOfPlanes == 1) ? mediaSurface->pSurfDesc->uiOffsets[1]:mediaSurface->pSurfDesc->uiOffsets[2];
        }
        switch(gmmCustomParams.NoOfPlanes)
        {
            case 1:
                gmmCustomParams.PlaneOffset.X[GMM_PLANE_Y] = 0;
                gmmCustomParams.PlaneOffset.Y[GMM_PLANE_Y] = mediaSurface->pSurfDesc->uiOffsets[0] / pitch;
                if (bMemCompEnable)
                {
                    gmmCustomParams.AuxSurf.Size = mediaSurface->pSurfDesc->uiSize - gmmCustomParams.Size;
                    gmmCustomParams.AuxSurf.Pitch = mediaSurface->pSurfDesc->uiPitches[1];
                    gmmCustomParams.AuxSurf.PlaneOffset.X[GMM_PLANE_Y] = 0;
                    gmmCustomParams.AuxSurf.PlaneOffset.Y[GMM_PLANE_Y] = 0;
                }
                break;
            case 2:
                gmmCustomParams.PlaneOffset.X[GMM_PLANE_Y] = 0;
                gmmCustomParams.PlaneOffset.Y[GMM_PLANE_Y] = mediaSurface->pSurfDesc->uiOffsets[0] / pitch;
                gmmCustomParams.PlaneOffset.X[GMM_PLANE_U] = 0;
                gmmCustomParams.PlaneOffset.Y[GMM_PLANE_U] = mediaSurface->pSurfDesc->uiOffsets[1] / pitch;
                gmmCustomParams.PlaneOffset.X[GMM_PLANE_V] = 0;
                gmmCustomParams.PlaneOffset.Y[GMM_PLANE_V] = mediaSurface->pSurfDesc->uiOffsets[1] / pitch;
                if (bMemCompEnable)
                {
                    gmmCustomParams.AuxSurf.Size = (mediaSurface->pSurfDesc->uiOffsets[3] - mediaSurface->pSurfDesc->uiOffsets[2]) * 2;
                    gmmCustomParams.AuxSurf.Pitch = mediaSurface->pSurfDesc->uiPitches[2];
                    gmmCustomParams.AuxSurf.PlaneOffset.X[GMM_PLANE_Y] = 0;
                    gmmCustomParams.AuxSurf.PlaneOffset.Y[GMM_PLANE_Y] = 0;
                    gmmCustomParams.AuxSurf.PlaneOffset.X[GMM_PLANE_U] = (mediaSurface->pSurfDesc->uiOffsets[3]
                                                                    - mediaSurface->pSurfDesc->uiOffsets[2]);
                    gmmCustomParams.AuxSurf.PlaneOffset.Y[GMM_PLANE_U] = 0;
                    gmmCustomParams.AuxSurf.PlaneOffset.X[GMM_PLANE_V] = (mediaSurface->pSurfDesc->uiOffsets[3]
                                                                    - mediaSurface->pSurfDesc->uiOffsets[2]);
                    gmmCustomParams.AuxSurf.PlaneOffset.Y[GMM_PLANE_V] = 0;
                }
                break;
            case 3:
                if (mediaSurface->format == Media_Format_YV12)
                {
                    gmmCustomParams.PlaneOffset.X[GMM_PLANE_Y] = 0;
                    gmmCustomParams.PlaneOffset.Y[GMM_PLANE_Y] = mediaSurface->pSurfDesc->uiOffsets[0] / pitch;
                    gmmCustomParams.PlaneOffset.X[GMM_PLANE_U] = 0;
                    gmmCustomParams.PlaneOffset.Y[GMM_PLANE_U] = mediaSurface->pSurfDesc->uiOffsets[2] / pitch;
                    gmmCustomParams.PlaneOffset.X[GMM_PLANE_V] = 0;
                    gmmCustomParams.PlaneOffset.Y[GMM_PLANE_V] = mediaSurface->pSurfDesc->uiOffsets[1] / pitch;
                }
                else
                {
                    gmmCustomParams.PlaneOffset.X[GMM_PLANE_Y] = 0;
                    gmmCustomParams.PlaneOffset.Y[GMM_PLANE_Y] = mediaSurface->pSurfDesc->uiOffsets[0] / pitch;
                    gmmCustomParams.PlaneOffset.X[GMM_PLANE_U] = 0;
                    gmmCustomParams.PlaneOffset.Y[GMM_PLANE_U] = mediaSurface->pSurfDesc->uiOffsets[1] / pitch;
                    gmmCustomParams.PlaneOffset.X[GMM_PLANE_V] = 0;
                    gmmCustomParams.PlaneOffset.Y[GMM_PLANE_V] = mediaSurface->pSurfDesc->uiOffsets[2] / pitch;
                }
                break;
            default:
                DDI_ASSERTMESSAGE("Invalid plane number.");
                return VA_STATUS_ERROR_ALLOCATION_FAILED;
        }
        gmmResourceInfo = mediaDrvCtx->pGmmClientContext->CreateCustomResInfoObject_2(&gmmCustomParams);

        if(nullptr == gmmResourceInfo)
        {
            DDI_ASSERTMESSAGE("Gmm Create Resource Failed.");
            hRes = VA_STATUS_ERROR_ALLOCATION_FAILED;
            goto finish;
        }
        int gmmTiledType = gmmResourceInfo->GetTileType();
        if (bo)
        {
            mediaSurface->pGmmResourceInfo = gmmResourceInfo;
            mediaSurface->bMapped          = false;
            mediaSurface->format           = format;
            mediaSurface->iWidth           = width;
            mediaSurface->iHeight          = gmmResourceInfo->GetBaseHeight();
            mediaSurface->iRealHeight      = height;
            mediaSurface->iPitch           = pitch;
            mediaSurface->iRefCount        = 0;
            mediaSurface->bo               = bo;
            mediaSurface->TileType         = tileformat;
            mediaSurface->isTiled          = (tileformat != TILING_NONE) ? 1 : 0;
            mediaSurface->pData            = (uint8_t*) bo->virt;
            DDI_VERBOSEMESSAGE("Allocate external surface %7d bytes (%d x %d resource). gmmTiledType %d", mediaSurface->pSurfDesc->uiSize, width, height, gmmTiledType);
            uint32_t event[] = {bo->handle, format, width, height, pitch, bo->size, tileformat, cpTag};
            MOS_TraceEventExt(EVENT_VA_SURFACE, EVENT_TYPE_INFO, event, sizeof(event), &gmmResourceInfo->GetResFlags(), sizeof(GMM_RESOURCE_FLAG));
        }
        else
        {
            DDI_ASSERTMESSAGE("Fail to allocate external surface");
            return VA_STATUS_ERROR_ALLOCATION_FAILED;
        }
    }
    else
    {
        if (mediaSurface->pSurfDesc)
        {
            if( mediaSurface->pSurfDesc->uiFlags & VA_SURFACE_EXTBUF_DESC_ENABLE_TILING )
            {
                tileformat = TILING_Y;
            }
            else if (mediaSurface->pSurfDesc->uiVaMemType == VA_SURFACE_ATTRIB_MEM_TYPE_VA)
            {
                tileformat = TILING_NONE;
                alignedHeight = height;
                if (format == Media_Format_YV12 ||
                    format == Media_Format_I420)
                {
                    alignedHeight = MOS_ALIGN_CEIL(height, 2);
                }
            }
        }

        // Create GmmResourceInfo
        MOS_ZeroMemory(&gmmParams, sizeof(gmmParams));
        gmmParams.BaseWidth         = alignedWidth;
        gmmParams.BaseHeight        = alignedHeight;
        gmmParams.ArraySize         = 1;
        gmmParams.Type              = RESOURCE_2D;
        gmmParams.Format            = mediaDrvCtx->m_caps->ConvertMediaFmtToGmmFmt(format);
        DDI_CHK_CONDITION(gmmParams.Format == GMM_FORMAT_INVALID, "Unsupported format", VA_STATUS_ERROR_UNSUPPORTED_RT_FORMAT);
        switch (tileformat)
        {
            case TILING_Y:
                // Disable MMC for application required surfaces, because some cases' output streams have corruption.
                gmmParams.Flags.Gpu.MMC    = false;
                if (MEDIA_IS_SKU(&mediaDrvCtx->SkuTable, FtrE2ECompression)             &&
                    (!MEDIA_IS_WA(&mediaDrvCtx->WaTable, WaDisableVPMmc)                &&
                    !MEDIA_IS_WA(&mediaDrvCtx->WaTable, WaDisableCodecMmc))             &&
                    MEDIA_IS_SKU(&mediaDrvCtx->SkuTable, FtrCompressibleSurfaceDefault) &&
                    bMemCompEnable)
                {
                    gmmParams.Flags.Gpu.MMC               = true;
                    gmmParams.Flags.Info.MediaCompressed  = 1;
                    gmmParams.Flags.Info.RenderCompressed = 0;
                    gmmParams.Flags.Gpu.CCS               = 1;
                    gmmParams.Flags.Gpu.RenderTarget      = 1;
                    gmmParams.Flags.Gpu.UnifiedAuxSurface = 1;

                    if (bMemCompRC)
                    {
                        gmmParams.Flags.Info.MediaCompressed  = 0;
                        gmmParams.Flags.Info.RenderCompressed = 1;
                    }

                    if(MEDIA_IS_SKU(&mediaDrvCtx->SkuTable, FtrRenderCompressionOnly))
                    {
                        gmmParams.Flags.Info.MediaCompressed = 0;

                        if (format == Media_Format_X8R8G8B8 ||
                            format == Media_Format_X8B8G8R8 ||
                            format == Media_Format_A8B8G8R8 ||
                            format == Media_Format_A8R8G8B8 ||
                            format == Media_Format_R8G8B8A8)
                        {
                            gmmParams.Flags.Info.MediaCompressed  = 0;
                            gmmParams.Flags.Info.RenderCompressed = 1;
                        }
                        else
                        {
                            gmmParams.Flags.Gpu.MMC               = false;
                            gmmParams.Flags.Info.MediaCompressed  = 0;
                            gmmParams.Flags.Info.RenderCompressed = 0;
                            gmmParams.Flags.Gpu.CCS               = 0;
                            gmmParams.Flags.Gpu.UnifiedAuxSurface = 0;
                        }
                    }
                }
                break;
            case TILING_X:
                gmmParams.Flags.Info.TiledX    = true;
                break;
            default:
                gmmParams.Flags.Info.Linear    = true;
        }
        gmmParams.Flags.Gpu.Video = true;
        gmmParams.Flags.Info.LocalOnly = MEDIA_IS_SKU(&mediaDrvCtx->SkuTable, FtrLocalMemory);

        mediaSurface->pGmmResourceInfo = gmmResourceInfo = mediaDrvCtx->pGmmClientContext->CreateResInfoObject(&gmmParams);

        if(nullptr == gmmResourceInfo)
        {
            DDI_ASSERTMESSAGE("Gmm Create Resource Failed.");
            hRes = VA_STATUS_ERROR_ALLOCATION_FAILED;
            goto finish;
        }

        uint32_t    gmmPitch  = (uint32_t)gmmResourceInfo->GetRenderPitch();
        uint32_t    gmmSize   = (uint32_t)gmmResourceInfo->GetSizeSurface();
        uint32_t    gmmHeight = gmmResourceInfo->GetBaseHeight();

        if ( 0 == gmmPitch || 0 == gmmSize || 0 == gmmHeight)
        {
            DDI_ASSERTMESSAGE("Gmm Create Resource Failed.");
            hRes = VA_STATUS_ERROR_ALLOCATION_FAILED;
            goto finish;
        }

        switch (gmmResourceInfo->GetTileType())
        {
            case GMM_TILED_Y:
                tileformat = TILING_Y;
                break;
            case GMM_TILED_X:
                tileformat = TILING_X;
                break;
            case GMM_NOT_TILED:
                tileformat = TILING_NONE;
                break;
            default:
                tileformat = TILING_Y;
                break;
        }

        MemoryPolicyParameter memPolicyPar;
        MOS_ZeroMemory(&memPolicyPar, sizeof(MemoryPolicyParameter));

        memPolicyPar.skuTable = &mediaDrvCtx->SkuTable;
        memPolicyPar.waTable = &mediaDrvCtx->WaTable;
        memPolicyPar.resInfo = mediaSurface->pGmmResourceInfo;
        memPolicyPar.resName = "Media Surface";
        memPolicyPar.preferredMemType = (MEDIA_IS_WA(&mediaDrvCtx->WaTable, WaForceAllocateLML4)) ? MOS_MEMPOOL_DEVICEMEMORY : mem_type;

        mem_type = MemoryPolicyManager::UpdateMemoryPolicy(&memPolicyPar);

        if ( tileformat == TILING_NONE )
        {
            struct mos_drm_bo_alloc alloc;
            alloc.name = "MEDIA";
            alloc.size = gmmSize;
            alloc.alignment = 4096;
            alloc.ext.mem_type = mem_type;

            bo = mos_bo_alloc(mediaDrvCtx->pDrmBufMgr, &alloc);
            pitch = gmmPitch;
        }
        else
        {
            struct mos_drm_bo_alloc_tiled alloc_tiled;
            alloc_tiled.name = "MEDIA";
            alloc_tiled.x = gmmPitch;
            alloc_tiled.y = (gmmSize + gmmPitch -1)/gmmPitch;
            alloc_tiled.cpp = 1;
            alloc_tiled.ext.tiling_mode = tileformat;
            alloc_tiled.ext.mem_type = mem_type;

            bo = mos_bo_alloc_tiled(mediaDrvCtx->pDrmBufMgr, &alloc_tiled);
            pitch = alloc_tiled.pitch;
        }

        mediaSurface->bMapped = false;
        if (bo)
        {
            mediaSurface->format      = format;
            mediaSurface->iWidth      = width;
            mediaSurface->iHeight     = gmmHeight;
            mediaSurface->iRealHeight = height;
            mediaSurface->iPitch      = pitch;
            mediaSurface->iRefCount   = 0;
            mediaSurface->bo          = bo;
            mediaSurface->TileType    = tileformat;
            mediaSurface->isTiled     = (tileformat != TILING_NONE) ? 1 : 0;
            mediaSurface->pData       = (uint8_t*) bo->virt;
            DDI_VERBOSEMESSAGE("Alloc %7d bytes (%d x %d resource, gmmTiledType %d).",gmmSize, width, height, gmmResourceInfo->GetTileType());
            uint32_t event[] = {bo->handle, format, width, height, pitch, bo->size, tileformat, cpTag};
            MOS_TraceEventExt(EVENT_VA_SURFACE, EVENT_TYPE_INFO, event, sizeof(event), &gmmResourceInfo->GetResFlags(), sizeof(GMM_RESOURCE_FLAG));
        }
        else
        {
            DDI_ASSERTMESSAGE("Fail to Alloc %7d bytes (%d x %d resource).",gmmSize, width, height);
            hRes = VA_STATUS_ERROR_ALLOCATION_FAILED;
            goto finish;
        }
    }


finish:
    return hRes;
}

//!
//! \brief  Allocate buffer
//!
//! \param  [in] format
//!         Ddi media format
//! \param  [in] size
//!         Size of the region
//! \param  [out] mediaBuffer
//!         Pointer to ddi media buffer
//! \param  [in] bufmgr
//!         Mos buffer manager
//!
//! \return VAStatus
//!     VA_STATUS_SUCCESS if success, else fail reason
//!
VAStatus DdiMediaUtil_AllocateBuffer(
    DDI_MEDIA_FORMAT            format,
    int32_t                     size,
    PDDI_MEDIA_BUFFER           mediaBuffer,
    MOS_BUFMGR                 *bufmgr)
{

    DDI_CHK_NULL(mediaBuffer, "mediaBuffer is nullptr", VA_STATUS_ERROR_INVALID_BUFFER);
    DDI_CHK_NULL(mediaBuffer->pMediaCtx, "mediaBuffer->pMediaCtx is nullptr", VA_STATUS_ERROR_INVALID_BUFFER);
    DDI_CHK_NULL(mediaBuffer->pMediaCtx->pGmmClientContext, "mediaBuffer->pMediaCtx->pGmmClientContext is nullptr", VA_STATUS_ERROR_INVALID_BUFFER);
    if(format >= Media_Format_Count)
       return VA_STATUS_ERROR_INVALID_PARAMETER;

    VAStatus     hRes = VA_STATUS_SUCCESS;
    int32_t      mem_type = MOS_MEMPOOL_VIDEOMEMORY;

    // create fake GmmResourceInfo
    GMM_RESCREATE_PARAMS    gmmParams;
    MOS_ZeroMemory(&gmmParams, sizeof(gmmParams));
    gmmParams.BaseWidth             = 1;
    gmmParams.BaseHeight            = 1;
    gmmParams.ArraySize             = 0;
    gmmParams.Type                  = RESOURCE_1D;
    gmmParams.Format                = GMM_FORMAT_GENERIC_8BIT;
    gmmParams.Flags.Gpu.Video       = true;
    gmmParams.Flags.Info.Linear     = true;
    DDI_CHK_NULL(mediaBuffer->pMediaCtx, "MediaCtx is null", VA_STATUS_ERROR_INVALID_BUFFER);
    gmmParams.Flags.Info.LocalOnly = MEDIA_IS_SKU(&mediaBuffer->pMediaCtx->SkuTable, FtrLocalMemory);

    mediaBuffer->pGmmResourceInfo = mediaBuffer->pMediaCtx->pGmmClientContext->CreateResInfoObject(&gmmParams);

    DDI_CHK_NULL(mediaBuffer->pGmmResourceInfo, "pGmmResourceInfo is nullptr", VA_STATUS_ERROR_INVALID_BUFFER);
    mediaBuffer->pGmmResourceInfo->OverrideSize(mediaBuffer->iSize);
    mediaBuffer->pGmmResourceInfo->OverrideBaseWidth(mediaBuffer->iSize);
    mediaBuffer->pGmmResourceInfo->OverridePitch(mediaBuffer->iSize);

    MemoryPolicyParameter memPolicyPar;
    MOS_ZeroMemory(&memPolicyPar, sizeof(MemoryPolicyParameter));

    memPolicyPar.skuTable = &mediaBuffer->pMediaCtx->SkuTable;
    memPolicyPar.waTable = &mediaBuffer->pMediaCtx->WaTable;
    memPolicyPar.resInfo = mediaBuffer->pGmmResourceInfo;
    memPolicyPar.resName = "Media Buffer";
    memPolicyPar.uiType = mediaBuffer->uiType;
    memPolicyPar.preferredMemType = mediaBuffer->bUseSysGfxMem ? MOS_MEMPOOL_SYSTEMMEMORY : 0;

    mem_type = MemoryPolicyManager::UpdateMemoryPolicy(&memPolicyPar);

    struct mos_drm_bo_alloc alloc;
    alloc.name = "Media Buffer";
    alloc.size = size;
    alloc.alignment = 4096;
    alloc.ext.mem_type = mem_type;
    MOS_LINUX_BO *bo  = mos_bo_alloc(bufmgr, &alloc);

    mediaBuffer->bMapped = false;
    if (bo)
    {
        mediaBuffer->format     = format;
        mediaBuffer->iSize      = size;
        mediaBuffer->iRefCount  = 0;
        mediaBuffer->bo         = bo;
        mediaBuffer->pData      = (uint8_t*) bo->virt;

        DDI_VERBOSEMESSAGE("Alloc %8d bytes resource.",size);
        uint32_t event[] = {bo->handle, format, size, 1, size, bo->size, 0, 0};
        MOS_TraceEventExt(EVENT_VA_BUFFER, EVENT_TYPE_INFO, event, sizeof(event), &mediaBuffer->pGmmResourceInfo->GetResFlags(), sizeof(GMM_RESOURCE_FLAG));
    }
    else
    {
        DDI_ASSERTMESSAGE("Fail to Alloc %8d bytes resource.",size);
        hRes = VA_STATUS_ERROR_ALLOCATION_FAILED;
        goto finish;
    }

finish:
    return hRes;
}

//!
//! \brief  Allocate 2D buffer
//!
//! \param  [in] height
//!         Height of the region
//! \param  [in] width
//!         Width of the region
//! \param  [out] mediaBuffer
//!         Pointer to ddi media buffer
//! \param  [in] bufmgr
//!         Mos buffer manager
//!
//! \return VAStatus
//!     VA_STATUS_SUCCESS if success, else fail reason
//!
VAStatus DdiMediaUtil_Allocate2DBuffer(
    uint32_t                    height,
    uint32_t                    width,
    PDDI_MEDIA_BUFFER           mediaBuffer,
    MOS_BUFMGR                 *bufmgr)
{
    DDI_CHK_NULL(mediaBuffer, "mediaBuffer is nullptr", VA_STATUS_ERROR_INVALID_BUFFER);
    DDI_CHK_NULL(mediaBuffer->pMediaCtx, "mediaBuffer->pMediaCtx is nullptr", VA_STATUS_ERROR_INVALID_BUFFER);
    DDI_CHK_NULL(mediaBuffer->pMediaCtx->pGmmClientContext, "mediaBuffer->pMediaCtx->pGmmClientContext is nullptr", VA_STATUS_ERROR_INVALID_BUFFER);

    int32_t  size           = 0;
    uint32_t tileformat     = TILING_NONE;
    VAStatus hRes           = VA_STATUS_SUCCESS;
    int32_t  mem_type       = MOS_MEMPOOL_VIDEOMEMORY;

    // Create GmmResourceInfo
    GMM_RESCREATE_PARAMS        gmmParams;
    MOS_ZeroMemory(&gmmParams, sizeof(gmmParams));
    gmmParams.BaseWidth             = width;
    gmmParams.BaseHeight            = height;
    gmmParams.ArraySize             = 1;
    gmmParams.Type                  = RESOURCE_2D;
    gmmParams.Format                = GMM_FORMAT_GENERIC_8BIT;

    DDI_CHK_CONDITION(gmmParams.Format == GMM_FORMAT_INVALID,
                         "Unsupported format",
                         VA_STATUS_ERROR_UNSUPPORTED_RT_FORMAT);

    gmmParams.Flags.Info.Linear = true;
    gmmParams.Flags.Gpu.Video   = true;
    DDI_CHK_NULL(mediaBuffer->pMediaCtx, "MediaCtx is null", VA_STATUS_ERROR_INVALID_BUFFER);
    gmmParams.Flags.Info.LocalOnly = MEDIA_IS_SKU(&mediaBuffer->pMediaCtx->SkuTable, FtrLocalMemory);
    GMM_RESOURCE_INFO          *gmmResourceInfo;
    mediaBuffer->pGmmResourceInfo = gmmResourceInfo = mediaBuffer->pMediaCtx->pGmmClientContext->CreateResInfoObject(&gmmParams);

    if(nullptr == gmmResourceInfo)
    {
        DDI_VERBOSEMESSAGE("Gmm Create Resource Failed.");
        hRes = VA_STATUS_ERROR_ALLOCATION_FAILED;
        return hRes;
    }
    uint32_t    gmmPitch;
    uint32_t    gmmSize;
    uint32_t    gmmHeight;
    gmmPitch    = (uint32_t)gmmResourceInfo->GetRenderPitch();
    gmmSize     = (uint32_t)gmmResourceInfo->GetSizeSurface();
    gmmHeight   = gmmResourceInfo->GetBaseHeight();

    MemoryPolicyParameter memPolicyPar;
    MOS_ZeroMemory(&memPolicyPar, sizeof(MemoryPolicyParameter));

    memPolicyPar.skuTable = &mediaBuffer->pMediaCtx->SkuTable;
    memPolicyPar.waTable = &mediaBuffer->pMediaCtx->WaTable;
    memPolicyPar.resInfo = mediaBuffer->pGmmResourceInfo;
    memPolicyPar.resName = "Media 2D Buffer";
    memPolicyPar.uiType   = mediaBuffer->uiType;
    memPolicyPar.preferredMemType = mediaBuffer->bUseSysGfxMem ? MOS_MEMPOOL_SYSTEMMEMORY : 0;

    mem_type = MemoryPolicyManager::UpdateMemoryPolicy(&memPolicyPar);

    MOS_LINUX_BO  *bo;
    struct mos_drm_bo_alloc alloc;
    alloc.name = "Media 2D Buffer";
    alloc.size = gmmSize;
    alloc.alignment = 4096;
    alloc.ext.mem_type = mem_type;
    bo = mos_bo_alloc(bufmgr, &alloc);

    mediaBuffer->bMapped = false;
    if (bo)
    {
        mediaBuffer->format     = Media_Format_2DBuffer;
        mediaBuffer->uiWidth    = width;
        mediaBuffer->uiHeight   = gmmHeight;
        mediaBuffer->uiPitch    = gmmPitch;
        mediaBuffer->iSize      = gmmSize;
        mediaBuffer->iRefCount  = 0;
        mediaBuffer->bo         = bo;
        mediaBuffer->TileType   = tileformat;
        mediaBuffer->pData      = (uint8_t*) bo->virt;
        DDI_VERBOSEMESSAGE("Alloc %7d bytes (%d x %d resource)\n",size, width, height);
        uint32_t event[] = {bo->handle, mediaBuffer->format, width, height, gmmPitch, bo->size, tileformat, 0};
        MOS_TraceEventExt(EVENT_VA_BUFFER, EVENT_TYPE_INFO, event, sizeof(event), &gmmResourceInfo->GetResFlags(), sizeof(GMM_RESOURCE_FLAG));
    }
    else
    {
        DDI_VERBOSEMESSAGE("Fail to Alloc %7d bytes (%d x %d resource)\n", size, width, height);
        hRes = VA_STATUS_ERROR_ALLOCATION_FAILED;
    }

finish:
    return hRes;
}

VAStatus DdiMediaUtil_CreateSurface(DDI_MEDIA_SURFACE  *surface, PDDI_MEDIA_CONTEXT mediaDrvCtx)
{
    VAStatus hr = VA_STATUS_SUCCESS;

    DDI_CHK_NULL(surface, "nullptr surface", VA_STATUS_ERROR_INVALID_BUFFER);

    // better to differentiate 1D and 2D type
    hr = DdiMediaUtil_AllocateSurface(surface->format,
                         surface->iWidth,
                         surface->iHeight,
                         surface,
                         mediaDrvCtx);
    if (VA_STATUS_SUCCESS == hr && nullptr != surface->bo)
        surface->base = surface->name;

    return hr;
}

VAStatus DdiMediaUtil_CreateBuffer(DDI_MEDIA_BUFFER *buffer, MOS_BUFMGR *bufmgr)
{
    VAStatus hr = VA_STATUS_SUCCESS;

    DDI_CHK_NULL(buffer, "nullptr buffer", VA_STATUS_ERROR_INVALID_BUFFER);

    DDI_CHK_LESS(buffer->format, Media_Format_Count, "Invalid buffer->format", VA_STATUS_ERROR_INVALID_PARAMETER);

    if (buffer->format == Media_Format_CPU)
    {
        buffer->pData= (uint8_t*)MOS_AllocAndZeroMemory(buffer->iSize);
        if (nullptr == buffer->pData)
            hr = VA_STATUS_ERROR_ALLOCATION_FAILED;
    }

    else
    {
        if (Media_Format_2DBuffer == buffer->format)
        {
            hr = DdiMediaUtil_Allocate2DBuffer(buffer->uiHeight,
                                  buffer->uiWidth,
                                  buffer,
                                  bufmgr);
         }
         else
         {
             hr = DdiMediaUtil_AllocateBuffer(buffer->format,
                                 buffer->iSize,
                                 buffer,
                                 bufmgr);
         }
    }

    buffer->uiLockedBufID   = VA_INVALID_ID;
    buffer->uiLockedImageID = VA_INVALID_ID;
    buffer->iRefCount       = 0;

    return hr;
}

VAStatus SwizzleSurface(PDDI_MEDIA_CONTEXT mediaCtx, PGMM_RESOURCE_INFO pGmmResInfo, void *pLockedAddr, uint32_t TileType, uint8_t* pResourceBase, bool bUpload);

static VAStatus CreateShadowResource(DDI_MEDIA_SURFACE *surface)
{
    VAStatus vaStatus = VA_STATUS_SUCCESS;
    DDI_CHK_NULL(surface, "nullptr surface", VA_STATUS_ERROR_INVALID_SURFACE);

    if (surface->pGmmResourceInfo->GetSetCpSurfTag(0, 0) != 0)
    {
        return VA_STATUS_ERROR_INVALID_SURFACE;
    }

    // 422V fallback to SW swizzle
    if (surface->iWidth < 64 || surface->iRealHeight < 64 || (surface->iPitch % 64 != 0) || surface->format == Media_Format_P016 || surface->format == Media_Format_422V)
    {
        return VA_STATUS_ERROR_UNSUPPORTED_RT_FORMAT;
    }

    surface->pShadowBuffer = (DDI_MEDIA_BUFFER *)MOS_AllocAndZeroMemory(sizeof(DDI_MEDIA_BUFFER));
    DDI_CHK_NULL(surface->pShadowBuffer, "Failed to allocate shadow buffer", VA_STATUS_ERROR_INVALID_BUFFER);
    surface->pShadowBuffer->pMediaCtx = surface->pMediaCtx;
    surface->pShadowBuffer->bUseSysGfxMem = true;
    surface->pShadowBuffer->iSize = surface->pGmmResourceInfo->GetSizeSurface();

    vaStatus = DdiMediaUtil_AllocateBuffer(Media_Format_Buffer,
                                 surface->pShadowBuffer->iSize,
                                 surface->pShadowBuffer,
                                 surface->pMediaCtx->pDrmBufMgr);

    if (vaStatus != VA_STATUS_SUCCESS)
    {
        MOS_FreeMemory(surface->pShadowBuffer);
        surface->pShadowBuffer = nullptr;
    }

    return vaStatus;
}

//!
//! \brief  Swizzle surface by Hardware, current only support VEBOX
//!
//! \param  [in] surface
//!         Pointer of surface
//! \param  [in] isDeSwizzle
//!         Whether it's de-swizzling or not
//!         Swizzling    - copying from video memory to temporary buffer
//!         De-swizzling - copying from temporary buffer to video memory
//!
//! \return VAStatus
//!     VA_STATUS_SUCCESS if success, else fail reason
//!
static VAStatus SwizzleSurfaceByHW(DDI_MEDIA_SURFACE *surface, bool isDeSwizzle = false)
{
    DDI_CHK_NULL(surface, "nullptr surface", VA_STATUS_ERROR_INVALID_SURFACE);
    DDI_CHK_NULL(surface->pMediaCtx, "nullptr media context", VA_STATUS_ERROR_INVALID_CONTEXT);

    MOS_CONTEXT mosCtx = { };
    PERF_DATA perfData = { };
    PDDI_MEDIA_CONTEXT mediaDrvCtx = surface->pMediaCtx;
    VAStatus vaStatus = VA_STATUS_SUCCESS;

    // Get the buf manager for codechal create
    mosCtx.bufmgr          = mediaDrvCtx->pDrmBufMgr;
    mosCtx.m_gpuContextMgr = mediaDrvCtx->m_gpuContextMgr;
    mosCtx.m_cmdBufMgr     = mediaDrvCtx->m_cmdBufMgr;
    mosCtx.fd              = mediaDrvCtx->fd;
    mosCtx.iDeviceId       = mediaDrvCtx->iDeviceId;
    mosCtx.m_skuTable      = mediaDrvCtx->SkuTable;
    mosCtx.m_waTable       = mediaDrvCtx->WaTable;
    mosCtx.m_gtSystemInfo  = *mediaDrvCtx->pGtSystemInfo;
    mosCtx.m_platform      = mediaDrvCtx->platform;

    mosCtx.ppMediaMemDecompState = &mediaDrvCtx->pMediaMemDecompState;
    mosCtx.pfnMemoryDecompress   = mediaDrvCtx->pfnMemoryDecompress;
    mosCtx.pfnMediaMemoryCopy    = mediaDrvCtx->pfnMediaMemoryCopy;
    mosCtx.pfnMediaMemoryCopy2D  = mediaDrvCtx->pfnMediaMemoryCopy2D;
    mosCtx.pPerfData             = &perfData;
    mosCtx.m_gtSystemInfo        = *mediaDrvCtx->pGtSystemInfo;
    mosCtx.m_auxTableMgr         = mediaDrvCtx->m_auxTableMgr;
    mosCtx.pGmmClientContext     = mediaDrvCtx->pGmmClientContext;
    mosCtx.m_userSettingPtr      = mediaDrvCtx->m_userSettingPtr;

    mosCtx.m_osDeviceContext     = mediaDrvCtx->m_osDeviceContext;

    MOS_RESOURCE source = {};
    MOS_RESOURCE target = {};

    if (isDeSwizzle)
    {
        DdiMedia_MediaBufferToMosResource(surface->pShadowBuffer, &source);
        DdiMedia_MediaSurfaceToMosResource(surface, &target);
    }
    else
    {
        DdiMedia_MediaSurfaceToMosResource(surface, &source);
        DdiMedia_MediaBufferToMosResource(surface->pShadowBuffer, &target);
    }

    DdiMediaUtil_LockMutex(&mediaDrvCtx->MemDecompMutex);
    vaStatus = mediaDrvCtx->pfnMediaMemoryTileConvert(
            &mosCtx,
            &source,
            &target,
            surface->pGmmResourceInfo->GetBaseWidth(),
            surface->pGmmResourceInfo->GetBaseHeight(),
            0,
            0,
            !isDeSwizzle,
            false);
    DdiMediaUtil_UnLockMutex(&mediaDrvCtx->MemDecompMutex);
    return vaStatus;
}

void* DdiMediaUtil_LockSurface(DDI_MEDIA_SURFACE* surface, uint32_t flag)
{
    DDI_CHK_NULL(surface, "nullptr surface", nullptr);
    DDI_CHK_NULL(surface->pMediaCtx, "nullptr surface->pMediaCtx", nullptr);
    if (MEDIA_IS_SKU(&surface->pMediaCtx->SkuTable, FtrLocalMemory))
    {
        if ((MosUtilities::MosAtomicIncrement(&surface->iRefCount) == 1) && (false == surface->bMapped))
        {
           return  DdiMediaUtil_LockSurfaceInternal(surface, flag);
        }
        else
        {
            DDI_VERBOSEMESSAGE("line %d, invalide operation for lockSurface. the surface reference count = %d", __LINE__, surface->iRefCount);
        }
    }
    else
    {
         if ((surface->iRefCount == 0) && (false == surface->bMapped))
         {
             DdiMediaUtil_LockSurfaceInternal(surface, flag);
         }
         else
         {
             // do nothing here
         }
         surface->iRefCount++;
    }

    return surface->pData;

}

// add thread protection for multiple thread?
void* DdiMediaUtil_LockSurfaceInternal(DDI_MEDIA_SURFACE  *surface, uint32_t flag)
{
    DDI_CHK_NULL(surface, "nullptr surface", nullptr);
    DDI_CHK_NULL(surface->bo, "nullptr surface->bo", nullptr);

    if (surface->pMediaCtx->bIsAtomSOC)
    {
        mos_bo_map_gtt(surface->bo);
    }
    else
    {
        if (surface->TileType == TILING_NONE)
        {
            mos_bo_map(surface->bo, flag & MOS_LOCKFLAG_WRITEONLY);
        }
        else if ((surface->pMediaCtx->m_useSwSwizzling) && !(flag & MOS_LOCKFLAG_NO_SWIZZLE))
        {
            uint64_t surfSize = surface->pGmmResourceInfo->GetSizeMainSurface();
            DDI_CHK_CONDITION((surface->TileType != TILING_Y), "Unsupported tile type", nullptr);
            DDI_CHK_CONDITION((surfSize <= 0 || surface->iPitch <= 0), "Invalid surface size or pitch", nullptr);

            VAStatus vaStatus = VA_STATUS_SUCCESS;
            if (MEDIA_IS_SKU(&surface->pMediaCtx->SkuTable, FtrLocalMemory))
            {
                if (surface->pShadowBuffer == nullptr)
                {
                    CreateShadowResource(surface);
                }

                if (surface->pShadowBuffer != nullptr)
                {
                    vaStatus = SwizzleSurfaceByHW(surface);
                    int err = 0;
                    if (vaStatus == VA_STATUS_SUCCESS)
                    {
                        err = mos_bo_map(surface->pShadowBuffer->bo, flag & MOS_LOCKFLAG_WRITEONLY);
                    }

                    if (vaStatus != VA_STATUS_SUCCESS || err != 0)
                    {
                        DdiMediaUtil_FreeBuffer(surface->pShadowBuffer);
                        MOS_FreeMemory(surface->pShadowBuffer);
                        surface->pShadowBuffer = nullptr;
                    }
                }
            }

            mos_bo_map(surface->bo, flag & MOS_LOCKFLAG_WRITEONLY);

            if (surface->pShadowBuffer == nullptr)
            {
                if (surface->pSystemShadow == nullptr)
                {
                    surface->pSystemShadow = (uint8_t*)MOS_AllocMemory(surface->bo->size);
                    DDI_CHK_CONDITION((surface->pSystemShadow == nullptr), "Failed to allocate shadow surface", nullptr);
                }

                vaStatus = SwizzleSurface(surface->pMediaCtx,
                                                   surface->pGmmResourceInfo,
                                                   surface->bo->virt,
                                                   (MOS_TILE_TYPE)surface->TileType,
                                                   (uint8_t *)surface->pSystemShadow,
                                                   false);
                DDI_CHK_CONDITION((vaStatus != VA_STATUS_SUCCESS), "SwizzleSurface failed", nullptr);
            }

        }
        else if (flag & MOS_LOCKFLAG_NO_SWIZZLE)
        {
            mos_bo_map(surface->bo, flag & MOS_LOCKFLAG_READONLY);
        }
        else if (flag & MOS_LOCKFLAG_WRITEONLY)
        {
            mos_bo_map_gtt(surface->bo);
        }
        else
        {
            mos_bo_map_unsynchronized(surface->bo);     // only call mmap_gtt ioctl
            mos_bo_start_gtt_access(surface->bo, 0);    // set to GTT domain,0 means readonly
        }
    }
    surface->uiMapFlag = flag;
    if (surface->pShadowBuffer)
    {
        surface->pData = (uint8_t *)surface->pShadowBuffer->bo->virt;
    }
    else if (surface->pSystemShadow)
    {
        surface->pData = surface->pSystemShadow;
    }
    else
    {
        surface->pData = (uint8_t*) surface->bo->virt;
    }
    surface->data_size = surface->bo->size;
    surface->bMapped = true;

    return surface->pData;
}

void DdiMediaUtil_UnlockSurface(DDI_MEDIA_SURFACE *surface)
{
    DDI_CHK_NULL(surface, "nullptr surface", );
    DDI_CHK_NULL(surface->pMediaCtx, "nullptr surface->pMediaCtx", );
    if (0 == surface->iRefCount)
     return;

    if (MEDIA_IS_SKU(&surface->pMediaCtx->SkuTable, FtrLocalMemory))
    {
        if (MosUtilities::MosAtomicDecrement(&surface->iRefCount) == 0 && (true == surface->bMapped))
        {
            DdiMediaUtil_UnlockSurfaceInternal(surface);
        }
        else
        {
            DDI_VERBOSEMESSAGE("invalide operation for unlockSurface. the surface reference count = %d", surface->iRefCount);
        }
    }
    else
    {
        if ((1 == surface->iRefCount) && (true == surface->bMapped))
        {
           DdiMediaUtil_UnlockSurfaceInternal(surface);
        }
        else
        {
           DDI_VERBOSEMESSAGE("invalide operation for unlockSurface. the surface reference count = %d", surface->iRefCount);
        }
        surface->iRefCount --;
    }
}

void DdiMediaUtil_UnlockSurfaceInternal(DDI_MEDIA_SURFACE  *surface)
{
    DDI_CHK_NULL(surface, "nullptr surface",);
    DDI_CHK_NULL(surface->bo, "nullptr surface->bo", );

    if (surface->pMediaCtx->bIsAtomSOC)
    {
        mos_bo_unmap_gtt(surface->bo);
    }
    else
    {
        if (surface->TileType == TILING_NONE)
        {
           mos_bo_unmap(surface->bo);
        }
        else if (surface->pShadowBuffer != nullptr)
        {
            SwizzleSurfaceByHW(surface, true);

            mos_bo_unmap(surface->pShadowBuffer->bo);
            mos_bo_unmap(surface->bo);
        }
        else if (surface->pSystemShadow)
        {
            SwizzleSurface(surface->pMediaCtx,
                           surface->pGmmResourceInfo,
                           surface->bo->virt,
                           (MOS_TILE_TYPE)surface->TileType,
                           (uint8_t *)surface->pSystemShadow,
                           true);

            MOS_FreeMemory(surface->pSystemShadow);
            surface->pSystemShadow = nullptr;

            mos_bo_unmap(surface->bo);
        }
        else if(surface->uiMapFlag & MOS_LOCKFLAG_NO_SWIZZLE)
        {
            mos_bo_unmap(surface->bo);
        }
        else
        {
           mos_bo_unmap_gtt(surface->bo);
        }
    }
    surface->pData       = nullptr;
    surface->bo->virt    = nullptr;
    surface->bMapped     = false;
}

// add thread protection for multiple thread?
// MapBuffer?
void* DdiMediaUtil_LockBuffer(DDI_MEDIA_BUFFER *buf, uint32_t flag)
{
    DDI_CHK_NULL(buf, "nullptr buf", nullptr);
    if((Media_Format_CPU != buf->format) && (false == buf->bMapped))
    {
        if (nullptr != buf->pSurface)
        {
            DdiMediaUtil_LockSurface(buf->pSurface, flag);
            buf->pData = buf->pSurface->pData;
        }
        else
        {
            if (buf->pMediaCtx->bIsAtomSOC)
            {
                mos_bo_map_gtt(buf->bo);
            }
            else
            {
                if (buf->TileType == TILING_NONE)
                {
                    mos_bo_map(buf->bo, ((MOS_LOCKFLAG_READONLY | MOS_LOCKFLAG_WRITEONLY) & flag));
                }
                else
                {
                    mos_bo_map_gtt(buf->bo);
                }
             }

            buf->pData = (uint8_t*)(buf->bo->virt);
        }

        buf->bMapped = true;
        buf->iRefCount++;
    }
    else if ((Media_Format_CPU == buf->format) && (false == buf->bMapped))
    {
        buf->bMapped = true;
        buf->iRefCount++;
    }
    else
    {
        buf->iRefCount++;
    }

    return buf->pData;
}

void DdiMediaUtil_UnlockBuffer(DDI_MEDIA_BUFFER *buf)
{
    DDI_CHK_NULL(buf, "nullptr buf", );
    if (0 == buf->iRefCount)
        return;
    if((true == buf->bMapped) && (Media_Format_CPU != buf->format) && (1 == buf->iRefCount))
    {
        if (nullptr != buf->pSurface)
        {
            DdiMediaUtil_UnlockSurface(buf->pSurface);
        }
        else
        {
             if (buf->pMediaCtx->bIsAtomSOC)
             {
                 mos_bo_unmap_gtt(buf->bo);
             }
             else
             {
                 if (buf->TileType == TILING_NONE)
                 {
                     mos_bo_unmap(buf->bo);
                 }
                 else
                 {
                     mos_bo_unmap_gtt(buf->bo);
                 }
            }
            buf->bo->virt = nullptr;
        }

        buf->pData       = nullptr;

        buf->bMapped     = false;
    }
    else if ((true == buf->bMapped) && (Media_Format_CPU == buf->format) && (1 == buf->iRefCount))
    {
        buf->bMapped     = false;
    }
    else
    {
        // do nothing here
    }
    buf->iRefCount--;
}

// should ref_count added for bo?
void DdiMediaUtil_FreeSurface(DDI_MEDIA_SURFACE *surface)
{
    DDI_CHK_NULL(surface, "nullptr surface", );
    DDI_CHK_NULL(surface->bo, "nullptr surface->bo", );
    DDI_CHK_NULL(surface->pMediaCtx, "nullptr surface->pMediaCtx", );
    DDI_CHK_NULL(surface->pMediaCtx->pGmmClientContext, "nullptr surface->pMediaCtx->pGmmClientContext", );

    // Unmap Aux mapping if the surface was mapped
    if (surface->pMediaCtx->m_auxTableMgr)
    {
        surface->pMediaCtx->m_auxTableMgr->UnmapResource(surface->pGmmResourceInfo, surface->bo);
    }

    // free shadow buffer it created
    if (surface->pShadowBuffer != nullptr)
    {
        DdiMediaUtil_FreeBuffer(surface->pShadowBuffer);
        MOS_FreeMemory(surface->pShadowBuffer);
        surface->pShadowBuffer = nullptr;
    }

    if(surface->bMapped)
    {
        DdiMediaUtil_UnlockSurface(surface);
        DDI_VERBOSEMESSAGE("DDI: try to free a locked surface.");
    }
    mos_bo_unreference(surface->bo);
    // For External Buffer, only needs to destory SurfaceDescriptor
    if (surface->pSurfDesc)
    {
        MOS_FreeMemory(surface->pSurfDesc);
        surface->pSurfDesc = nullptr;
    }

    if (nullptr != surface->pGmmResourceInfo)
    {
        surface->pMediaCtx->pGmmClientContext->DestroyResInfoObject(surface->pGmmResourceInfo);
        surface->pGmmResourceInfo = nullptr;
    }
}


// should ref_count added for bo?
void DdiMediaUtil_FreeBuffer(DDI_MEDIA_BUFFER  *buf)
{
    DDI_CHK_NULL(buf, "nullptr", );
    // calling sequence checking
    if (buf->bMapped)
    {
        DdiMediaUtil_UnlockBuffer(buf);
        DDI_VERBOSEMESSAGE("DDI: try to free a locked buffer.");
    }
    if (buf->format == Media_Format_CPU)
    {
        MOS_FreeMemory(buf->pData);
        buf->pData = nullptr;
    }
    else
    {
        mos_bo_unreference(buf->bo);
        buf->bo = nullptr;
    }

    if (nullptr != buf->pMediaCtx && nullptr != buf->pMediaCtx->pGmmClientContext && nullptr != buf->pGmmResourceInfo)
    {
        buf->pMediaCtx->pGmmClientContext->DestroyResInfoObject(buf->pGmmResourceInfo);
        buf->pGmmResourceInfo = nullptr;
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////////
// purpose: fill a rect structure with the regsion specified by parameters
// rect[in]: input pointer to the rect
// offset_x: x offset of the region
// offset_y: y offset of the region
// width:    width of the region
// hiehgt:   height of the regsion
////////////////////////////////////////////////////////////////////////////////////////////////////
VAStatus DdiMediaUtil_FillPositionToRect(RECT *rect, int16_t offset_x, int16_t offset_y, int16_t width, int16_t height)
{
    DDI_CHK_NULL(rect, "Invalid Rect.", VA_STATUS_ERROR_INVALID_PARAMETER);

    rect->left   = offset_x;
    rect->top    = offset_y;
    rect->right  = offset_x + width;
    rect->bottom = offset_y + height;

    return VA_STATUS_SUCCESS;
}

void DdiMediaUtil_InitMutex(PMEDIA_MUTEX_T  mutex)
{
    pthread_mutex_init(mutex, nullptr);
}

void DdiMediaUtil_DestroyMutex(PMEDIA_MUTEX_T  mutex)
{
    int32_t ret = pthread_mutex_destroy(mutex);
    if(ret != 0)
    {
        DDI_NORMALMESSAGE("can't destroy the mutex!\n");
    }
}

void DdiMediaUtil_LockMutex(PMEDIA_MUTEX_T  mutex)
{
    int32_t ret = pthread_mutex_lock(mutex);
    if(ret != 0)
    {
        DDI_NORMALMESSAGE("can't lock the mutex!\n");
    }
}

void DdiMediaUtil_UnLockMutex(PMEDIA_MUTEX_T  mutex)
{
    int32_t ret = pthread_mutex_unlock(mutex);
    if(ret != 0)
    {
        DDI_NORMALMESSAGE("can't unlock the mutex!\n");
    }
}

void DdiMediaUtil_DestroySemaphore(PMEDIA_SEM_T  sem)
{
    int32_t ret = sem_destroy(sem);
    if(ret != 0)
    {
        DDI_NORMALMESSAGE("can't destroy the semaphore!\n");
    }
}

void DdiMediaUtil_WaitSemaphore(PMEDIA_SEM_T  sem)
{
    int32_t ret = sem_wait(sem);
    if(ret != 0)
    {
        DDI_NORMALMESSAGE("wait semaphore error!\n");
    }
}

int32_t DdiMediaUtil_TryWaitSemaphore(PMEDIA_SEM_T  sem)
{
    return sem_trywait(sem);
}

void DdiMediaUtil_PostSemaphore(PMEDIA_SEM_T  sem)
{
    int32_t ret = sem_post(sem);
    if(ret != 0)
    {
        DDI_NORMALMESSAGE("post semaphore error!\n");
    }
}

// heap related
PDDI_MEDIA_SURFACE_HEAP_ELEMENT DdiMediaUtil_AllocPMediaSurfaceFromHeap(PDDI_MEDIA_HEAP surfaceHeap)
{
    DDI_CHK_NULL(surfaceHeap, "nullptr surfaceHeap", nullptr);

    PDDI_MEDIA_SURFACE_HEAP_ELEMENT  mediaSurfaceHeapElmt = nullptr;

    if (nullptr == surfaceHeap->pFirstFreeHeapElement)
    {
        void *newHeapBase = MOS_ReallocMemory(surfaceHeap->pHeapBase, (surfaceHeap->uiAllocatedHeapElements + DDI_MEDIA_HEAP_INCREMENTAL_SIZE) * sizeof(DDI_MEDIA_SURFACE_HEAP_ELEMENT));

        if (nullptr == newHeapBase)
        {
            DDI_ASSERTMESSAGE("DDI: realloc failed.");
            return nullptr;
        }
        surfaceHeap->pHeapBase                    = newHeapBase;
        PDDI_MEDIA_SURFACE_HEAP_ELEMENT surfaceHeapBase  = (PDDI_MEDIA_SURFACE_HEAP_ELEMENT)surfaceHeap->pHeapBase;
        surfaceHeap->pFirstFreeHeapElement        = (void*)(&surfaceHeapBase[surfaceHeap->uiAllocatedHeapElements]);
        for (int32_t i = 0; i < (DDI_MEDIA_HEAP_INCREMENTAL_SIZE); i++)
        {
            mediaSurfaceHeapElmt                  = &surfaceHeapBase[surfaceHeap->uiAllocatedHeapElements + i];
            mediaSurfaceHeapElmt->pNextFree       = (i == (DDI_MEDIA_HEAP_INCREMENTAL_SIZE - 1))? nullptr : &surfaceHeapBase[surfaceHeap->uiAllocatedHeapElements + i + 1];
            mediaSurfaceHeapElmt->uiVaSurfaceID   = surfaceHeap->uiAllocatedHeapElements + i;
            mediaSurfaceHeapElmt->pSurface        = nullptr;
        }
        surfaceHeap->uiAllocatedHeapElements     += DDI_MEDIA_HEAP_INCREMENTAL_SIZE;
    }

    mediaSurfaceHeapElmt                          = (PDDI_MEDIA_SURFACE_HEAP_ELEMENT)surfaceHeap->pFirstFreeHeapElement;
    surfaceHeap->pFirstFreeHeapElement            = mediaSurfaceHeapElmt->pNextFree;

    return mediaSurfaceHeapElmt;
}


void DdiMediaUtil_ReleasePMediaSurfaceFromHeap(PDDI_MEDIA_HEAP surfaceHeap, uint32_t vaSurfaceID)
{
    DDI_CHK_NULL(surfaceHeap, "nullptr surfaceHeap", );

    DDI_CHK_LESS(vaSurfaceID, surfaceHeap->uiAllocatedHeapElements, "invalid surface id", );
    PDDI_MEDIA_SURFACE_HEAP_ELEMENT mediaSurfaceHeapBase                   = (PDDI_MEDIA_SURFACE_HEAP_ELEMENT)surfaceHeap->pHeapBase;
    DDI_CHK_NULL(mediaSurfaceHeapBase, "nullptr mediaSurfaceHeapBase", );

    PDDI_MEDIA_SURFACE_HEAP_ELEMENT mediaSurfaceHeapElmt                   = &mediaSurfaceHeapBase[vaSurfaceID];
    DDI_CHK_NULL(mediaSurfaceHeapElmt->pSurface, "surface is already released", );
    void *firstFree                         = surfaceHeap->pFirstFreeHeapElement;
    surfaceHeap->pFirstFreeHeapElement     = (void*)mediaSurfaceHeapElmt;
    mediaSurfaceHeapElmt->pNextFree        = (PDDI_MEDIA_SURFACE_HEAP_ELEMENT)firstFree;
    mediaSurfaceHeapElmt->pSurface         = nullptr;
}


PDDI_MEDIA_BUFFER_HEAP_ELEMENT DdiMediaUtil_AllocPMediaBufferFromHeap(PDDI_MEDIA_HEAP bufferHeap)
{
    DDI_CHK_NULL(bufferHeap, "nullptr bufferHeap", nullptr);

    PDDI_MEDIA_BUFFER_HEAP_ELEMENT  mediaBufferHeapElmt = nullptr;
    if (nullptr == bufferHeap->pFirstFreeHeapElement)
    {
        void *newHeapBase = MOS_ReallocMemory(bufferHeap->pHeapBase, (bufferHeap->uiAllocatedHeapElements + DDI_MEDIA_HEAP_INCREMENTAL_SIZE) * sizeof(DDI_MEDIA_BUFFER_HEAP_ELEMENT));
        if (nullptr == newHeapBase)
        {
            DDI_ASSERTMESSAGE("DDI: realloc failed.");
            return nullptr;
        }
        bufferHeap->pHeapBase                                 = newHeapBase;
        PDDI_MEDIA_BUFFER_HEAP_ELEMENT mediaBufferHeapBase    = (PDDI_MEDIA_BUFFER_HEAP_ELEMENT)bufferHeap->pHeapBase;
        bufferHeap->pFirstFreeHeapElement     = (void*)(&mediaBufferHeapBase[bufferHeap->uiAllocatedHeapElements]);
        for (int32_t i = 0; i < (DDI_MEDIA_HEAP_INCREMENTAL_SIZE); i++)
        {
            mediaBufferHeapElmt               = &mediaBufferHeapBase[bufferHeap->uiAllocatedHeapElements + i];
            mediaBufferHeapElmt->pNextFree    = (i == (DDI_MEDIA_HEAP_INCREMENTAL_SIZE - 1))? nullptr : &mediaBufferHeapBase[bufferHeap->uiAllocatedHeapElements + i + 1];
            mediaBufferHeapElmt->uiVaBufferID = bufferHeap->uiAllocatedHeapElements + i;
        }
        bufferHeap->uiAllocatedHeapElements  += DDI_MEDIA_HEAP_INCREMENTAL_SIZE;
    }

    mediaBufferHeapElmt                       = (PDDI_MEDIA_BUFFER_HEAP_ELEMENT)bufferHeap->pFirstFreeHeapElement;
    bufferHeap->pFirstFreeHeapElement         = mediaBufferHeapElmt->pNextFree;
    return mediaBufferHeapElmt;
}


void DdiMediaUtil_ReleasePMediaBufferFromHeap(PDDI_MEDIA_HEAP bufferHeap, uint32_t vaBufferID)
{
    DDI_CHK_NULL(bufferHeap, "nullptr bufferHeap", );

    DDI_CHK_LESS(vaBufferID, bufferHeap->uiAllocatedHeapElements, "invalid buffer id", );
    PDDI_MEDIA_BUFFER_HEAP_ELEMENT mediaBufferHeapBase                    = (PDDI_MEDIA_BUFFER_HEAP_ELEMENT)bufferHeap->pHeapBase;
    PDDI_MEDIA_BUFFER_HEAP_ELEMENT mediaBufferHeapElmt                    = &mediaBufferHeapBase[vaBufferID];
    DDI_CHK_NULL(mediaBufferHeapElmt->pBuffer, "buffer is already released", );
    void *firstFree                        = bufferHeap->pFirstFreeHeapElement;
    bufferHeap->pFirstFreeHeapElement      = (void*)mediaBufferHeapElmt;
    mediaBufferHeapElmt->pNextFree         = (PDDI_MEDIA_BUFFER_HEAP_ELEMENT)firstFree;
    mediaBufferHeapElmt->pBuffer           = nullptr;
}

PDDI_MEDIA_IMAGE_HEAP_ELEMENT DdiMediaUtil_AllocPVAImageFromHeap(PDDI_MEDIA_HEAP imageHeap)
{
    PDDI_MEDIA_IMAGE_HEAP_ELEMENT   vaimageHeapElmt = nullptr;

    DDI_CHK_NULL(imageHeap, "nullptr imageHeap", nullptr);

    if (nullptr == imageHeap->pFirstFreeHeapElement)
    {
        void *newHeapBase = MOS_ReallocMemory(imageHeap->pHeapBase, (imageHeap->uiAllocatedHeapElements + DDI_MEDIA_HEAP_INCREMENTAL_SIZE) * sizeof(DDI_MEDIA_IMAGE_HEAP_ELEMENT));

        if (nullptr == newHeapBase)
        {
            DDI_ASSERTMESSAGE("DDI: realloc failed.");
            return nullptr;
        }
        imageHeap->pHeapBase                           = newHeapBase;
        PDDI_MEDIA_IMAGE_HEAP_ELEMENT vaimageHeapBase  = (PDDI_MEDIA_IMAGE_HEAP_ELEMENT)imageHeap->pHeapBase;
        imageHeap->pFirstFreeHeapElement               = (void*)(&vaimageHeapBase[imageHeap->uiAllocatedHeapElements]);
        for (int32_t i = 0; i < (DDI_MEDIA_HEAP_INCREMENTAL_SIZE); i++)
        {
            vaimageHeapElmt                   = &vaimageHeapBase[imageHeap->uiAllocatedHeapElements + i];
            vaimageHeapElmt->pNextFree        = (i == (DDI_MEDIA_HEAP_INCREMENTAL_SIZE - 1))? nullptr : &vaimageHeapBase[imageHeap->uiAllocatedHeapElements + i + 1];
            vaimageHeapElmt->uiVaImageID      = imageHeap->uiAllocatedHeapElements + i;
        }
        imageHeap->uiAllocatedHeapElements   += DDI_MEDIA_HEAP_INCREMENTAL_SIZE;

    }

    vaimageHeapElmt                           = (PDDI_MEDIA_IMAGE_HEAP_ELEMENT)imageHeap->pFirstFreeHeapElement;
    imageHeap->pFirstFreeHeapElement          = vaimageHeapElmt->pNextFree;
    return vaimageHeapElmt;
}


void DdiMediaUtil_ReleasePVAImageFromHeap(PDDI_MEDIA_HEAP imageHeap, uint32_t vaImageID)
{
    PDDI_MEDIA_IMAGE_HEAP_ELEMENT    vaImageHeapBase = nullptr;
    PDDI_MEDIA_IMAGE_HEAP_ELEMENT    vaImageHeapElmt = nullptr;
    void                            *firstFree      = nullptr;

    DDI_CHK_NULL(imageHeap, "nullptr imageHeap", );

    DDI_CHK_LESS(vaImageID, imageHeap->uiAllocatedHeapElements, "invalid image id", );
    vaImageHeapBase                    = (PDDI_MEDIA_IMAGE_HEAP_ELEMENT)imageHeap->pHeapBase;
    vaImageHeapElmt                    = &vaImageHeapBase[vaImageID];
    DDI_CHK_NULL(vaImageHeapElmt->pImage, "image is already released", );
    firstFree                          = imageHeap->pFirstFreeHeapElement;
    imageHeap->pFirstFreeHeapElement   = (void*)vaImageHeapElmt;
    vaImageHeapElmt->pNextFree         = (PDDI_MEDIA_IMAGE_HEAP_ELEMENT)firstFree;
    vaImageHeapElmt->pImage            = nullptr;
}

PDDI_MEDIA_VACONTEXT_HEAP_ELEMENT DdiMediaUtil_AllocPVAContextFromHeap(PDDI_MEDIA_HEAP vaContextHeap)
{
    PDDI_MEDIA_VACONTEXT_HEAP_ELEMENT   vacontextHeapElmt = nullptr;
    DDI_CHK_NULL(vaContextHeap, "nullptr vaContextHeap", nullptr);

    if (nullptr == vaContextHeap->pFirstFreeHeapElement)
    {
        void *newHeapBase = MOS_ReallocMemory(vaContextHeap->pHeapBase, (vaContextHeap->uiAllocatedHeapElements + DDI_MEDIA_HEAP_INCREMENTAL_SIZE) * sizeof(DDI_MEDIA_VACONTEXT_HEAP_ELEMENT));

        if (nullptr == newHeapBase)
        {
            DDI_ASSERTMESSAGE("DDI: realloc failed.");
            return nullptr;
        }
        vaContextHeap->pHeapBase                            = newHeapBase;
        PDDI_MEDIA_VACONTEXT_HEAP_ELEMENT vacontextHeapBase = (PDDI_MEDIA_VACONTEXT_HEAP_ELEMENT)vaContextHeap->pHeapBase;
        vaContextHeap->pFirstFreeHeapElement        = (void*)(&(vacontextHeapBase[vaContextHeap->uiAllocatedHeapElements]));
        for (int32_t i = 0; i < (DDI_MEDIA_HEAP_INCREMENTAL_SIZE); i++)
        {
            vacontextHeapElmt                       = &vacontextHeapBase[vaContextHeap->uiAllocatedHeapElements + i];
            vacontextHeapElmt->pNextFree            = (i == (DDI_MEDIA_HEAP_INCREMENTAL_SIZE - 1))? nullptr : &vacontextHeapBase[vaContextHeap->uiAllocatedHeapElements + i + 1];
            vacontextHeapElmt->uiVaContextID        = vaContextHeap->uiAllocatedHeapElements + i;
            vacontextHeapElmt->pVaContext           = nullptr;
        }
        vaContextHeap->uiAllocatedHeapElements     += DDI_MEDIA_HEAP_INCREMENTAL_SIZE;
    }

    vacontextHeapElmt                               = (PDDI_MEDIA_VACONTEXT_HEAP_ELEMENT)vaContextHeap->pFirstFreeHeapElement;
    vaContextHeap->pFirstFreeHeapElement            = vacontextHeapElmt->pNextFree;
    return vacontextHeapElmt;
}


void DdiMediaUtil_ReleasePVAContextFromHeap(PDDI_MEDIA_HEAP vaContextHeap, uint32_t vaContextID)
{
    DDI_CHK_NULL(vaContextHeap, "nullptr vaContextHeap", );
    DDI_CHK_LESS(vaContextID, vaContextHeap->uiAllocatedHeapElements, "invalid context id", );
    PDDI_MEDIA_VACONTEXT_HEAP_ELEMENT vaContextHeapBase = (PDDI_MEDIA_VACONTEXT_HEAP_ELEMENT)vaContextHeap->pHeapBase;
    PDDI_MEDIA_VACONTEXT_HEAP_ELEMENT vaContextHeapElmt = &vaContextHeapBase[vaContextID];
    DDI_CHK_NULL(vaContextHeapElmt->pVaContext, "context is already released", );
    void *firstFree                        = vaContextHeap->pFirstFreeHeapElement;
    vaContextHeap->pFirstFreeHeapElement   = (void*)vaContextHeapElmt;
    vaContextHeapElmt->pNextFree           = (PDDI_MEDIA_VACONTEXT_HEAP_ELEMENT)firstFree;
    vaContextHeapElmt->pVaContext          = nullptr;
}

void DdiMediaUtil_UnRefBufObjInMediaBuffer(PDDI_MEDIA_BUFFER buf)
{
    mos_bo_unreference(buf->bo);
}

// Open Intel's Graphics Device to get the file descriptor
int32_t DdiMediaUtil_OpenGraphicsAdaptor(char *devName)
{
    struct stat st;
    int32_t    hDevice = -1;
    if(nullptr == devName)
    {
        DDI_ASSERTMESSAGE("Invalid Graphics Node");
        return -1;
    }

    hDevice = open (devName, O_RDWR);
    if (-1 == hDevice)
    {
        DDI_ASSERTMESSAGE("Cannot open '%s': %d, %s.", devName, errno, strerror (errno));
        return -1;
    }

    if (-1 == fstat (hDevice, &st))
    {
        DDI_ASSERTMESSAGE("Cannot identify '%s': %d, %s.", devName, errno, strerror (errno));
        close(hDevice);
        return -1;
    }

    if (!S_ISCHR (st.st_mode))
    {
        DDI_ASSERTMESSAGE("%s is no device.", devName);
        close(hDevice);
        return -1;
    }
    return hDevice;
}

VAStatus DdiMediaUtil_UnRegisterRTSurfaces(
    VADriverContextP    ctx,
    PDDI_MEDIA_SURFACE surface)
{
    DDI_CHK_NULL(ctx,"nullptr context!", VA_STATUS_ERROR_INVALID_CONTEXT);
    PDDI_MEDIA_CONTEXT mediaCtx   = DdiMedia_GetMediaContext(ctx);
    DDI_CHK_NULL(mediaCtx,"nullptr mediaCtx!", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(surface, "nullptr surface!", VA_STATUS_ERROR_INVALID_PARAMETER);

    //Look through all decode contexts to unregister the surface in each decode context's RTtable.
    if (mediaCtx->pDecoderCtxHeap != nullptr)
    {
        PDDI_MEDIA_VACONTEXT_HEAP_ELEMENT decVACtxHeapBase;

        DdiMediaUtil_LockMutex(&mediaCtx->DecoderMutex);
        decVACtxHeapBase  = (PDDI_MEDIA_VACONTEXT_HEAP_ELEMENT)mediaCtx->pDecoderCtxHeap->pHeapBase;
        for (uint32_t j = 0; j < mediaCtx->pDecoderCtxHeap->uiAllocatedHeapElements; j++)
        {
            if (decVACtxHeapBase[j].pVaContext != nullptr)
            {
                PDDI_DECODE_CONTEXT  decCtx = (PDDI_DECODE_CONTEXT)decVACtxHeapBase[j].pVaContext;
                if (decCtx && decCtx->m_ddiDecode)
                {
                    //not check the return value since the surface may not be registered in the context. pay attention to LOGW.
                    decCtx->m_ddiDecode->UnRegisterRTSurfaces(&decCtx->RTtbl, surface);
                }
            }
        }
        DdiMediaUtil_UnLockMutex(&mediaCtx->DecoderMutex);
    }
    if (mediaCtx->pEncoderCtxHeap != nullptr)
    {
        PDDI_MEDIA_VACONTEXT_HEAP_ELEMENT pEncVACtxHeapBase;

        DdiMediaUtil_LockMutex(&mediaCtx->EncoderMutex);
        pEncVACtxHeapBase  = (PDDI_MEDIA_VACONTEXT_HEAP_ELEMENT)mediaCtx->pEncoderCtxHeap->pHeapBase;
        for (uint32_t j = 0; j < mediaCtx->pEncoderCtxHeap->uiAllocatedHeapElements; j++)
        {
            if (pEncVACtxHeapBase[j].pVaContext != nullptr)
            {
                PDDI_ENCODE_CONTEXT  pEncCtx = (PDDI_ENCODE_CONTEXT)pEncVACtxHeapBase[j].pVaContext;
                if (pEncCtx && pEncCtx->m_encode)
                {
                    //not check the return value since the surface may not be registered in the context. pay attention to LOGW.
                    pEncCtx->m_encode->UnRegisterRTSurfaces(&pEncCtx->RTtbl, surface);
                }
            }
        }
        DdiMediaUtil_UnLockMutex(&mediaCtx->EncoderMutex);
    }

    return VA_STATUS_SUCCESS;
}

VAStatus DdiMediaUtil_SetMediaResetEnableFlag(PDDI_MEDIA_CONTEXT mediaCtx)
{
    bool enableReset = false;
    mediaCtx->bMediaResetEnable = false;
    
    DDI_CHK_NULL(mediaCtx,"nullptr mediaCtx!", VA_STATUS_ERROR_INVALID_CONTEXT);
    
    if(!MEDIA_IS_SKU(&mediaCtx->SkuTable, FtrSWMediaReset))
    {
        mediaCtx->bMediaResetEnable = false;
        return VA_STATUS_SUCCESS;
    }

    mediaCtx->bMediaResetEnable = true;
#if (_DEBUG || _RELEASE_INTERNAL)
    ReadUserSettingForDebug(
        mediaCtx->m_userSettingPtr,
        enableReset,
        __MEDIA_USER_FEATURE_VALUE_MEDIA_RESET_ENABLE,
        MediaUserSetting::Group::Device);
    mediaCtx->bMediaResetEnable = enableReset;
#endif

    if(!mediaCtx->bMediaResetEnable)
    {
        return VA_STATUS_SUCCESS;
    }

    char* mediaResetEnv = getenv("INTEL_MEDIA_RESET_WATCHDOG");
    if(mediaResetEnv)
    {
        mediaCtx->bMediaResetEnable = strcmp(mediaResetEnv, "1") ? false : true;
        return VA_STATUS_SUCCESS;
    }

    return VA_STATUS_SUCCESS;
}
