/*
* Copyright (c) 2010-2020, 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      vphal_render_hdr_base.cpp
//! \brief     Unified VP HAL HDR Implementation 
//!
//!
//! \file     vphal_render_hdr_base.cpp
//! \brief    Common interface and structure used in HDR
//! \details  Common interface and structure used in HDR which are platform independent
//!

#include "vphal.h"
#include "vphal_renderer.h"
#include "vphal_render_hdr_base.h"
#include "renderhal_platform_interface.h"
#include "vphal_render_hdr_g9_base.h"


#if (_DEBUG || _RELEASE_INTERNAL)
static bool sEnableKernelDump = false;
#endif

//!
//! \brief    Initialize HDR state
//! \details  Initialize HDR state
//! \param    PVPHAL_HDR_STATE pHdrState
//!           [in,out] HDR State pointer
//! \param    const VphalSettings* pSettings
//!           [in] Pointer to VPHAL Setting
//! \param    Kdll_KernelCache * pKernelCache
//!           [in] Pointer to kernel cache
//! \return   void
//!
MOS_STATUS VpHal_HdrInitialize(
    PVPHAL_HDR_STATE         pHdrState,
    const VphalSettings      *pSettings,
    Kdll_State               *pKernelDllState)
{
    int32_t    i;
    uint32_t   dwSize = 0;
    bool       bAllocated = false;
    MOS_NULL_RENDERING_FLAGS    NullRenderingFlags;
    MOS_STATUS                  eStatus;
    PRENDERHAL_INTERFACE        pRenderHal;
    MOS_USER_FEATURE_VALUE_DATA UserFeatureData;

    eStatus = MOS_STATUS_SUCCESS;

    VPHAL_PUBLIC_CHK_NULL(pHdrState);
    VPHAL_PUBLIC_CHK_NULL(pHdrState->pOsInterface);
    VPHAL_PUBLIC_CHK_NULL(pHdrState->pSkuTable);
    VPHAL_PUBLIC_CHK_NULL(pKernelDllState);

    NullRenderingFlags          = 
                    pHdrState->pOsInterface->pfnGetNullHWRenderFlags(pHdrState->pOsInterface);
    pHdrState->bNullHwRenderHdr = false;
    pRenderHal                  = pHdrState->pRenderHal;

    VPHAL_PUBLIC_CHK_NULL(pRenderHal);

    // Setup disable render flag controlled by a user feature key for validation purpose
    pHdrState->bDisableRender = (pSettings->disableHdr) ? true : false;

    // Setup interface to KDLL
    pHdrState->pKernelCache   = &pKernelDllState->ComponentKernelCache;

    eStatus = MOS_STATUS_SUCCESS;

    pHdrState->uiSplitFramePortions = 1;

    // If user set the user feature key, then the uiSplitFramePortions specified will always be used,
    // no matter HW support preemption or not.
    // If it is not set, and HW doesn't support preemption, then the split portions
    // will be calculted based on resolution.
    if (!pHdrState->bForceSplitFrame)
    {
        if (MEDIA_IS_SKU(pHdrState->pSkuTable, FtrMediaMidBatchPreempt) ||
            MEDIA_IS_SKU(pHdrState->pSkuTable, FtrMediaThreadGroupLevelPreempt) ||
            MEDIA_IS_SKU(pHdrState->pSkuTable, FtrMediaMidThreadLevelPreempt))
        {
            pHdrState->uiSplitFramePortions = 1;
            pHdrState->bForceSplitFrame     = true;
        }
    }

    pHdrState->bFtrComputeWalker = false;
    pHdrState->uiSplitFramePortions = 1;

    pHdrState->bVeboxpreprocessed = false;

    VpHal_HdrInitInterface_g9(pHdrState);            // Total number of slices
    
finish:
    return eStatus;
}

//!
//! \brief    Destroy HDR state
//! \details  Release local resources.
//! \param    PVPHAL_HDR_STATE pHdrState
//!           [in] Pointer to HDR state
//! \return   MOS_STATUS
//!           MOS_STATUS_SUCCESS if successful, otherwise failed
//!
MOS_STATUS VpHal_HdrDestroy(
    PVPHAL_HDR_STATE    pHdrState)
{
    MOS_STATUS                  eStatus = MOS_STATUS_SUCCESS;
    VPHAL_PUBLIC_CHK_NULL(pHdrState);

    VpHal_HdrDestroyInterface_g9(pHdrState);

    // Free allocations
    if (pHdrState->pfnFreeResources)
    {
        pHdrState->pfnFreeResources(pHdrState);
    }

finish:
    return eStatus;
}

//!
//! \brief    Assemble the HDR kernel per layer stages
//! \details  Contruct a case id from the input information, and look up the configuration entry in the table.
//! \param    PVPHAL_HDR_STATE pHdrState
//!           [in] Pointer to HDR state
//! \param    PVPHAL_SURFACE pSource
//!           [in] Pointer to source surface
//! \param    PVPHAL_SURFACE pTarget
//!           [in] Pointer to target surface
//! \param    HDRStageConfigEntry *pConfigEntry
//!           [out] Pointer to configuration entry
//! \return   bool
//!           True if find proper configuration entry, otherwise false
//!
static bool Vphal_HdrToneMappingStagesAssemble(
    PVPHAL_HDR_STATE       pHdrState,
    PVPHAL_SURFACE         pSource,
    PVPHAL_SURFACE         pTarget,
    HDRStageConfigEntry   *pConfigEntry)
{
    HDRCaseID id = { 0 };

    VPHAL_RENDER_ASSERT(pHdrState);
    VPHAL_RENDER_ASSERT(pSource);
    VPHAL_RENDER_ASSERT(pTarget);
    VPHAL_RENDER_ASSERT(pConfigEntry);

    if (!pHdrState || !pSource || !pTarget || !pConfigEntry)
        return false;

    // Because FP16 format can represent both SDR or HDR, we need do judgement here.
    // We need this information because we dont have unified tone mapping algorithm for various scenarios(H2S/H2H).
    // To do this, we make two assumptions:
    // 1. This colorspace will be set to BT709/Gamma1.0 from APP, so such information can NOT be used to check HDR.
    // 2. If APP pass any HDR metadata, it indicates this is HDR.
    id.InputXDR     = (pSource->pHDRParams &&
                      ((pSource->pHDRParams->EOTF == VPHAL_HDR_EOTF_SMPTE_ST2084) || IS_RGB64_FLOAT_FORMAT(pSource->Format))) ? 1 : 0;
    id.InputGamut   = IS_COLOR_SPACE_BT2020(pSource->ColorSpace);
    id.OutputXDR    = (pTarget->pHDRParams &&
                      ((pTarget->pHDRParams->EOTF == VPHAL_HDR_EOTF_SMPTE_ST2084) || IS_RGB64_FLOAT_FORMAT(pTarget->Format))) ? 1 : 0;
    id.OutputGamut  = IS_COLOR_SPACE_BT2020(pTarget->ColorSpace);
    id.OutputLinear = IS_RGB64_FLOAT_FORMAT(pTarget->Format) ? 1 : 0;

    if (pHdrState->pHDRStageConfigTable)
    {
        pConfigEntry->value = pHdrState->pHDRStageConfigTable[id.index];
    }
    else
    {
        pConfigEntry->Invalid = 1;
    }

    if (pConfigEntry->Invalid == 1)
    {
        VPHAL_RENDER_ASSERTMESSAGE("Tone mapping stages assembling failed, please reexamine the usage case(case id %d)! "
            "If it is definitely a correct usage, please add an entry in HDRStageEnableTable.", id.index);
    }

    return (pConfigEntry->Invalid != 1);
}

//!
//! \brief    Update per layer pipeline states and return update mask for each layer
//! \details  Update per layer pipeline states and return update mask for each layer
//! \param    PVPHAL_HDR_STATE pHdrStatee
//!           [in] Pointer to HDR state
//! \param    uint32_t* pdwUpdateMask
//!           [out] Pointer to update mask
//! \return   MOS_STATUS
//!
MOS_STATUS VpHal_HdrUpdatePerLayerPipelineStates(
    PVPHAL_HDR_STATE    pHdrState,
    uint32_t*           pdwUpdateMask)
{
    MOS_STATUS           eStatus              = MOS_STATUS_UNKNOWN;
    uint32_t             i                    = 0;
    PVPHAL_SURFACE       pSrc                 = nullptr;
    PVPHAL_SURFACE       pTarget              = nullptr;
    VPHAL_HDR_LUT_MODE   CurrentLUTMode       = VPHAL_HDR_LUT_MODE_NONE;
    VPHAL_GAMMA_TYPE     CurrentEOTF          = VPHAL_GAMMA_NONE;            //!< EOTF
    VPHAL_GAMMA_TYPE     CurrentOETF          = VPHAL_GAMMA_NONE;            //!< OETF
    VPHAL_HDR_MODE       CurrentHdrMode       = VPHAL_HDR_MODE_NONE;      //!< Hdr Mode
    VPHAL_HDR_CCM_TYPE   CurrentCCM           = VPHAL_HDR_CCM_NONE;           //!< CCM Mode
    VPHAL_HDR_CCM_TYPE   CurrentCCMExt1       = VPHAL_HDR_CCM_NONE;       //!< CCM Ext1 Mode
    VPHAL_HDR_CCM_TYPE   CurrentCCMExt2       = VPHAL_HDR_CCM_NONE;       //!< CCM Ext2 Mode
    VPHAL_HDR_CSC_TYPE   CurrentPriorCSC      = VPHAL_HDR_CSC_NONE;      //!< Prior CSC Mode
    VPHAL_HDR_CSC_TYPE   CurrentPostCSC       = VPHAL_HDR_CSC_NONE;       //!< Post CSC Mode
    HDRStageConfigEntry  ConfigEntry          = { 0 };
    HDRStageEnables      StageEnables         = { 0 };

    VPHAL_RENDER_CHK_NULL(pHdrState);
    VPHAL_RENDER_CHK_NULL(pdwUpdateMask);
    VPHAL_PUBLIC_CHK_NULL(pHdrState->pTargetSurf[0]);

    *pdwUpdateMask = 0;

    pTarget = (PVPHAL_SURFACE)pHdrState->pTargetSurf[0];

    for (i = 0; i < VPHAL_MAX_HDR_INPUT_LAYER; i++)
    {
        if (pHdrState->pSrcSurf[i] == nullptr)
        {
            pHdrState->LUTMode[i]   = VPHAL_HDR_LUT_MODE_NONE;
            pHdrState->EOTFGamma[i] = VPHAL_GAMMA_NONE;
            pHdrState->OETFGamma[i] = VPHAL_GAMMA_NONE;
            pHdrState->CCM[i]       = VPHAL_HDR_CCM_NONE;
            pHdrState->CCMExt1[i]   = VPHAL_HDR_CCM_NONE;
            pHdrState->CCMExt2[i]   = VPHAL_HDR_CCM_NONE;
            pHdrState->HdrMode[i]   = VPHAL_HDR_MODE_NONE;
            pHdrState->PriorCSC[i]  = VPHAL_HDR_CSC_NONE;
            pHdrState->PostCSC[i]   = VPHAL_HDR_CSC_NONE;

            pHdrState->StageEnableFlags[i].value = 0;
            MOS_ZeroMemory(&pHdrState->HDRLastFrameSourceParams[i], sizeof(VPHAL_HDR_PARAMS));

            continue;
        }

        pSrc = (PVPHAL_SURFACE)pHdrState->pSrcSurf[i];

        CurrentLUTMode  = VPHAL_HDR_LUT_MODE_NONE;
        CurrentEOTF     = VPHAL_GAMMA_NONE;
        CurrentOETF     = VPHAL_GAMMA_NONE;
        CurrentHdrMode  = VPHAL_HDR_MODE_NONE;
        CurrentCCM      = VPHAL_HDR_CCM_NONE;
        CurrentCCMExt1  = VPHAL_HDR_CCM_NONE;
        CurrentCCMExt2  = VPHAL_HDR_CCM_NONE;
        CurrentPriorCSC = VPHAL_HDR_CSC_NONE;
        CurrentPostCSC  = VPHAL_HDR_CSC_NONE;

        if (!Vphal_HdrToneMappingStagesAssemble(pHdrState, pSrc, pTarget, &ConfigEntry))
        {
            eStatus = MOS_STATUS_INVALID_PARAMETER;
            goto finish;
        }

        CurrentHdrMode = (VPHAL_HDR_MODE)ConfigEntry.PWLF;
        CurrentCCM     = (VPHAL_HDR_CCM_TYPE)ConfigEntry.CCM;
        CurrentCCMExt1 = (VPHAL_HDR_CCM_TYPE)ConfigEntry.CCMExt1;
        CurrentCCMExt2 = (VPHAL_HDR_CCM_TYPE)ConfigEntry.CCMExt2;

        // So far only enable auto mode in H2S cases.
        if (CurrentHdrMode == VPHAL_HDR_MODE_TONE_MAPPING &&
            pSrc->pHDRParams                              &&
            pSrc->pHDRParams->bAutoMode                   &&
            pSrc->SurfType == SURF_IN_PRIMARY)
        {
            CurrentHdrMode = VPHAL_HDR_MODE_TONE_MAPPING_AUTO_MODE;
        }

        StageEnables.value             = 0;
        StageEnables.CCMEnable         = (CurrentCCM     != VPHAL_HDR_CCM_NONE ) ? 1 : 0;
        StageEnables.PWLFEnable        = (CurrentHdrMode != VPHAL_HDR_MODE_NONE) ? 1 : 0;
        StageEnables.CCMExt1Enable     = (CurrentCCMExt1 != VPHAL_HDR_CCM_NONE ) ? 1 : 0;
        StageEnables.CCMExt2Enable     = (CurrentCCMExt2 != VPHAL_HDR_CCM_NONE ) ? 1 : 0;
        StageEnables.GamutClamp1Enable = ConfigEntry.GamutClamp1;
        StageEnables.GamutClamp2Enable = ConfigEntry.GamutClamp2;

        if (IS_YUV_FORMAT(pSrc->Format) || IS_ALPHA_YUV_FORMAT(pSrc->Format))
        {
            StageEnables.PriorCSCEnable = 1;
        }

        if (!IS_RGB64_FLOAT_FORMAT(pSrc->Format) &&
            (StageEnables.CCMEnable || StageEnables.PWLFEnable || StageEnables.CCMExt1Enable || StageEnables.CCMExt2Enable))
        {
            StageEnables.EOTFEnable = 1;
        }

        if (!IS_RGB64_FLOAT_FORMAT(pTarget->Format) && (StageEnables.EOTFEnable || IS_RGB64_FLOAT_FORMAT(pSrc->Format)))
        {
            StageEnables.OETFEnable = 1;
        }

        if (IS_YUV_FORMAT(pTarget->Format))
        {
            StageEnables.PostCSCEnable = 1;
        }

        if (pSrc->SurfType == SURF_IN_PRIMARY && pHdrState->GlobalLutMode != VPHAL_HDR_LUT_MODE_3D)
        {
            CurrentLUTMode = VPHAL_HDR_LUT_MODE_2D;
        }
        else
        {
            CurrentLUTMode = VPHAL_HDR_LUT_MODE_3D;
        }

        // Neither 1D nor 3D LUT is needed in linear output case.
        if (IS_RGB64_FLOAT_FORMAT(pHdrState->pTargetSurf[0]->Format))
        {
            CurrentLUTMode = VPHAL_HDR_LUT_MODE_NONE;
        }

        // EOTF/CCM/Tone Mapping/OETF require RGB input
        // So if prior CSC is needed, it will always be YUV to RGB conversion
        if (StageEnables.PriorCSCEnable)
        {
            if (pSrc->ColorSpace == CSpace_BT601)
            {
                CurrentPriorCSC = VPHAL_HDR_CSC_YUV_TO_RGB_BT601;
            }
            else if (pSrc->ColorSpace == CSpace_BT709)
            {
                CurrentPriorCSC = VPHAL_HDR_CSC_YUV_TO_RGB_BT709;
            }
            else if (pSrc->ColorSpace == CSpace_BT2020)
            {
                CurrentPriorCSC = VPHAL_HDR_CSC_YUV_TO_RGB_BT2020;
            }
            else if (pSrc->ColorSpace == CSpace_BT2020_FullRange)
            {
                CurrentPriorCSC = VPHAL_HDR_CSC_YUV_TO_RGB_BT2020;
            }
            else
            {
                VPHAL_RENDER_ASSERTMESSAGE("Color Space %d Not found.", pSrc->ColorSpace);
                eStatus = MOS_STATUS_INVALID_PARAMETER;
                goto finish;
            }
        }

        if (StageEnables.EOTFEnable)
        {
            if ((!pSrc->pHDRParams) ||
                (pSrc->pHDRParams &&
                 (pSrc->pHDRParams->EOTF == VPHAL_HDR_EOTF_TRADITIONAL_GAMMA_SDR ||
                  pSrc->pHDRParams->EOTF == VPHAL_HDR_EOTF_TRADITIONAL_GAMMA_HDR)))
            {
                // Mark tranditional HDR/SDR gamma as the same type
                CurrentEOTF = VPHAL_GAMMA_TRADITIONAL_GAMMA;
            }
            else if (pSrc->pHDRParams &&
                     pSrc->pHDRParams->EOTF == VPHAL_HDR_EOTF_SMPTE_ST2084)
            {
                CurrentEOTF = VPHAL_GAMMA_SMPTE_ST2084;
            }
            else if (pSrc->pHDRParams &&
                     pSrc->pHDRParams->EOTF == VPHAL_HDR_EOTF_BT1886)
            {
                CurrentEOTF = VPHAL_GAMMA_BT1886;
            }
            else
            {
                VPHAL_RENDER_ASSERTMESSAGE("Invalid EOTF setting for tone mapping");
                eStatus = MOS_STATUS_INVALID_PARAMETER;
                goto finish;
            }
        }

        if (StageEnables.OETFEnable)
        {
            if ((!pTarget->pHDRParams) ||
                (pTarget->pHDRParams &&
                 (pTarget->pHDRParams->EOTF == VPHAL_HDR_EOTF_TRADITIONAL_GAMMA_SDR ||
                  pTarget->pHDRParams->EOTF == VPHAL_HDR_EOTF_TRADITIONAL_GAMMA_HDR)))
            {
                CurrentOETF = VPHAL_GAMMA_SRGB;
            }
            else if (pTarget->pHDRParams &&
                     pTarget->pHDRParams->EOTF == VPHAL_HDR_EOTF_SMPTE_ST2084)
            {
                CurrentOETF = VPHAL_GAMMA_SMPTE_ST2084;
            }
            else
            {
                VPHAL_RENDER_ASSERTMESSAGE("Invalid EOTF setting for tone mapping");
                eStatus = MOS_STATUS_INVALID_PARAMETER;
                goto finish;
            }
        }

        // OETF will output RGB surface
        // So if post CSC is needed, it will always be RGB to YUV conversion
        if (StageEnables.PostCSCEnable)
        {
            if (pTarget->ColorSpace == CSpace_BT601)
            {
                CurrentPostCSC = VPHAL_HDR_CSC_RGB_TO_YUV_BT601;
            }
            else if (pTarget->ColorSpace == CSpace_BT709)
            {
                CurrentPostCSC = VPHAL_HDR_CSC_RGB_TO_YUV_BT709;
            }
            else if (pTarget->ColorSpace == CSpace_BT709_FullRange)
            {
                // CSC for target BT709_FULLRANGE is only exposed to Vebox Preprocessed HDR cases.
                CurrentPostCSC = VPHAL_HDR_CSC_RGB_TO_YUV_BT709_FULLRANGE;
            }
            else if (pTarget->ColorSpace == CSpace_BT2020 ||
                     pTarget->ColorSpace == CSpace_BT2020_FullRange)
            {
                CurrentPostCSC = VPHAL_HDR_CSC_RGB_TO_YUV_BT2020;
            }
            else
            {
                VPHAL_RENDER_ASSERTMESSAGE("Color Space %d Not found.", pTarget->ColorSpace);
                eStatus = MOS_STATUS_INVALID_PARAMETER;
                goto finish;
            }
        }

        if (pHdrState->LUTMode[i]   != CurrentLUTMode  ||
            pHdrState->EOTFGamma[i] != CurrentEOTF     ||
            pHdrState->OETFGamma[i] != CurrentOETF     ||
            pHdrState->CCM[i]       != CurrentCCM      ||
            pHdrState->CCMExt1[i]   != CurrentCCMExt1  ||
            pHdrState->CCMExt2[i]   != CurrentCCMExt2  ||
            pHdrState->HdrMode[i]   != CurrentHdrMode  ||
            pHdrState->PriorCSC[i]  != CurrentPriorCSC ||
            pHdrState->PostCSC[i]   != CurrentPostCSC)
        {
            *pdwUpdateMask |= (1 << i);
        }
        
        if (pSrc->pHDRParams)
        {
            if (memcmp(pSrc->pHDRParams, &pHdrState->HDRLastFrameSourceParams[i], sizeof(VPHAL_HDR_PARAMS)))
            {
                *pdwUpdateMask |= (1 << i);
                pHdrState->HDRLastFrameSourceParams[i] = *pSrc->pHDRParams;
            }
        }
        else
        {
            MOS_ZeroMemory(&pHdrState->HDRLastFrameSourceParams[i], sizeof(VPHAL_HDR_PARAMS));
        }

        pHdrState->LUTMode[i]          = CurrentLUTMode;
        pHdrState->EOTFGamma[i]        = CurrentEOTF;
        pHdrState->OETFGamma[i]        = CurrentOETF;
        pHdrState->CCM[i]              = CurrentCCM;
        pHdrState->CCMExt1[i]          = CurrentCCMExt1;
        pHdrState->CCMExt2[i]          = CurrentCCMExt2;
        pHdrState->HdrMode[i]          = CurrentHdrMode;
        pHdrState->PriorCSC[i]         = CurrentPriorCSC;
        pHdrState->PostCSC[i]          = CurrentPostCSC;
        pHdrState->StageEnableFlags[i] = StageEnables;
    }

    if (pTarget->pHDRParams)
    {
        if (memcmp(pTarget->pHDRParams, &pHdrState->HDRLastFrameTargetParams, sizeof(VPHAL_HDR_PARAMS)))
        {
            *pdwUpdateMask |= (1 << VPHAL_MAX_HDR_INPUT_LAYER);
            pHdrState->HDRLastFrameTargetParams = *pTarget->pHDRParams;
        }
    }
    else
    {
        MOS_ZeroMemory(&pHdrState->HDRLastFrameTargetParams, sizeof(VPHAL_HDR_PARAMS));
    }
    pHdrState->dwUpdateMask = *pdwUpdateMask;
    eStatus = MOS_STATUS_SUCCESS;

finish:
    return eStatus;
}

//!
//! \brief    Checks to see if HDR is needed and supported
//! \details  Checks to see if HDR is needed and supported
//! \param    pRenderer
//            [in] Pointer to VphalRenderer
//! \param    pBeNeeded
//!           [out] 1 Needed 0 not Needed
//! \return   MOS_STATUS
//!           MOS_STATUS_SUCCESS if successful, otherwise failed
//!
MOS_STATUS VpHal_HdrIsNeeded(
    VphalRenderer         *pRenderer,
    bool*                  pBeNeeded)
{
    MOS_STATUS                  eStatus;
    eStatus = MOS_STATUS_SUCCESS;

    VPHAL_PUBLIC_CHK_NULL(pRenderer);
    VPHAL_PUBLIC_CHK_NULL(pBeNeeded);

    // Check whether Hdr is supported by platform
    if (!MEDIA_IS_SKU(pRenderer->GetSkuTable(), FtrHDR) ||
         pRenderer->pHdrState->bDisableRender)
    {
        *pBeNeeded = false;
        VPHAL_RENDER_ASSERTMESSAGE("Hdr not enabled or disabled for this platform.");
        goto finish;
    }

    *pBeNeeded = true;

finish:
    return eStatus;
}

//!
//! \brief    Set up HDR Render Data
//! \details  Set up HDR render data, including kernel information, input surface's block size
//! \param    PVPHAL_HDR_STATE pHdrState
//!           [in] Pointer to HDR state
//! \param    PVPHAL_HDR_RENDER_DATA pRenderData
//!           [out] Pointer to HDR render data
//! \param    int32_t iKUID
//!           [in] Kernel unique ID
//! \param    int32_t iKDTIndex
//            [in] KDT index.
//! \return   MOS_STATUS
//!           MOS_STATUS_SUCCESS if successful, otherwise failed
//!
MOS_STATUS VpHal_HdrSetupRenderData(
    PVPHAL_HDR_STATE        pHdrState,
    PVPHAL_HDR_RENDER_DATA  pRenderData,
    int32_t                 iKUID,
    int32_t                 iKDTIndex)
{
    int32_t                         iBlockWd;                                       // Block width
    int32_t                         iBlockHt;                                       // Block Height
    MOS_STATUS                      eStatus;                                        // Return code
    PRENDERHAL_INTERFACE            pRenderHal;
    Kdll_CacheEntry                 *pCacheEntryTable;                              // Kernel Cache Entry table
    PVPHAL_SURFACE                  pSrcSurface;
    uint32_t                        dwSrcWidth;
    uint32_t                        dwSrcHeight;
    uint32_t                        i;

    VPHAL_RENDER_CHK_NULL(pHdrState);
    VPHAL_RENDER_CHK_NULL(pRenderData);
    VPHAL_RENDER_CHK_NULL(pHdrState->pRenderHal);

    MOS_ZeroMemory(pRenderData, sizeof(VPHAL_HDR_RENDER_DATA));

    // Initialize Variables
    eStatus = MOS_STATUS_SUCCESS;

    if (iKDTIndex == KERNEL_HDR_MANDATORY_G9)
    {
        for (i = 0; i < pHdrState->uSourceCount; i++)
        {
            if (pHdrState->pSrcSurf[i])
            {
                if (pHdrState->pSrcSurf[i]->SurfType == SURF_IN_PRIMARY)
                {
                    if (pHdrState->pSrcSurf[i]->pIEFParams)
                    {
                        pRenderData->pIEFParams = pHdrState->pSrcSurf[i]->pIEFParams;
                    }


                    if (pHdrState->pSrcSurf[i]->Rotation == VPHAL_ROTATION_IDENTITY ||
                        pHdrState->pSrcSurf[i]->Rotation == VPHAL_ROTATION_180 ||
                        pHdrState->pSrcSurf[i]->Rotation == VPHAL_MIRROR_HORIZONTAL ||
                        pHdrState->pSrcSurf[i]->Rotation == VPHAL_MIRROR_VERTICAL)
                    {
                        pRenderData->fPrimaryLayerScaleX = (float)(pHdrState->pSrcSurf[i]->rcDst.right - pHdrState->pSrcSurf[i]->rcDst.left) /
                                                           (float)(pHdrState->pSrcSurf[i]->rcSrc.right - pHdrState->pSrcSurf[i]->rcSrc.left);
                        pRenderData->fPrimaryLayerScaleY = (float)(pHdrState->pSrcSurf[i]->rcDst.bottom - pHdrState->pSrcSurf[i]->rcDst.top) /
                                                           (float)(pHdrState->pSrcSurf[i]->rcSrc.bottom - pHdrState->pSrcSurf[i]->rcSrc.top);
                    }
                    else
                    {
                        // VPHAL_ROTATION_90 || VPHAL_ROTATION_270 || 
                        // VPHAL_ROTATE_90_MIRROR_HORIZONTAL || VPHAL_ROTATE_90_MIRROR_VERTICAL
                        pRenderData->fPrimaryLayerScaleX = (float)(pHdrState->pSrcSurf[i]->rcDst.right - pHdrState->pSrcSurf[i]->rcDst.left) /
                                                           (float)(pHdrState->pSrcSurf[i]->rcSrc.bottom - pHdrState->pSrcSurf[i]->rcSrc.top);
                        pRenderData->fPrimaryLayerScaleY = (float)(pHdrState->pSrcSurf[i]->rcDst.bottom - pHdrState->pSrcSurf[i]->rcDst.top) /
                                                           (float)(pHdrState->pSrcSurf[i]->rcSrc.right - pHdrState->pSrcSurf[i]->rcSrc.left);
                    }

                    pRenderData->PrimaryLayerFormat = pHdrState->pSrcSurf[i]->Format;
                }
            }
        }

        // Store pointer to Kernel Parameter
        pRenderData->pKernelParam[iKDTIndex] = &pHdrState->pKernelParamTable[iKDTIndex];
        pCacheEntryTable                     = pHdrState->pKernelCache->pCacheEntries;

        VPHAL_RENDER_CHK_NULL(pCacheEntryTable);

        // Set Parameters for Kernel Entry
        MOS_ZeroMemory(&pRenderData->KernelEntry[iKDTIndex], sizeof(Kdll_CacheEntry));

        // Set the curbe length
        pRenderData->iCurbeLength = (pRenderData->pKernelParam[iKDTIndex]->CURBE_Length) * GRF_SIZE;

        // Set Parameters for Kernel Entry
        pRenderData->KernelEntry[iKDTIndex].iKUID   = iKUID;
        pRenderData->KernelEntry[iKDTIndex].iKCID   = -1;
        pRenderData->KernelEntry[iKDTIndex].iSize   = pCacheEntryTable[iKUID].iSize;
        pRenderData->KernelEntry[iKDTIndex].pBinary = pCacheEntryTable[iKUID].pBinary;
        pRenderData->KernelEntry[iKDTIndex].szName  = pCacheEntryTable[iKUID].szName;

        pRenderData->PerfTag  = (VPHAL_PERFTAG)(VPHAL_HDR_GENERIC + pHdrState->uSourceCount);

        // Get per block resulution
        iBlockWd = pRenderData->pKernelParam[iKDTIndex]->block_width;
        iBlockHt = pRenderData->pKernelParam[iKDTIndex]->block_height;

        // Calcualte block numbers to process
        dwSrcWidth            = pHdrState->pTargetSurf[0]->rcDst.right - pHdrState->pTargetSurf[0]->rcDst.left;
        dwSrcHeight           = pHdrState->pTargetSurf[0]->rcDst.bottom - pHdrState->pTargetSurf[0]->rcDst.top;
        pRenderData->iBlocksX = (dwSrcWidth + iBlockWd - 1) / iBlockWd;
        pRenderData->iBlocksY = (dwSrcHeight + iBlockHt -1) / iBlockHt;

        // Set up Scoreboard parameters
        pRenderData->ScoreboardParams.ScoreboardMask = 0;
        pRenderData->ScoreboardParams.ScoreboardType = 1;

        // Set up AVS parameters
        pRenderData->pAVSParameters[0] = &pHdrState->AVSParameters[0];
        pRenderData->pAVSParameters[1] = &pHdrState->AVSParameters[1];
    }
    else if (iKDTIndex == KERNEL_HDR_PREPROCESS)
    {
        // Store pointer to Kernel Parameter
        pRenderData->pKernelParam[iKDTIndex] = &pHdrState->pKernelParamTable[iKDTIndex];
        pCacheEntryTable = pHdrState->pKernelCache->pCacheEntries;

        VPHAL_RENDER_CHK_NULL(pCacheEntryTable);

        // Set Parameters for Kernel Entry
        MOS_ZeroMemory(&pRenderData->KernelEntry[iKDTIndex], sizeof(Kdll_CacheEntry));

        // Set the curbe length
        pRenderData->iCurbeLength = (pRenderData->pKernelParam[iKDTIndex]->CURBE_Length) * GRF_SIZE;

        // Set Parameters for Kernel Entry
        pRenderData->KernelEntry[iKDTIndex].iKUID = iKUID;
        pRenderData->KernelEntry[iKDTIndex].iKCID = -1;
        pRenderData->KernelEntry[iKDTIndex].iSize = pCacheEntryTable[iKUID].iSize;
        pRenderData->KernelEntry[iKDTIndex].pBinary = pCacheEntryTable[iKUID].pBinary;
        pRenderData->KernelEntry[iKDTIndex].szName = pCacheEntryTable[iKUID].szName;

        // Set up Scoreboard parameters
        pRenderData->ScoreboardParams.ScoreboardMask = 0;
        pRenderData->ScoreboardParams.ScoreboardType = 1;
    }
    else
    {
        VPHAL_RENDER_ASSERTMESSAGE("Unknown HDR kernel");
        eStatus = MOS_STATUS_UNKNOWN;
        goto finish;
    }

finish:
    return eStatus;
}

//!
//! \brief    HDR HW States Setup
//! \details  Setup HW states for HDR
//! \param    PVPHAL_HDR_STATE pHdrState
//!           [in] Pointer to the HDR State
//! \param    PVPHAL_HDR_RENDER_DATA pRenderData
//!           [in,out] Pointer to HDR render data
//! \param    PVPHAL_HDR_RENDER_DATA pRenderData
//!           [in,out] Pointer to HDR render data
//! \param    uint32_t HDRKernelID
//!           [in] HDR Kernel ID
//! \return   MOS_STATUS
//!           Return MOS_STATUS_SUCCESS if successful, otherwise failed
//!
MOS_STATUS VpHal_HdrSetupHwStates(
    PVPHAL_HDR_STATE            pHdrState,
    PVPHAL_HDR_RENDER_DATA      pRenderData,
    uint32_t                    HDRKernelID)
{
    PRENDERHAL_INTERFACE        pRenderHal           = nullptr;
    int32_t                     iKrnAllocation       = 0;
    int32_t                     iCurbeOffset         = 0;
    MOS_STATUS                  eStatus              = MOS_STATUS_SUCCESS;
    MHW_KERNEL_PARAM            MhwKernelParam       = {};
    PMOS_INTERFACE              pOsInterface         = nullptr;

    VPHAL_RENDER_CHK_NULL(pHdrState);
    VPHAL_RENDER_CHK_NULL(pRenderData);
    VPHAL_RENDER_CHK_NULL(pHdrState->pOsInterface);
    
    pRenderHal   = pHdrState->pRenderHal;
    pOsInterface = pHdrState->pOsInterface;
    VPHAL_RENDER_CHK_NULL(pRenderHal);

    //----------------------------------
    // Allocate and reset media state
    //----------------------------------
    pRenderData->pMediaState = pRenderHal->pfnAssignMediaState(pRenderHal, (RENDERHAL_COMPONENT)RENDERHAL_COMPONENT_HDR);
    MOS_OS_CHK_NULL(pRenderData->pMediaState);

    //----------------------------------
    // Allocate and reset SSH instance
    //----------------------------------
    VPHAL_RENDER_CHK_STATUS(pRenderHal->pfnAssignSshInstance(pRenderHal));

    //----------------------------------
    // Assign and Reset Binding Table
    //----------------------------------
    VPHAL_RENDER_CHK_STATUS(pRenderHal->pfnAssignBindingTable(
        pRenderHal, 
        &pRenderData->iBindingTable));

    //----------------------------------
    // Setup Surface states
    //----------------------------------
    VPHAL_RENDER_CHK_STATUS(pHdrState->pfnSetupSurfaceStates(
        pHdrState,
        pRenderData));

    //----------------------------------
    // Load Static data
    //----------------------------------
    VPHAL_RENDER_CHK_STATUS(pHdrState->pfnLoadStaticData(
        pHdrState,
        pRenderData,
        &iCurbeOffset));

    //----------------------------------
    // Setup VFE State params. Each Renderer MUST call pfnSetVfeStateParams().
    // See comment in VpHal_HwSetVfeStateParams() for details.
    //----------------------------------
    pRenderHal->pfnSetVfeStateParams(
        pRenderHal,
        MEDIASTATE_DEBUG_COUNTER_FREE_RUNNING,
        pRenderData->pKernelParam[HDRKernelID]->Thread_Count,
        pRenderData->iCurbeLength,
        0,
        nullptr);

    //----------------------------------
    // Load kernel to GSH
    //----------------------------------
    INIT_MHW_KERNEL_PARAM(MhwKernelParam, &pRenderData->KernelEntry[HDRKernelID]);    

    iKrnAllocation = pRenderHal->pfnLoadKernel(
        pRenderHal,
        pRenderData->pKernelParam[HDRKernelID],
        &MhwKernelParam,
        nullptr);

    if (iKrnAllocation < 0)
    {
        VPHAL_RENDER_ASSERTMESSAGE("HDR Load kernel to GSH failed");
        eStatus = MOS_STATUS_UNKNOWN;
        goto finish;
    }

    //----------------------------------
    // Allocate Media ID, link to kernel
    //----------------------------------
    pRenderData->iMediaID = pRenderHal->pfnAllocateMediaID(
        pRenderHal,
        iKrnAllocation,
        pRenderData->iBindingTable,
        iCurbeOffset,
        pRenderData->iCurbeLength,
        0,
        nullptr);

    if (pRenderData->iMediaID < 0) 
    {
        VPHAL_RENDER_ASSERTMESSAGE("HDR Allocate Media ID failed");
        eStatus = MOS_STATUS_UNKNOWN;
        goto finish;
    }

    //----------------------------------
    // Setup Sampler states
    //----------------------------------
    if (HDRKernelID != KERNEL_HDR_PREPROCESS)
    {
        VPHAL_RENDER_CHK_STATUS(pHdrState->pfnSetSamplerStates(
            pHdrState,
            pRenderData));
    }

finish:
    VPHAL_RENDER_ASSERT(eStatus == MOS_STATUS_SUCCESS);
    return eStatus;
}

//!
//! \brief    Setup media walker command for HDR
//! \details  Setup media walker command for HDR
//! \param    PVPHAL_HDR_STATE pHdrState
//!           [in] Pointer to HDR state
//! \param    PVPHAL_HDR_RENDER_DATA pRenderData
//!           [in] Pointer to render data
//! \param    PMHW_WALKER_PARAMS pWalkerParams
//!           [out] Pointer to media walker parameters
//! \param    int32_t iKDTIndex
//            [in] KDT index.
//! \param    uint32_t uiPortionIndex
//            [in] Frame split portion index.
//! \return   MOS_STATUS
//!
MOS_STATUS VpHal_PreprocessHdrSetupWalkerObject(
    PVPHAL_HDR_STATE            pHdrState,
    PVPHAL_HDR_RENDER_DATA      pRenderData,
    PMHW_WALKER_PARAMS          pWalkerParams,
    int32_t                     iKDTIndex,
    uint32_t                    uiPortionIndex)
{
    MOS_STATUS  eStatus       = MOS_STATUS_SUCCESS;
    uint32_t threadswidth     = 1;
    uint32_t threadsheight    = VPHAL_MAX_HDR_INPUT_LAYER;

    VPHAL_RENDER_CHK_NULL(pHdrState);
    VPHAL_RENDER_CHK_NULL(pRenderData);
    VPHAL_RENDER_CHK_NULL(pWalkerParams);

    // Setup Media Walker cmd. Raster scan with no dependency
    MOS_ZeroMemory(pWalkerParams, sizeof(MHW_WALKER_PARAMS));
    pWalkerParams->InterfaceDescriptorOffset = pRenderData->iMediaID;
    pWalkerParams->dwGlobalLoopExecCount = 1;
    pWalkerParams->dwLocalLoopExecCount  = 1;
    pWalkerParams->BlockResolution.x  = threadswidth;
    pWalkerParams->BlockResolution.y  = threadsheight;
    pWalkerParams->GlobalResolution.x = threadswidth;
    pWalkerParams->GlobalResolution.y = threadsheight;


    pWalkerParams->GlobalStart.x = 0;
    pWalkerParams->GlobalStart.y = 0;
    pWalkerParams->GlobalOutlerLoopStride.x = threadswidth;
    pWalkerParams->GlobalOutlerLoopStride.y = 0;
    pWalkerParams->GlobalInnerLoopUnit.x = 0;
    pWalkerParams->GlobalInnerLoopUnit.y = threadsheight;
    pWalkerParams->LocalStart.x = 0;
    pWalkerParams->LocalStart.y = 0;
    pWalkerParams->LocalOutLoopStride.x = 1;
    pWalkerParams->LocalOutLoopStride.y = 0;
    pWalkerParams->LocalInnerLoopUnit.x = 0;
    pWalkerParams->LocalInnerLoopUnit.y = 1;
    pWalkerParams->LocalEnd.x = 0;
    pWalkerParams->LocalEnd.y = threadsheight - 1;

    eStatus = MOS_STATUS_SUCCESS;

finish:
    return eStatus;
}

//!
//! \brief    Render GpGpu Walker Buffer
//! \details  Render GpGpu Walker Buffer, fill Walker static data fields and set walker
//!           cmd params
//! \param    [in] pHdrState
//!           Pointer to HdrState
//! \param    [in] pRenderingData
//!           Pointer to Rendering Data
//! \param    [in] pWalkerParams
//!           Pointer to Walker parameters
//! \return   MOS_STATUS
//!           Return MOS_STATUS_SUCCESS if successful, otherwise false
//!
MOS_STATUS Vphal_HdrSetupComputeWalker(
    PVPHAL_HDR_STATE                pHdrState,
    PVPHAL_HDR_RENDER_DATA          pRenderData,
    PMHW_GPGPU_WALKER_PARAMS        pWalkerParams)
{
    MOS_STATUS                          eStatus                 = MOS_STATUS_UNINITIALIZED;
    PRENDERHAL_INTERFACE                pRenderHal              = nullptr;
    PVPHAL_BB_COMP_ARGS                 pBbArgs                 = nullptr;
    bool                                bResult                 = false;
    int32_t                             iLayers                 = 0;
    uint32_t                            uiMediaWalkerBlockSize  = 0;
    uint32_t*                           pdwDestXYTopLeft        = nullptr;
    uint32_t*                           pdwDestXYBottomRight    = nullptr;
    RECT                                AlignedRect             = {};
    bool                                bVerticalPattern        = false;

    VPHAL_RENDER_CHK_NULL(pHdrState);
    VPHAL_RENDER_CHK_NULL(pHdrState->pTargetSurf[0]);
    VPHAL_RENDER_CHK_NULL(pHdrState->pSrcSurf[0]);
    VPHAL_RENDER_CHK_NULL(pRenderData);
    VPHAL_RENDER_CHK_NULL(pWalkerParams);

    pRenderHal              = pHdrState->pRenderHal;
    VPHAL_RENDER_CHK_NULL(pRenderHal);

    bVerticalPattern        = false;
    AlignedRect             = pHdrState->pTargetSurf[0]->rcDst;

    // Get media walker kernel block size
    uiMediaWalkerBlockSize  = pRenderHal->pHwSizes->dwSizeMediaWalkerBlock;

    // Calculate aligned output area in order to determine the total # blocks
    // to process in case of non-16x16 aligned target.
    AlignedRect.right       += uiMediaWalkerBlockSize - 1;
    AlignedRect.bottom      += uiMediaWalkerBlockSize - 1;
    AlignedRect.left        -= AlignedRect.left   % uiMediaWalkerBlockSize;
    AlignedRect.top         -= AlignedRect.top    % uiMediaWalkerBlockSize;
    AlignedRect.right       -= AlignedRect.right  % uiMediaWalkerBlockSize;
    AlignedRect.bottom      -= AlignedRect.bottom % uiMediaWalkerBlockSize;

    // Set walker cmd params - Rasterscan
    pWalkerParams->InterfaceDescriptorOffset = pRenderData->iMediaID;

    if (pHdrState->uSourceCount == 1 &&
        pHdrState->pSrcSurf[0]->TileType == MOS_TILE_LINEAR &&
        (pHdrState->pSrcSurf[0]->Rotation == VPHAL_ROTATION_90 || pHdrState->pSrcSurf[0]->Rotation == VPHAL_ROTATION_270))
    {
        pWalkerParams->GroupStartingX       = (AlignedRect.top / uiMediaWalkerBlockSize);
        pWalkerParams->GroupStartingY       = (AlignedRect.left / uiMediaWalkerBlockSize);
        pWalkerParams->GroupWidth           = pRenderData->iBlocksY;
        pWalkerParams->GroupHeight          = pRenderData->iBlocksX;
    }
    else
    {
        pWalkerParams->GroupStartingX       = (AlignedRect.left / uiMediaWalkerBlockSize);
        pWalkerParams->GroupStartingY       = (AlignedRect.top / uiMediaWalkerBlockSize);
        pWalkerParams->GroupWidth           = pRenderData->iBlocksX;
        pWalkerParams->GroupHeight          = pRenderData->iBlocksY;
    }

    pWalkerParams->ThreadWidth              = 1;
    pWalkerParams->ThreadHeight             = 1;
    pWalkerParams->ThreadDepth              = 1;
    pWalkerParams->IndirectDataStartAddress = pRenderData->iCurbeOffset;
    // Indirect Data Length is a multiple of 64 bytes (size of L3 cacheline). Bits [5:0] are zero.
    pWalkerParams->IndirectDataLength       = MOS_ALIGN_CEIL(pRenderData->iCurbeLength, 1 << MHW_COMPUTE_INDIRECT_SHIFT);
    pWalkerParams->BindingTableID           = pRenderData->iBindingTable;

    eStatus = MOS_STATUS_SUCCESS;

finish:
    return eStatus;
}

//!
//! \brief    HDR render
//! \details  Launch HDR kernel to render output picture
//! \param    PVPHAL_HDR_STATE pHdrState
//!           [in] Poniter to HDR state
//! \param    PVPHAL_RENDER_PARAMS pRenderParams
//!           [in,out] Pointer to Render parameters
//! \return   MOS_STATUS
//!           MOS_STATUS_SUCCESS if successful, otherwise failed
//!
MOS_STATUS VpHal_HdrRender(
    PVPHAL_HDR_STATE             pHdrState,
    PVPHAL_RENDER_PARAMS         pRenderParams)
{
    PRENDERHAL_INTERFACE            pRenderHal              = nullptr;
    PMOS_INTERFACE                  pOsInterface            = nullptr;
    MHW_WALKER_PARAMS               WalkerParams            = {};
    PMHW_WALKER_PARAMS              pWalkerParams           = nullptr;
    MHW_GPGPU_WALKER_PARAMS         ComputeWalkerParams     = {};
    PMHW_GPGPU_WALKER_PARAMS        pComputeWalkerParams    = nullptr;
    int32_t                         iKUID                   = 0;
    int32_t                         iKDTIndex               = 0;
    uint32_t                        i                       = 0;
    bool                            bSupported              = true;
    MOS_STATUS                      eStatus                 = MOS_STATUS_UNKNOWN;
    uint32_t                        HdrKernel               = 0;
    VPHAL_HDR_RENDER_DATA           RenderData              = {};
    MOS_GPU_CONTEXT                 RenderGpuContext        = MOS_GPU_CONTEXT_RENDER;
    bool                            bLastSummit             = true;

    VPHAL_RENDER_FUNCTION_ENTER;

    VPHAL_RENDER_CHK_NULL(pHdrState);
    VPHAL_RENDER_CHK_NULL(pRenderParams);
    VPHAL_RENDER_CHK_NULL(pHdrState->pRenderHal);
    VPHAL_RENDER_CHK_NULL(pHdrState->pOsInterface);

    // Initialize Variables
    pRenderHal                = pHdrState->pRenderHal;
    pOsInterface              = pHdrState->pOsInterface;
    pWalkerParams             = &WalkerParams;
    pHdrState->uSourceCount   = 0;
    pHdrState->uTargetCount   = 0;
    pRenderParams             = (VPHAL_RENDER_PARAMS*)pRenderParams;

    RenderGpuContext          = pOsInterface ? (pOsInterface->CurrentGpuContextOrdinal) : MOS_GPU_CONTEXT_RENDER;
    pComputeWalkerParams      = nullptr;
    MOS_ZeroMemory(&ComputeWalkerParams, sizeof(ComputeWalkerParams));

    if (pRenderParams->uSrcCount > VPHAL_MAX_HDR_INPUT_LAYER)
    {
        eStatus = MOS_STATUS_INVALID_PARAMETER;
        goto finish;
    }
    if (pRenderParams->uDstCount > VPHAL_MAX_HDR_OUTPUT_LAYER)
    {
        eStatus = MOS_STATUS_INVALID_PARAMETER;
        goto finish;
    }

    HdrKernel = KERNEL_HDR_MANDATORY;
    
    for (i = 0; i < pRenderParams->uSrcCount; i++)
    {
        if (pRenderParams->pSrc[i] == nullptr)
        {
            continue;
        }

        VPHAL_RENDER_CHK_STATUS(pHdrState->pfnIsInputFormatSupported(pRenderParams->pSrc[i], &bSupported));

        if (!bSupported)
        {
            eStatus = MOS_STATUS_INVALID_PARAMETER;
            goto finish;
        }

        pHdrState->pSrcSurf[i] = pRenderParams->pSrc[i];
        pHdrState->uSourceCount++;

        // Ensure the input is ready to be read
        pOsInterface->pfnSyncOnResource(
            pOsInterface, 
            &pHdrState->pSrcSurf[i]->OsResource,
            RenderGpuContext,
            false);
    }

    for (i = 0; i < pRenderParams->uDstCount; i++)
    {
        if (pRenderParams->pTarget[i] == nullptr)
        {
            continue;
        }

        VPHAL_RENDER_CHK_STATUS(pHdrState->pfnIsOutputFormatSupported(pRenderParams->pTarget[i], &bSupported));

        if (!bSupported)
        {
            eStatus = MOS_STATUS_INVALID_PARAMETER;
            goto finish;
        }


        pHdrState->pTargetSurf[i] = pRenderParams->pTarget[i];
        pHdrState->uTargetCount++;

        // Ensure the output is ready to be written
        pOsInterface->pfnSyncOnResource(
            pOsInterface, 
            &pHdrState->pTargetSurf[i]->OsResource,
            RenderGpuContext,
            true);

        // Sync Render Target with Overlay Context
        if (pHdrState->pTargetSurf[i]->bOverlay)
        {
            pOsInterface->pfnSyncOnOverlayResource(
                pOsInterface,
                &pHdrState->pTargetSurf[i]->OsResource,
                RenderGpuContext);
        }
    }

    pHdrState->pColorFillParams = pRenderParams->pColorFillParams;
    // Allocate resources needed by Hdr
    VPHAL_RENDER_CHK_STATUS(pHdrState->pfnAllocateResources(pHdrState));
    VPHAL_RENDER_CHK_STATUS(VpHal_HdrPreprocess(pHdrState, pRenderParams));    

    for (i = 0; i < pHdrState->uiSplitFramePortions; i++)
    {
        // Reset states before rendering
        // (clear allocations, get GSH allocation index + any additional housekeeping)
        pOsInterface->pfnResetOsStates(pOsInterface);
        VPHAL_RENDER_CHK_STATUS(pRenderHal->pfnReset(pRenderHal));

        // Get the Kernel Parameter (Platform Specific)
        VPHAL_RENDER_CHK_STATUS(pHdrState->pfnGetKernelParam(
            HdrKernel,
            &iKUID,
            &iKDTIndex));

        // Setup Hdr render data
        VPHAL_RENDER_CHK_STATUS(VpHal_HdrSetupRenderData(
            pHdrState,
            &RenderData,
            iKUID,
            iKDTIndex));

        // Set up HW States and Commands
        VPHAL_RENDER_CHK_STATUS(VpHal_HdrSetupHwStates(pHdrState, &RenderData, iKDTIndex));

        // Set Perf Tag
        pOsInterface->pfnResetPerfBufferID(pOsInterface);
        pOsInterface->pfnSetPerfTag(pOsInterface, RenderData.PerfTag);

        if (pHdrState->bFtrComputeWalker)
        {
            // Setup Compute Walker
            pWalkerParams = nullptr;
            pComputeWalkerParams = &ComputeWalkerParams;

            VPHAL_RENDER_CHK_STATUS(Vphal_HdrSetupComputeWalker(
                pHdrState,
                &RenderData,
                &ComputeWalkerParams));
        }
        else
        {
            // Setup Media Walker Object
            VpHal_HdrSetupWalkerObject(
                pHdrState,
                &RenderData,
                &WalkerParams,
                iKDTIndex,
                i);
        }

        bLastSummit = (i == (pHdrState->uiSplitFramePortions - 1) ? true : false);
        // Submit all media states to HW
        VPHAL_RENDER_CHK_STATUS(VpHal_RndrSubmitCommands(
            pRenderHal,
            nullptr,
            pHdrState->bNullHwRenderHdr,
            pWalkerParams,
            pComputeWalkerParams,
            &pHdrState->StatusTableUpdateParams,
            (VpKernelID)kernelHdrMandatory,
            0,
            nullptr,
            bLastSummit));
    }

    eStatus = MOS_STATUS_SUCCESS;

finish:
    VPHAL_RENDER_EXITMESSAGE("eStatus %d", eStatus);
    return eStatus;
}

//!
//! \brief    Hdr init renderer interface
//! \details  Initializes the Hdr interface
//! \param    PVPHAL_HDR_STATE pHdrState
//!           [in] Pointer to Hdr state
//! \param    PRENDERHAL_INTERFACE pRenderHal
//!           [in] Pointer to RENDERHAL interface
//! \return   void
//!
MOS_STATUS VpHal_HdrInitInterface(
    PVPHAL_HDR_STATE          pHdrState,
    PRENDERHAL_INTERFACE      pRenderHal)
{
    MOS_STATUS                  eStatus         = MOS_STATUS_SUCCESS;
    PMOS_INTERFACE              pOsInterface    = nullptr;

    VPHAL_PUBLIC_CHK_NULL(pHdrState);
    VPHAL_PUBLIC_CHK_NULL(pRenderHal);

    MOS_ZeroMemory(pHdrState, sizeof(VPHAL_HDR_STATE));

    pOsInterface = pRenderHal->pOsInterface;

    VPHAL_PUBLIC_CHK_NULL(pOsInterface);

    // Set interface to OS and HW interfaces
    pHdrState->pRenderHal                 = pRenderHal;
    pHdrState->pOsInterface               = pOsInterface;
    pHdrState->pSkuTable                  = pOsInterface->pfnGetSkuTable(pOsInterface);

    // Setup Function Pointers
    pHdrState->pfnInitialize              = VpHal_HdrInitialize;
    pHdrState->pfnDestroy                 = VpHal_HdrDestroy;
    pHdrState->pfnRender                  = VpHal_HdrRender;
    pHdrState->pfnIsNeeded                = VpHal_HdrIsNeeded;

    eStatus = MOS_STATUS_SUCCESS;

finish:
    return eStatus;
}

//! \brief    Perform Rendering HDR step
//! \details  Check whether HDR is needed. When it's needed, perform HDR
//!           operation
//! \param    [in,out] pRenderer
//!           VPHAL renderer pointer
//! \param    [in,out] pRenderParams
//!           Pointer to VPHAL render parameter
//! \param    [in,out] pRenderPassData
//!           Pointer to the VPHAL render pass data
//! \return   MOS_STATUS
//!           Return MOS_STATUS_SUCCESS if successful, otherwise failed
//!
MOS_STATUS VpHal_RndrRenderHDR(
    VphalRenderer           *pRenderer,
    PVPHAL_RENDER_PARAMS    pRenderParams,
    RenderpassData          *pRenderPassData)
{
    PRENDERHAL_INTERFACE    *pRenderHal = nullptr;
    MOS_STATUS              eStatus     = MOS_STATUS_SUCCESS;
    bool                    bEnabled    = false;

    VPHAL_RENDER_FUNCTION_ENTER;

    VPHAL_RENDER_CHK_NULL(pRenderer);
    VPHAL_RENDER_CHK_NULL(pRenderParams);
    VPHAL_RENDER_CHK_NULL(pRenderPassData);
    VPHAL_RENDER_CHK_NULL(pRenderer->pHdrState);

    pRenderHal = &pRenderer->pHdrState->pRenderHal;
    VPHAL_RENDER_CHK_NULL(pRenderHal);

    // Disable bEnableP010SinglePass for HDR path, to avoid AVS sampler and 1 planes 3D sampler path in kernel.
    // Kernel solution only support 2 planes rendering of 3D sampler.
    if ((*pRenderHal)->bEnableP010SinglePass)
    {
        bEnabled = true;
        (*pRenderHal)->bEnableP010SinglePass = false;
    }
    eStatus = pRenderer->pHdrState->pfnRender(pRenderer->pHdrState, pRenderParams);

    if (bEnabled)
       (*pRenderHal)->bEnableP010SinglePass = true;

finish:
    VPHAL_RENDER_EXITMESSAGE("eStatus %d", eStatus);
    return eStatus;
}

//!
//! \brief    Check if HDR path is needed
//! \details  Check if HDR path is needed
//! \param    [in] pRenderer
//!           VPHAL renderer pointer
//! \param    [in] pRenderParams
//!           Pointer to VPHAL render parameter
//! \param    [in] pRenderPassData
//!           Pointer to VPHAL render pass data
//! \return   bool
//!
bool VpHal_RndrIsHdrPathNeeded(
    VphalRenderer           *pRenderer,
    PVPHAL_RENDER_PARAMS    pRenderParams,
    RenderpassData          *pRenderPassData)
{
    if (!pRenderer || !pRenderParams || !pRenderPassData)
    {
        return false;
    }

    if (pRenderPassData->bHdrNeeded && pRenderer->pHdrState)
    {
        // Hdr kernel render will be disabled for 1 layer H2H bypass case for PnP optimization
        if (!pRenderer->pHdrState->bBypassHdrKernelPath)
        {
            return true;
        }
    }

    return false;
}

//!
//! \brief    Setup media walker command for HDR
//! \details  Setup media walker command for HDR
//! \param    PVPHAL_HDR_STATE pHdrState
//!           [in] Pointer to HDR state
//! \param    PVPHAL_HDR_RENDER_DATA pRenderData
//!           [in] Pointer to render data
//! \param    PMHW_WALKER_PARAMS pWalkerParams
//!           [out] Pointer to media walker parameters
//! \param    int32_t iKDTIndex
//            [in] KDT index.
//! \param    uint32_t uiPortionIndex
//            [in] Frame split portion index.
//! \return   MOS_STATUS
//!
MOS_STATUS VpHal_HdrSetupWalkerObject(
    PVPHAL_HDR_STATE            pHdrState,
    PVPHAL_HDR_RENDER_DATA      pRenderData,
    PMHW_WALKER_PARAMS          pWalkerParams,
    int32_t                     iKDTIndex,
    uint32_t                    uiPortionIndex)
{
    MOS_STATUS                  eStatus        =  MOS_STATUS_SUCCESS;
    RECT                        AlignedRect    = {};
    int32_t                     iBlockWd       = 0;                                       // Block Width
    int32_t                     iBlockHt       = 0;                                       // Block Height

    VPHAL_RENDER_CHK_NULL(pHdrState);
    VPHAL_RENDER_CHK_NULL(pHdrState->pTargetSurf[0]);
    VPHAL_RENDER_CHK_NULL(pRenderData);
    VPHAL_RENDER_CHK_NULL(pWalkerParams);

    AlignedRect = pHdrState->pTargetSurf[0]->rcDst;
    iBlockWd = pRenderData->pKernelParam[iKDTIndex]->block_width;
    iBlockHt = pRenderData->pKernelParam[iKDTIndex]->block_height;

    AlignedRect.right += iBlockWd - 1;
    AlignedRect.bottom += iBlockHt - 1;
    AlignedRect.left -= AlignedRect.left   % iBlockWd;
    AlignedRect.top -= AlignedRect.top    % iBlockHt;
    AlignedRect.right -= AlignedRect.right  % iBlockWd;
    AlignedRect.bottom -= AlignedRect.bottom % iBlockHt;

    // Setup Media Walker cmd. Raster scan with no dependency
    MOS_ZeroMemory(pWalkerParams, sizeof(MHW_WALKER_PARAMS));
    pWalkerParams->InterfaceDescriptorOffset = pRenderData->iMediaID;
    pWalkerParams->dwGlobalLoopExecCount = 1;
    pWalkerParams->dwLocalLoopExecCount = pRenderData->iBlocksX - 1;
    pWalkerParams->BlockResolution.x = pRenderData->iBlocksX;
    pWalkerParams->BlockResolution.y = pRenderData->iBlocksY;

    if (AlignedRect.left != 0 || AlignedRect.top != 0)
    {
        // if the rect starts from any other macro block other than the first
        // then the global resolution should be the whole frame and the global 
        // start should be the rect start.
        pWalkerParams->GlobalResolution.x =
            (AlignedRect.right / iBlockWd);
        pWalkerParams->GlobalResolution.y =
            (AlignedRect.bottom / iBlockHt);
    }
    else
    {
        pWalkerParams->GlobalResolution.x = pRenderData->iBlocksX;
        pWalkerParams->GlobalResolution.y = pRenderData->iBlocksY;
    }

    pWalkerParams->GlobalStart.x = (AlignedRect.left / iBlockWd);
    pWalkerParams->GlobalStart.y = (AlignedRect.top / iBlockHt);
    pWalkerParams->GlobalOutlerLoopStride.x = pRenderData->iBlocksX;
    pWalkerParams->GlobalOutlerLoopStride.y = 0;
    pWalkerParams->GlobalInnerLoopUnit.x = 0;
    pWalkerParams->GlobalInnerLoopUnit.y = pRenderData->iBlocksY;
    pWalkerParams->LocalStart.x = 0;
    pWalkerParams->LocalStart.y = 0;
    pWalkerParams->LocalOutLoopStride.x = 1;
    pWalkerParams->LocalOutLoopStride.y = 0;
    pWalkerParams->LocalInnerLoopUnit.x = 0;
    pWalkerParams->LocalInnerLoopUnit.y = 1;
    pWalkerParams->LocalEnd.x = 0;
    pWalkerParams->LocalEnd.y = pRenderData->iBlocksY - 1;

    if (pHdrState->uiSplitFramePortions > 1)
    {
        pWalkerParams->GlobalStart.x = MOS_MAX(MOS_ROUNDUP_DIVIDE(pWalkerParams->GlobalResolution.x, pHdrState->uiSplitFramePortions) * (uiPortionIndex),
            pWalkerParams->GlobalStart.x);
        pWalkerParams->GlobalResolution.x = MOS_MIN(MOS_ROUNDUP_DIVIDE(pWalkerParams->GlobalResolution.x, pHdrState->uiSplitFramePortions) * (uiPortionIndex + 1),
            pWalkerParams->GlobalResolution.x);
    }

    eStatus = MOS_STATUS_SUCCESS;

finish:
    return eStatus;
}

//!
//! \brief    HDR preprocess
//! \details  Launch HDR pre process kernel to render hdr coefficients surface
//! \param    PVPHAL_HDR_STATE pHdrState
//!           [in] Poniter to HDR state
//! \param    PVPHAL_RENDER_PARAMS pRenderParams
//!           [in,out] Pointer to Render parameters
//! \return   MOS_STATUS
//!           MOS_STATUS_SUCCESS if successful, otherwise failed
//!
MOS_STATUS VpHal_HdrPreprocess(
    PVPHAL_HDR_STATE        pHdrState,
    PVPHAL_RENDER_PARAMS    pRenderParams)
{
    MOS_STATUS                    eStatus               = MOS_STATUS_UNKNOWN;
    PRENDERHAL_INTERFACE          pRenderHal            = nullptr;
    PMOS_INTERFACE                pOsInterface          = nullptr;
    uint32_t                      HdrKernel             = KERNEL_HDR_PREPROCESS;
    MOS_GPU_CONTEXT               RenderGpuContext      = MOS_GPU_CONTEXT_RENDER;
    int32_t                       iKUID                 = 0;
    int32_t                       iKDTIndex             = 0;
    VPHAL_HDR_RENDER_DATA         RenderData            = {};
    bool                          bLastSummit           = true;
    MHW_WALKER_PARAMS             WalkerParams          = {};
    PMHW_WALKER_PARAMS            pWalkerParams         = nullptr;
    MHW_GPGPU_WALKER_PARAMS       ComputeWalkerParams   = {};
    PMHW_GPGPU_WALKER_PARAMS      pComputeWalkerParams  = nullptr;
    uint32_t                      i                     = 0;
    int32_t                       iCurbeOffset          = 0;
    const uint32_t                HDRKernelID           = KERNEL_HDR_PREPROCESS;
    MHW_KERNEL_PARAM              MhwKernelParam        = {};
    int32_t                       iKrnAllocation        = 0;

    VPHAL_RENDER_FUNCTION_ENTER;

    VPHAL_RENDER_CHK_NULL(pHdrState);
    VPHAL_RENDER_CHK_NULL(pRenderParams);
    VPHAL_RENDER_CHK_NULL(pHdrState->pRenderHal);
    VPHAL_RENDER_CHK_NULL(pHdrState->pOsInterface);

    // HDR PreProcess Kernel is needed only if HDR metada is changed.
    if (!pHdrState->dwUpdateMask)
    {
        VPHAL_RENDER_EXITMESSAGE("pHdrState->dwUpdateMask is false, no need to update coefficients, exit with MOS_STATUS_SUCCESS!");
        return MOS_STATUS_SUCCESS;
    }

    // Initialize Variables
    pRenderHal          = pHdrState->pRenderHal;
    pOsInterface        = pHdrState->pOsInterface;
    RenderGpuContext    = pOsInterface->CurrentGpuContextOrdinal;

    // Reset states before rendering
    // (clear allocations, get GSH allocation index + any additional housekeeping)
    pOsInterface->pfnResetOsStates(pOsInterface);
    VPHAL_RENDER_CHK_STATUS(pRenderHal->pfnReset(pRenderHal));

    // Get the Kernel Parameter (Platform Specific)
    VPHAL_RENDER_CHK_STATUS(pHdrState->pfnGetKernelParam(
        HdrKernel,
        &iKUID,
        &iKDTIndex));

    // Setup Hdr render data
    VPHAL_RENDER_CHK_STATUS(VpHal_HdrSetupRenderData(
        pHdrState,
        &RenderData,
        iKUID,
        iKDTIndex));

    //----------------------------------
    // Allocate and reset media state
    //----------------------------------
    RenderData.pMediaState = pRenderHal->pfnAssignMediaState(pRenderHal, (RENDERHAL_COMPONENT)RENDERHAL_COMPONENT_HDR);
    MOS_OS_CHK_NULL(RenderData.pMediaState);

    //----------------------------------
    // Allocate and reset SSH instance
    //----------------------------------
    VPHAL_RENDER_CHK_STATUS(pRenderHal->pfnAssignSshInstance(pRenderHal));

    //----------------------------------
    // Assign and Reset Binding Table
    //----------------------------------
    VPHAL_RENDER_CHK_STATUS(pRenderHal->pfnAssignBindingTable(
        pRenderHal,
        &RenderData.iBindingTable));

    //----------------------------------
   // Setup Surface states
   //----------------------------------
    VPHAL_RENDER_CHK_STATUS(pHdrState->pfnSetupPreSurfaceStates(
        pHdrState,
        &RenderData));

    //----------------------------------
    // Load Static data
    //----------------------------------
    VPHAL_RENDER_CHK_STATUS(pHdrState->pfnLoadPreStaticData(
        pHdrState,
        &RenderData,
        &iCurbeOffset));

    //----------------------------------
    // Setup VFE State params. Each Renderer MUST call pfnSetVfeStateParams().
    // See comment in VpHal_HwSetVfeStateParams() for details.
    //----------------------------------
    pRenderHal->pfnSetVfeStateParams(
        pRenderHal,
        MEDIASTATE_DEBUG_COUNTER_FREE_RUNNING,
        RenderData.pKernelParam[HDRKernelID]->Thread_Count,
        RenderData.iCurbeLength,
        0,
        nullptr);

    //----------------------------------
    // Load kernel to GSH
    //----------------------------------
    INIT_MHW_KERNEL_PARAM(MhwKernelParam, &RenderData.KernelEntry[HDRKernelID]);

    iKrnAllocation = pRenderHal->pfnLoadKernel(
        pRenderHal,
        RenderData.pKernelParam[HDRKernelID],
        &MhwKernelParam,
        nullptr);

    if (iKrnAllocation < 0)
    {
        VPHAL_RENDER_ASSERTMESSAGE("HDR Load kernel to GSH failed");
        eStatus = MOS_STATUS_UNKNOWN;
        goto finish;
    }

    //----------------------------------
    // Allocate Media ID, link to kernel
    //----------------------------------
    RenderData.iMediaID = pRenderHal->pfnAllocateMediaID(
        pRenderHal,
        iKrnAllocation,
        RenderData.iBindingTable,
        iCurbeOffset,
        RenderData.iCurbeLength,
        0,
        nullptr);

    if (RenderData.iMediaID < 0)
    {
        VPHAL_RENDER_ASSERTMESSAGE("HDR Allocate Media ID failed");
        eStatus = MOS_STATUS_UNKNOWN;
        goto finish;
    }

    // Set Perf Tag
    pOsInterface->pfnResetPerfBufferID(pOsInterface);
    pOsInterface->pfnSetPerfTag(pOsInterface, RenderData.PerfTag);

    if (pHdrState->bFtrComputeWalker)
    {
        // Setup Compute Walker
        pWalkerParams = nullptr;
        pComputeWalkerParams = &ComputeWalkerParams;

        VPHAL_RENDER_CHK_STATUS(Vphal_HdrSetupComputeWalker(
            pHdrState,
            &RenderData,
            &ComputeWalkerParams));
    }
    else
    {
        // Setup Media Walker Object
        VpHal_PreprocessHdrSetupWalkerObject(
            pHdrState,
            &RenderData,
            &WalkerParams,
            iKDTIndex,
            0);
    }

    // Submit all media states to HW
    VPHAL_RENDER_CHK_STATUS(VpHal_RndrSubmitCommands(
        pRenderHal,
        nullptr,
        pHdrState->bNullHwRenderHdr,
        &WalkerParams,
        &ComputeWalkerParams,
        &pHdrState->StatusTableUpdateParams,
        (VpKernelID)kernelHdrPreprocess,
        0,
        nullptr,
        bLastSummit));

#if (_DEBUG || _RELEASE_INTERNAL)
    if (sEnableKernelDump)
    {
        VphalSurfaceDumper surfaceDumper(pOsInterface);
        std::string fileName("Preprocessed_HDRMandatory_coefficient_8x98");
        surfaceDumper.DumpSurfaceToFile(pOsInterface, &pHdrState->CoeffSurface, fileName.c_str(), 0, true, false, nullptr);
    }
#endif

    eStatus = MOS_STATUS_SUCCESS;

finish:
    VPHAL_RENDER_EXITMESSAGE("eStatus %d", eStatus);
    return eStatus;
}


//!
//! \brief    Calculate Yuv Range and Offest
//! \details  Calculate Yuv Range and Offest
//! \param    VPHAL_CSPACE cspace
//!           [in] Source color space
//! \param    float* pLumaOffset
//!           [out] Pointer to Luma Offset
//! \param    float* pLumaExcursion 
//!           [out] Pointer to Luma Excursion
//! \param    float* pChromaZero
//!           [out] Pointer to Chroma Offset
//! \param    float* pChromaExcursion
//!           [out] Pointer to Chroma Excursion
//! \return   MOS_STATUS
//!
MOS_STATUS VpHal_HdrGetYuvRangeAndOffset(
    VPHAL_CSPACE cspace,
    float*       pLumaOffset,
    float*       pLumaExcursion,
    float*       pChromaZero,
    float*       pChromaExcursion)
{
    MOS_STATUS eStatus = MOS_STATUS_SUCCESS;

    VPHAL_PUBLIC_CHK_NULL(pLumaOffset);
    VPHAL_PUBLIC_CHK_NULL(pLumaExcursion);
    VPHAL_PUBLIC_CHK_NULL(pChromaZero);
    VPHAL_PUBLIC_CHK_NULL(pChromaExcursion);

    switch (cspace)
    {
    case CSpace_BT601_FullRange:
    case CSpace_BT709_FullRange:
    case CSpace_BT601Gray_FullRange:
    case CSpace_BT2020_FullRange:
        *pLumaOffset = 0.0f;
        *pLumaExcursion = 255.0f;
        *pChromaZero = 128.0f;
        *pChromaExcursion = 255.0f;
        break;

    case CSpace_BT601:
    case CSpace_BT709:
    case CSpace_xvYCC601: // since matrix is the same as 601, use the same range
    case CSpace_xvYCC709: // since matrix is the same as 709, use the same range
    case CSpace_BT601Gray:
    case CSpace_BT2020:
        *pLumaOffset = 16.0f;
        *pLumaExcursion = 219.0f;
        *pChromaZero = 128.0f;
        *pChromaExcursion = 224.0f;
        break;

    default:
        *pLumaOffset = 0.0f;
        *pLumaExcursion = 255.0f;
        *pChromaZero = 128.0f;
        *pChromaExcursion = 255.0f;
        break;
    }

    *pLumaOffset /= 255.0f;
    *pLumaExcursion /= 255.0f;
    *pChromaZero /= 255.0f;
    *pChromaExcursion /= 255.0f;

finish:
    return eStatus;
}

//!
//! \brief    Calculate Rgb Range and Offest
//! \details  Calculate Rgb Range and Offest
//! \param    VPHAL_CSPACE cspace
//!           [in] Source color space
//! \param    float* pLumaOffset
//!           [out] Pointer to Rgb Offset
//! \param    float* pLumaExcursion 
//!           [out] Pointer to Rgb Excursion
//! \return   MOS_STATUS
//!
MOS_STATUS VpHal_HdrGetRgbRangeAndOffset(
    VPHAL_CSPACE cspace,
    float*       pRgbOffset,
    float*       pRgbExcursion)
{
    MOS_STATUS eStatus = MOS_STATUS_SUCCESS;

    VPHAL_PUBLIC_CHK_NULL(pRgbOffset);
    VPHAL_PUBLIC_CHK_NULL(pRgbExcursion);

    switch (cspace)
    {
    case CSpace_sRGB:
    case CSpace_BT2020_RGB:
        *pRgbOffset = 0.0f;
        *pRgbExcursion = 255.0f;
        break;

    case CSpace_stRGB:
    case CSpace_BT2020_stRGB:
        *pRgbOffset = 16.0f;
        *pRgbExcursion = 219.0f;
        break;

    default:
        *pRgbOffset = 0.0f;
        *pRgbExcursion = 255.0f;
        break;
    }

    *pRgbOffset /= 255.0f;
    *pRgbExcursion /= 255.0f;

finish:
    return eStatus;
}

//!
//! \brief    Calculate Yuv To Rgb Matrix
//! \details  Calculate Yuv To Rgb Matrix
//! \param    VPHAL_CSPACE src
//!           [in] Source color space
//! \param    VPHAL_CSPACE dst
//!           [in] Dest color space
//! \param    float* pTransferMatrix
//!           [in] Pointer to input transfer matrix
//! \param    float* pOutMatrix
//!           [out] Pointer to output transfer matrix for curbe
//! \return   MOS_STATUS
//!
MOS_STATUS VpHal_HdrCalcYuvToRgbMatrix(
    VPHAL_CSPACE    src,
    VPHAL_CSPACE    dst,
    float*          pTransferMatrix,
    float*          pOutMatrix)
{
    MOS_STATUS eStatus = MOS_STATUS_SUCCESS;
    float   Y_o = 0.0f, Y_e = 0.0f, C_z = 0.0f, C_e = 0.0f;
    float   R_o = 0.0f, R_e = 0.0f;

    VPHAL_PUBLIC_CHK_NULL(pTransferMatrix);
    VPHAL_PUBLIC_CHK_NULL(pOutMatrix);

    VpHal_HdrGetRgbRangeAndOffset(dst, &R_o, &R_e);
    VpHal_HdrGetYuvRangeAndOffset(src, &Y_o, &Y_e, &C_z, &C_e);

    // after + (3x3)(3x3)
    pOutMatrix[0] = pTransferMatrix[0] * R_e / Y_e;
    pOutMatrix[4] = pTransferMatrix[4] * R_e / Y_e;
    pOutMatrix[8] = pTransferMatrix[8] * R_e / Y_e;
    pOutMatrix[1] = pTransferMatrix[1] * R_e / C_e;
    pOutMatrix[5] = pTransferMatrix[5] * R_e / C_e;
    pOutMatrix[9] = pTransferMatrix[9] * R_e / C_e;
    pOutMatrix[2] = pTransferMatrix[2] * R_e / C_e;
    pOutMatrix[6] = pTransferMatrix[6] * R_e / C_e;
    pOutMatrix[10] = pTransferMatrix[10] * R_e / C_e;

    // (3x1) - (3x3)(3x3)(3x1)
    pOutMatrix[3] = R_o - (pOutMatrix[0] * Y_o + pOutMatrix[1] * C_z + pOutMatrix[2] * C_z);
    pOutMatrix[7] = R_o - (pOutMatrix[4] * Y_o + pOutMatrix[5] * C_z + pOutMatrix[6] * C_z);
    pOutMatrix[11] = R_o - (pOutMatrix[8] * Y_o + pOutMatrix[9] * C_z + pOutMatrix[10] * C_z);

finish:
    return eStatus;
}

//!
//! \brief    Calculate Rgb To Yuv Matrix
//! \details  Calculate Rgb To Yuv Matrix
//! \param    VPHAL_CSPACE src
//!           [in] Source color space
//! \param    VPHAL_CSPACE dst
//!           [in] Dest color space
//! \param    float* pTransferMatrix
//!           [in] Pointer to input transfer matrix
//! \param    float* pOutMatrix
//!           [out] Pointer to output transfer matrix for curbe
//! \return   MOS_STATUS
//!
MOS_STATUS VpHal_HdrCalcRgbToYuvMatrix(
    VPHAL_CSPACE    src,
    VPHAL_CSPACE    dst,
    float*          pTransferMatrix,
    float*          pOutMatrix)
{
    MOS_STATUS eStatus = MOS_STATUS_SUCCESS;
    float   Y_o = 0.0f, Y_e = 0.0f, C_z = 0.0f, C_e = 0.0f;
    float   R_o = 0.0f, R_e = 0.0f;

    VPHAL_PUBLIC_CHK_NULL(pTransferMatrix);
    VPHAL_PUBLIC_CHK_NULL(pOutMatrix);

    VpHal_HdrGetRgbRangeAndOffset(src, &R_o, &R_e);
    VpHal_HdrGetYuvRangeAndOffset(dst, &Y_o, &Y_e, &C_z, &C_e);

    // multiplication of + onwards
    pOutMatrix[0] = pTransferMatrix[0] * Y_e / R_e;
    pOutMatrix[1] = pTransferMatrix[1] * Y_e / R_e;
    pOutMatrix[2] = pTransferMatrix[2] * Y_e / R_e;
    pOutMatrix[4] = pTransferMatrix[4] * C_e / R_e;
    pOutMatrix[5] = pTransferMatrix[5] * C_e / R_e;
    pOutMatrix[6] = pTransferMatrix[6] * C_e / R_e;
    pOutMatrix[8] = pTransferMatrix[8] * C_e / R_e;
    pOutMatrix[9] = pTransferMatrix[9] * C_e / R_e;
    pOutMatrix[10] = pTransferMatrix[10] * C_e / R_e;

    pOutMatrix[7] = Y_o - Y_e * R_o / R_e;
    pOutMatrix[3] = C_z;
    pOutMatrix[11] = C_z;

finish:
    return eStatus;
}

//!
//! \brief    Calculate CCM Matrix
//! \details  Calculate CCM Matrix
//! \param    float* pTransferMatrix
//!           [in] Pointer to input transfer matrix
//! \param    float* pOutMatrix
//!           [out] Pointer to output transfer matrix for curbe
//! \return   MOS_STATUS
//!
MOS_STATUS VpHal_HdrCalcCCMMatrix(
    float*          pTransferMatrix,
    float*          pOutMatrix)
{
    MOS_STATUS  eStatus = MOS_STATUS_SUCCESS;

    VPHAL_PUBLIC_CHK_NULL(pTransferMatrix);
    VPHAL_PUBLIC_CHK_NULL(pOutMatrix);

    // multiplication of + onwards
    pOutMatrix[0] = pTransferMatrix[1];
    pOutMatrix[1] = pTransferMatrix[2];
    pOutMatrix[2] = pTransferMatrix[0];
    pOutMatrix[4] = pTransferMatrix[5];
    pOutMatrix[5] = pTransferMatrix[6];
    pOutMatrix[6] = pTransferMatrix[4];
    pOutMatrix[8] = pTransferMatrix[9];
    pOutMatrix[9] = pTransferMatrix[10];
    pOutMatrix[10] = pTransferMatrix[8];

    pOutMatrix[3] = pTransferMatrix[11];
    pOutMatrix[7] = pTransferMatrix[3];
    pOutMatrix[11] = pTransferMatrix[7];

finish:
    return eStatus;
}