/*
* Copyright (c) 2016-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     codechal_decode_scalability.cpp
//! \brief    Implements the decode interface extension for decode scalability.
//! \details  Implements all functions required by CodecHal for scalability decoding.
//!

#include "codechal_decoder.h"
#include "codechal_decode_scalability.h"
#include "mos_util_user_interface.h"
#include "mos_solo_generic.h"
#include "mos_os_virtualengine_next.h"

//!
//! \brief    calculate secondary cmd buffer index
//! \details  calculate secondary cmd buffer index to get or return secondary cmd buffer
//! \param    [in]  pScalabilityState
//!                pointer to scalability decode state
//! \param    [in]  pdwBufIdxPlus1
//!                pointer to buf index, will contain the returned buf index value.
//! \return   MOS_STATUS
//!           MOS_STATUS_SUCCESS if success, else fail reason
//!
static MOS_STATUS CodecHalDecodeScalability_CalculateScdryCmdBufIndex(
    PCODECHAL_DECODE_SCALABILITY_STATE  pScalabilityState,
    uint32_t                           *pdwBufIdxPlus1)
{
    uint32_t                        HcpDecPhaseForBufIdx;
    MOS_STATUS                      eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_DECODE_FUNCTION_ENTER;

    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState);
    CODECHAL_DECODE_CHK_NULL_RETURN(pdwBufIdxPlus1);

    HcpDecPhaseForBufIdx = pScalabilityState->HcpDecPhase;
    if (pScalabilityState->HcpDecPhase == CodechalDecode::CodechalHcpDecodePhaseLegacyS2L)
    {
        //S2L commands put in the FE secondary command buffer.
        CODECHAL_DECODE_ASSERT(pScalabilityState->bShortFormatInUse);
        HcpDecPhaseForBufIdx = CODECHAL_HCP_DECODE_PHASE_FE;
    }

    //buffer index order is same as the buffer order in resScalableBatchBufs[] of MOS_VIRTUALENGINE_HINT_PARAMS
    *pdwBufIdxPlus1 = HcpDecPhaseForBufIdx - (pScalabilityState->bFESeparateSubmission ?
        CODECHAL_HCP_DECODE_PHASE_BE0 : CODECHAL_HCP_DECODE_PHASE_FE) + 1;

    return eStatus;
}

//!
//! \brief    check if valid decode phase
//! \param    [in]  pScalabilityState
//!                pointer to scalability decode state
//! \param    [in]  HcpDecPhase
//!                Hcp Decode Phase
//! \return   MOS_STATUS
//!           MOS_STATUS_SUCCESS if valid decode phase, else fail reason
//!
static MOS_STATUS CodecHalDecodeScalability_CheckDecPhaseValidity(
    PCODECHAL_DECODE_SCALABILITY_STATE  pScalabilityState,
    uint32_t                            HcpDecPhase)
{
    bool                bInValidPhase = false;
    MOS_STATUS          eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_DECODE_FUNCTION_ENTER;

    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState);

    switch (HcpDecPhase)
    {
        case CodechalDecode::CodechalHcpDecodePhaseInitialized:
            break;
        case CodechalDecode::CodechalHcpDecodePhaseLegacyS2L:
            if (!pScalabilityState->bShortFormatInUse)
            {
                bInValidPhase = true;
            }
            break;
        case CodechalDecode::CodechalHcpDecodePhaseLegacyLong:
            if (pScalabilityState->bScalableDecodeMode)
            {
                bInValidPhase = true;
            }
            break;
        case CODECHAL_HCP_DECODE_PHASE_FE:
        case CODECHAL_HCP_DECODE_PHASE_BE0:
            if (!pScalabilityState->bScalableDecodeMode)
            {
                bInValidPhase = true;
            }
            else if (pScalabilityState->ucScalablePipeNum < 2)
            {
                //at least 2 pipe
                bInValidPhase = true;
            }
            break;
        case CODECHAL_HCP_DECODE_PHASE_BE1:
        case CODECHAL_HCP_DECODE_PHASE_RESERVED:
            if (!pScalabilityState->bScalableDecodeMode)
            {
                bInValidPhase = true;
            }
            else if (pScalabilityState->ucScalablePipeNum < (HcpDecPhase - CODECHAL_HCP_DECODE_PHASE_BE0 + 1))
            {
                bInValidPhase = true;
            }
            break;
        default:
            bInValidPhase = true;
            break;
    }

    if (bInValidPhase)
    {
        eStatus = MOS_STATUS_INVALID_PARAMETER;
        CODECHAL_DECODE_ASSERTMESSAGE("invalid decode phase : %d !", HcpDecPhase);
    }

    return eStatus;
}

//!
//! \brief    Allocate fixed size resources for scalability decode
//! \details  Allocate fixed size resources for scalability decode
//! \param    [in]  pScalabilityState
//!                pointer to scalability decode state
//! \return   MOS_STATUS
//!           MOS_STATUS_SUCCESS if success, else fail reason
//!
MOS_STATUS CodecHalDecodeScalability_AllocateResources_FixedSizes(
    PCODECHAL_DECODE_SCALABILITY_STATE  pScalabilityState)
{
    PMOS_INTERFACE          pOsInterface;
    MOS_ALLOC_GFXRES_PARAMS AllocParamsForBufferLinear;
    MOS_LOCK_PARAMS         LockFlagsWriteOnly;
    uint8_t                *pData;
    MOS_STATUS              eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_DECODE_FUNCTION_ENTER;

    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState->pHwInterface);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState->pHwInterface->GetOsInterface());

    pOsInterface = pScalabilityState->pHwInterface->GetOsInterface();

    MOS_ZeroMemory(&LockFlagsWriteOnly, sizeof(MOS_LOCK_PARAMS));
    LockFlagsWriteOnly.WriteOnly = 1;

    // initiate allocation paramters
    MOS_ZeroMemory(&AllocParamsForBufferLinear, sizeof(MOS_ALLOC_GFXRES_PARAMS));
    AllocParamsForBufferLinear.Type = MOS_GFXRES_BUFFER;
    AllocParamsForBufferLinear.TileType = MOS_TILE_LINEAR;
    AllocParamsForBufferLinear.Format = Format_Buffer;

    if (pScalabilityState->Standard == CODECHAL_HEVC)//Confirmed by HW that VP9 does not need this buffer
    {
        //for Scalability --- Slice State Stream Out Buffer
        AllocParamsForBufferLinear.dwBytes = CODECHAL_HEVC_MAX_NUM_SLICES_LVL_6 * pScalabilityState->sliceStateCLs * CODECHAL_CACHELINE_SIZE;
        AllocParamsForBufferLinear.pBufName = "SliceStateStreamOut";

        eStatus = (MOS_STATUS)pOsInterface->pfnAllocateResource(
            pOsInterface,
            &AllocParamsForBufferLinear,
            &pScalabilityState->resSliceStateStreamOutBuffer);

        if (eStatus != MOS_STATUS_SUCCESS)
        {
            CODECHAL_DECODE_ASSERTMESSAGE("Failed to allocate SliceState StreamOut Buffer.");
            return eStatus;
        }
    }

    //Semaphore memory for BEs to start at the same time
    AllocParamsForBufferLinear.dwBytes = sizeof(uint32_t);
    AllocParamsForBufferLinear.pBufName = "BESemaphoreMemory";

    eStatus = (MOS_STATUS)pOsInterface->pfnAllocateResource(
        pOsInterface,
        &AllocParamsForBufferLinear,
        &pScalabilityState->resSemaMemBEs);

    if (eStatus != MOS_STATUS_SUCCESS)
    {
        CODECHAL_DECODE_ASSERTMESSAGE("Failed to allocate BE Semaphore memory.");
        return eStatus;
    }

    pData = (uint8_t*)pOsInterface->pfnLockResource(
        pOsInterface,
        &pScalabilityState->resSemaMemBEs,
        &LockFlagsWriteOnly);

    CODECHAL_DECODE_CHK_NULL_RETURN(pData);

    MOS_ZeroMemory(pData, sizeof(uint32_t));

    CODECHAL_DECODE_CHK_STATUS_RETURN(pOsInterface->pfnUnlockResource(
        pOsInterface,
        &pScalabilityState->resSemaMemBEs));

    AllocParamsForBufferLinear.dwBytes = sizeof(uint32_t);
    AllocParamsForBufferLinear.pBufName = "DelayMinusMemory";

    eStatus = (MOS_STATUS)pOsInterface->pfnAllocateResource(
        pOsInterface,
        &AllocParamsForBufferLinear,
        &pScalabilityState->resDelayMinus);

    if (eStatus != MOS_STATUS_SUCCESS)
    {
        CODECHAL_DECODE_ASSERTMESSAGE("Failed to allocate delay minus memory.");
        return eStatus;
    }

    pData = (uint8_t*)pOsInterface->pfnLockResource(
        pOsInterface,
        &pScalabilityState->resDelayMinus,
        &LockFlagsWriteOnly);

    CODECHAL_DECODE_CHK_NULL_RETURN(pData);

    MOS_ZeroMemory(pData, sizeof(uint32_t));

    CODECHAL_DECODE_CHK_STATUS_RETURN(pOsInterface->pfnUnlockResource(
        pOsInterface,
        &pScalabilityState->resDelayMinus));

    if (pScalabilityState->pHwInterface->GetMfxInterface()->GetNumVdbox() > 2)
    {
        if (pScalabilityState->bFESeparateSubmission)
        {
            //SW Semaphore sync object for FE/BE synchronization
            CODECHAL_DECODE_CHK_STATUS_RETURN(pOsInterface->pfnCreateSyncResource(pOsInterface, &pScalabilityState->resFeBeSyncObject));
        }
        else if (pOsInterface->bUseHwSemaForResSyncInVE)
        {
            //Semaphore memory for FE /BE synchronization
            AllocParamsForBufferLinear.dwBytes = sizeof(uint32_t);
            AllocParamsForBufferLinear.pBufName = "FEBESemaphMemory";

            eStatus = (MOS_STATUS)pOsInterface->pfnAllocateResource(
                pOsInterface,
                &AllocParamsForBufferLinear,
                &pScalabilityState->resSemaMemFEBE);

            if (eStatus != MOS_STATUS_SUCCESS)
            {
                CODECHAL_DECODE_ASSERTMESSAGE("Failed to allocate FEBE Semaph memory.");
                return eStatus;
            }

            pData = (uint8_t*)pOsInterface->pfnLockResource(
                pOsInterface,
                &pScalabilityState->resSemaMemFEBE,
                &LockFlagsWriteOnly);

            CODECHAL_DECODE_CHK_NULL_RETURN(pData);

            MOS_ZeroMemory(pData, sizeof(uint32_t));

            CODECHAL_DECODE_CHK_STATUS_RETURN(pOsInterface->pfnUnlockResource(
                pOsInterface,
                &pScalabilityState->resSemaMemFEBE));
        }
    }

    AllocParamsForBufferLinear.dwBytes = sizeof(CODECHAL_DECODE_SCALABILITY_FE_STATUS);
    AllocParamsForBufferLinear.pBufName = "FEStatusBuffer";
    eStatus = (MOS_STATUS)pOsInterface->pfnAllocateResource(
        pOsInterface,
        &AllocParamsForBufferLinear,
        &pScalabilityState->resFEStatusBuffer);

    if (eStatus != MOS_STATUS_SUCCESS)
    {
        CODECHAL_DECODE_ASSERTMESSAGE("Failed to allocate FE status Buffer.");
        return eStatus;
    }

    //Semaphore memory for frame decode completion synchronization
    AllocParamsForBufferLinear.dwBytes = sizeof(uint32_t);
    AllocParamsForBufferLinear.pBufName = "CompletionSemaphMemory";

    eStatus = (MOS_STATUS)pOsInterface->pfnAllocateResource(
        pOsInterface,
        &AllocParamsForBufferLinear,
        &pScalabilityState->resSemaMemCompletion);

    if (eStatus != MOS_STATUS_SUCCESS)
    {
        CODECHAL_DECODE_ASSERTMESSAGE("Failed to allocate Completion Semaph memory.");
        return eStatus;
    }

    pData = (uint8_t*)pOsInterface->pfnLockResource(
        pOsInterface,
        &pScalabilityState->resSemaMemCompletion,
        &LockFlagsWriteOnly);

    CODECHAL_DECODE_CHK_NULL_RETURN(pData);

    MOS_ZeroMemory(pData, sizeof(uint32_t));

    CODECHAL_DECODE_CHK_STATUS_RETURN(pOsInterface->pfnUnlockResource(
        pOsInterface,
        &pScalabilityState->resSemaMemCompletion));

    return eStatus;
}

//!
//! \brief    Get secondary cmd buffer
//! \param    [in]  pScalabilityState
//!                Scalability decode state
//! \param    [in] pSdryCmdBuf
//!                secondary cmd buffer address
//! \return   MOS_STATUS
//!           MOS_STATUS_SUCCESS if success, else fail reason
//!
static MOS_STATUS CodecHalDecodeScalability_GetVESecondaryCmdBuffer(
    PCODECHAL_DECODE_SCALABILITY_STATE  pScalabilityState,
    PMOS_COMMAND_BUFFER                 pSdryCmdBuf)
{
    PMOS_INTERFACE                  pOsInterface;
    uint32_t                        HcpDecPhase;
    uint32_t                        dwBufIdxPlus1 = 0;
    MOS_STATUS                      eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_DECODE_FUNCTION_ENTER;

    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState);
    CODECHAL_DECODE_CHK_NULL_RETURN(pSdryCmdBuf);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState->pHwInterface);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState->pHwInterface->GetOsInterface());

    pOsInterface        = pScalabilityState->pHwInterface->GetOsInterface();
    HcpDecPhase         = pScalabilityState->HcpDecPhase;

    //calculate bufidx for getting secondary cmd buffer.
    CODECHAL_DECODE_CHK_STATUS_RETURN(CodecHalDecodeScalability_CalculateScdryCmdBufIndex(pScalabilityState, &dwBufIdxPlus1));
    //Check if valid decode phase
    CODECHAL_DECODE_CHK_STATUS_RETURN(CodecHalDecodeScalability_CheckDecPhaseValidity(pScalabilityState, HcpDecPhase));

    //Get batch buffer according to current decode phase
    switch (HcpDecPhase)
    {
        case CodechalDecode::CodechalHcpDecodePhaseLegacyS2L:
            //Note: no break here, S2L and FE commands put in one secondary command buffer.
        case CODECHAL_HCP_DECODE_PHASE_FE:
            if(!pScalabilityState->bFESeparateSubmission)
            {
                CODECHAL_DECODE_CHK_STATUS_RETURN(pOsInterface->pfnGetCommandBuffer(pOsInterface, pSdryCmdBuf, dwBufIdxPlus1));
                CODECHAL_DECODE_CHK_NULL_RETURN(pSdryCmdBuf);
            }
            else
            {
                //if FE separate submission, S2L and FE cmd buffer are in primary cmd buffer, shall not call this function to get secondary cmd buffer
                eStatus = MOS_STATUS_INVALID_PARAMETER;
                CODECHAL_DECODE_ASSERTMESSAGE("S2L or FE does not need secondary cmd buffer in FE separate submission!");
            }
            break;
        case CODECHAL_HCP_DECODE_PHASE_BE0:
        case CODECHAL_HCP_DECODE_PHASE_BE1:
        case CODECHAL_HCP_DECODE_PHASE_RESERVED:
            CODECHAL_DECODE_CHK_STATUS_RETURN(pOsInterface->pfnGetCommandBuffer(pOsInterface, pSdryCmdBuf, dwBufIdxPlus1));
            CODECHAL_DECODE_CHK_NULL_RETURN(pSdryCmdBuf);
            break;
        default:
            //never comes here because other decode phase already checked invalid in function CodecHalDecodeScalability_CheckDecPhaseValidity,
            eStatus = MOS_STATUS_INVALID_PARAMETER;
            break;
    }

    return eStatus;
}

MOS_STATUS CodecHalDecodeScalability_AllocateResources_VariableSizes(
    PCODECHAL_DECODE_SCALABILITY_STATE   pScalabilityState,
    PMHW_VDBOX_HCP_BUFFER_SIZE_PARAMS    pHcpBufSizeParam,
    PMHW_VDBOX_HCP_BUFFER_REALLOC_PARAMS pAllocParam)
{
    MOS_ALLOC_GFXRES_PARAMS AllocParamsForBufferLinear;
    PMOS_INTERFACE          pOsInterface;
    MOS_STATUS              eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_DECODE_FUNCTION_ENTER;

    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState->pHwInterface);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState->pHwInterface->GetOsInterface());
    CODECHAL_DECODE_CHK_NULL_RETURN(pHcpBufSizeParam);
    CODECHAL_DECODE_CHK_NULL_RETURN(pAllocParam);

    pOsInterface = pScalabilityState->pHwInterface->GetOsInterface();

    // initiate allocation paramters
    MOS_ZeroMemory(&AllocParamsForBufferLinear, sizeof(MOS_ALLOC_GFXRES_PARAMS));
    AllocParamsForBufferLinear.Type     = MOS_GFXRES_BUFFER;
    AllocParamsForBufferLinear.TileType = MOS_TILE_LINEAR;
    AllocParamsForBufferLinear.Format   = Format_Buffer;

    if (pScalabilityState->Standard == CODECHAL_HEVC)//Confirmed by HW that this buffer is not used in VP9 decoding
    {
        CODECHAL_DECODE_CHK_STATUS_RETURN(pScalabilityState->pfnIsHcpBufferReallocNeeded(pScalabilityState->pHwInterface,
                                                                                 MHW_VDBOX_HCP_INTERNAL_BUFFER_MV_UP_RT_COL,
                                                                                 pAllocParam));
        if (pAllocParam->bNeedBiggerSize || Mos_ResourceIsNull(&pScalabilityState->resMvUpRightColStoreBuffer))
        {
            if (!Mos_ResourceIsNull(&pScalabilityState->resMvUpRightColStoreBuffer))
            {
                pOsInterface->pfnFreeResource(
                    pOsInterface,
                    &pScalabilityState->resMvUpRightColStoreBuffer);
            }
            // MV UpperRight Column Store Buffer
            CODECHAL_DECODE_CHK_STATUS_RETURN(pScalabilityState->pfnGetHcpBufferSize(pScalabilityState->pHwInterface,
                                                                             MHW_VDBOX_HCP_INTERNAL_BUFFER_MV_UP_RT_COL,
                                                                             pHcpBufSizeParam));
            AllocParamsForBufferLinear.dwBytes  = pHcpBufSizeParam->dwBufferSize;
            AllocParamsForBufferLinear.pBufName = "MVUpperRightColumnStore";

            eStatus = (MOS_STATUS)pOsInterface->pfnAllocateResource(
                pOsInterface,
                &AllocParamsForBufferLinear,
                &pScalabilityState->resMvUpRightColStoreBuffer);

            if (eStatus != MOS_STATUS_SUCCESS)
            {
                CODECHAL_DECODE_ASSERTMESSAGE("Failed to allocate MV UpperRight Column Store Buffer.");
                return eStatus;
            }
        }
    }

    CODECHAL_DECODE_CHK_STATUS_RETURN(pScalabilityState->pfnIsHcpBufferReallocNeeded(pScalabilityState->pHwInterface,
                                                                             MHW_VDBOX_HCP_INTERNAL_BUFFER_INTRA_PRED_UP_RIGHT_COL,
                                                                             pAllocParam));
    if (pAllocParam->bNeedBiggerSize || Mos_ResourceIsNull(&pScalabilityState->resIntraPredUpRightColStoreBuffer))
    {
        if (!Mos_ResourceIsNull(&pScalabilityState->resIntraPredUpRightColStoreBuffer))
        {
            pOsInterface->pfnFreeResource(
                pOsInterface,
                &pScalabilityState->resIntraPredUpRightColStoreBuffer);
        }
        // IntraPred UpperRight Column Store Buffer
        CODECHAL_DECODE_CHK_STATUS_RETURN(pScalabilityState->pfnGetHcpBufferSize(pScalabilityState->pHwInterface,
                                                                         MHW_VDBOX_HCP_INTERNAL_BUFFER_INTRA_PRED_UP_RIGHT_COL,
                                                                         pHcpBufSizeParam));
        AllocParamsForBufferLinear.dwBytes  = pHcpBufSizeParam->dwBufferSize;
         AllocParamsForBufferLinear.pBufName = "IntraPredUpperRightColumnStore";

        eStatus = (MOS_STATUS)pOsInterface->pfnAllocateResource(
            pOsInterface,
            &AllocParamsForBufferLinear,
            &pScalabilityState->resIntraPredUpRightColStoreBuffer);

        if (eStatus != MOS_STATUS_SUCCESS)
        {
            CODECHAL_DECODE_ASSERTMESSAGE("Failed to allocate IntraPred UpperRight Column Store Buffer.");
            return eStatus;
        }
    }

    CODECHAL_DECODE_CHK_STATUS_RETURN(pScalabilityState->pfnIsHcpBufferReallocNeeded(pScalabilityState->pHwInterface,
                                                                             MHW_VDBOX_HCP_INTERNAL_BUFFER_INTRA_PRED_LFT_RECON_COL,
                                                                             pAllocParam));
    if (pAllocParam->bNeedBiggerSize || Mos_ResourceIsNull(&pScalabilityState->resIntraPredLeftReconColStoreBuffer))
    {
        if (!Mos_ResourceIsNull(&pScalabilityState->resIntraPredLeftReconColStoreBuffer))
        {
            pOsInterface->pfnFreeResource(
                pOsInterface,
                &pScalabilityState->resIntraPredLeftReconColStoreBuffer);
        }
        // IntraPred Left Recon Column Store Buffer
        CODECHAL_DECODE_CHK_STATUS_RETURN(pScalabilityState->pfnGetHcpBufferSize(pScalabilityState->pHwInterface,
                                                                         MHW_VDBOX_HCP_INTERNAL_BUFFER_INTRA_PRED_LFT_RECON_COL,
                                                                         pHcpBufSizeParam));
        AllocParamsForBufferLinear.dwBytes  = pHcpBufSizeParam->dwBufferSize;
        AllocParamsForBufferLinear.pBufName = "IntraPredLeftReconColumnStore";

        eStatus = (MOS_STATUS)pOsInterface->pfnAllocateResource(
            pOsInterface,
            &AllocParamsForBufferLinear,
            &pScalabilityState->resIntraPredLeftReconColStoreBuffer);

        if (eStatus != MOS_STATUS_SUCCESS)
        {
            CODECHAL_DECODE_ASSERTMESSAGE("Failed to allocate IntraPred Left Recon Column Store Buffer.");
            return eStatus;
        }
    }

    CODECHAL_DECODE_CHK_STATUS_RETURN(CodecHalDecodeScalability_AllocateCABACStreamOutBuffer(pScalabilityState,
                                                                            pHcpBufSizeParam,
                                                                            pAllocParam,
                                                                            &pScalabilityState->resCABACSyntaxStreamOutBuffer[0]));

    pScalabilityState->presCABACStreamOutBuffer = &pScalabilityState->resCABACSyntaxStreamOutBuffer[0];

    return eStatus;
}

MOS_STATUS CodecHalDecodeScalability_AllocateCABACStreamOutBuffer(
    PCODECHAL_DECODE_SCALABILITY_STATE   pScalabilityState,
    PMHW_VDBOX_HCP_BUFFER_SIZE_PARAMS    pHcpBufSizeParam,
    PMHW_VDBOX_HCP_BUFFER_REALLOC_PARAMS pAllocParam,
    PMOS_RESOURCE                        presCABACStreamOutBuffer)
{
    PMOS_INTERFACE          pOsInterface;
    MOS_ALLOC_GFXRES_PARAMS AllocParamsForBufferLinear;
    MOS_STATUS              eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_DECODE_FUNCTION_ENTER;

    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState->pHwInterface);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState->pHwInterface->GetOsInterface());
    CODECHAL_DECODE_CHK_NULL_RETURN(pHcpBufSizeParam);
    CODECHAL_DECODE_CHK_NULL_RETURN(pAllocParam);
    CODECHAL_DECODE_CHK_NULL_RETURN(presCABACStreamOutBuffer);

    pOsInterface = pScalabilityState->pHwInterface->GetOsInterface();

    // initiate allocation paramters
    MOS_ZeroMemory(&AllocParamsForBufferLinear, sizeof(MOS_ALLOC_GFXRES_PARAMS));
    AllocParamsForBufferLinear.Type     = MOS_GFXRES_BUFFER;
    AllocParamsForBufferLinear.TileType = MOS_TILE_LINEAR;
    AllocParamsForBufferLinear.Format   = Format_Buffer;

    CODECHAL_DECODE_CHK_STATUS_RETURN(pScalabilityState->pfnIsHcpBufferReallocNeeded(pScalabilityState->pHwInterface,
                                                                             MHW_VDBOX_HCP_INTERNAL_BUFFER_CABAC_STREAMOUT,
                                                                             pAllocParam));
    if (pAllocParam->bNeedBiggerSize || Mos_ResourceIsNull(presCABACStreamOutBuffer))
    {
        if (!Mos_ResourceIsNull(presCABACStreamOutBuffer))
        {
            pOsInterface->pfnFreeResource(
                pOsInterface,
                presCABACStreamOutBuffer);
        }

        CODECHAL_DECODE_CHK_STATUS_RETURN(pScalabilityState->pfnGetHcpBufferSize(pScalabilityState->pHwInterface,
                                                                         MHW_VDBOX_HCP_INTERNAL_BUFFER_CABAC_STREAMOUT,
                                                                         pHcpBufSizeParam));

        pScalabilityState->dwCABACSyntaxStreamOutBufferSize = pHcpBufSizeParam->dwBufferSize;

        AllocParamsForBufferLinear.dwBytes = pHcpBufSizeParam->dwBufferSize;
        AllocParamsForBufferLinear.pBufName = "CABACStreamOutBuffer";

        eStatus = (MOS_STATUS)pOsInterface->pfnAllocateResource(
            pOsInterface,
            &AllocParamsForBufferLinear,
            presCABACStreamOutBuffer);

        if (eStatus != MOS_STATUS_SUCCESS)
        {
            CODECHAL_DECODE_ASSERTMESSAGE("Failed to allocate CABAC StreamOut Buffer.");
            return eStatus;
        }
    }

    return eStatus;
}

void CodecHalDecodeScalability_Destroy (
    PCODECHAL_DECODE_SCALABILITY_STATE  pScalabilityState)
{
    PMOS_INTERFACE      pOsInterface;

    CODECHAL_DECODE_FUNCTION_ENTER;

    CODECHAL_DECODE_CHK_NULL_NO_STATUS_RETURN(pScalabilityState);
    CODECHAL_DECODE_CHK_NULL_NO_STATUS_RETURN(pScalabilityState->pHwInterface);
    CODECHAL_DECODE_CHK_NULL_NO_STATUS_RETURN(pScalabilityState->pHwInterface->GetOsInterface());
    pOsInterface = pScalabilityState->pHwInterface->GetOsInterface();

    pOsInterface->pfnFreeResource(
        pOsInterface,
        &pScalabilityState->resSliceStateStreamOutBuffer);
    pOsInterface->pfnFreeResource(
        pOsInterface,
        &pScalabilityState->resMvUpRightColStoreBuffer);
    pOsInterface->pfnFreeResource(
        pOsInterface,
        &pScalabilityState->resIntraPredLeftReconColStoreBuffer);
    pOsInterface->pfnFreeResource(
        pOsInterface,
        &pScalabilityState->resIntraPredUpRightColStoreBuffer);
    for (int i = 0; i < CODECHAL_HCP_STREAMOUT_BUFFER_MAX_NUM; i++)
    {
        pOsInterface->pfnFreeResource(
            pOsInterface,
            &pScalabilityState->resCABACSyntaxStreamOutBuffer[i]);
    }
    pOsInterface->pfnFreeResource(
        pOsInterface,
        &pScalabilityState->resSemaMemBEs);
    pOsInterface->pfnFreeResource(
        pOsInterface,
        &pScalabilityState->resDelayMinus);
    if (pOsInterface->bUseHwSemaForResSyncInVE)
    {
        pOsInterface->pfnFreeResource(
            pOsInterface,
            &pScalabilityState->resSemaMemFEBE);
    }
    pOsInterface->pfnFreeResource(
        pOsInterface,
        &pScalabilityState->resFEStatusBuffer);
    pOsInterface->pfnDestroySyncResource(pOsInterface, &pScalabilityState->resFeBeSyncObject);

    pOsInterface->pfnFreeResource(
        pOsInterface,
        &pScalabilityState->resSemaMemCompletion);

    return;
}

MOS_STATUS CodecHalDecodeScalability_GetCmdBufferToUse(
    PCODECHAL_DECODE_SCALABILITY_STATE  pScalabilityState,
    PMOS_COMMAND_BUFFER                 pScdryCmdBuf,
    PMOS_COMMAND_BUFFER                 *ppCmdBufToUse)
{
    MOS_STATUS                      eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_DECODE_FUNCTION_ENTER;

    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState);
    CODECHAL_DECODE_CHK_NULL_RETURN(ppCmdBufToUse);

    if (!CodecHalDecodeScalabilityIsFESeparateSubmission(pScalabilityState) ||
        CodecHalDecodeScalabilityIsBEPhase(pScalabilityState))
    {
        pScalabilityState->bUseSecdryCmdBuffer = true;
        CODECHAL_DECODE_CHK_STATUS_RETURN(CodecHalDecodeScalability_GetVESecondaryCmdBuffer(pScalabilityState, pScdryCmdBuf));
        *ppCmdBufToUse = pScdryCmdBuf;
    }
    else
    {
        pScalabilityState->bUseSecdryCmdBuffer = false;
    }

    return eStatus;
}

MOS_STATUS CodecHalDecodeScalability_ReturnSdryCmdBuffer(
    PCODECHAL_DECODE_SCALABILITY_STATE  pScalabilityState,
    PMOS_COMMAND_BUFFER                 pSdryCmdBuf)
{
    PMOS_INTERFACE                  pOsInterface;
    uint32_t                        HcpDecPhase;
    uint32_t                        dwBufIdxPlus1;
    MOS_STATUS                      eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_DECODE_FUNCTION_ENTER;

    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState);
    CODECHAL_DECODE_CHK_NULL_RETURN(pSdryCmdBuf);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState->pHwInterface);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState->pHwInterface->GetOsInterface());

    pOsInterface = pScalabilityState->pHwInterface->GetOsInterface();

    if (!pScalabilityState->bUseSecdryCmdBuffer)
    {
        return eStatus;
    }

    HcpDecPhase         = pScalabilityState->HcpDecPhase;

    //calculate bufidx for getting secondary cmd buffer.
    CODECHAL_DECODE_CHK_STATUS_RETURN(CodecHalDecodeScalability_CalculateScdryCmdBufIndex(pScalabilityState, &dwBufIdxPlus1));
    //Check if valid decode phase
    CODECHAL_DECODE_CHK_STATUS_RETURN(CodecHalDecodeScalability_CheckDecPhaseValidity(pScalabilityState, HcpDecPhase));

    //Get batch buffer according to current decode phase
    switch (HcpDecPhase)
    {
        case CodechalDecode::CodechalHcpDecodePhaseLegacyS2L:
            //Note: no break here, S2L and FE commands put in one secondary command buffer.
        case CODECHAL_HCP_DECODE_PHASE_FE:
            if(!pScalabilityState->bFESeparateSubmission)
            {
                pOsInterface->pfnReturnCommandBuffer(pOsInterface, pSdryCmdBuf, dwBufIdxPlus1);
            }
            else
            {
                //if FE separate submission, S2L and FE cmd buffer are in primary cmd buffer, shall not call this function to get secondary cmd buffer
                eStatus = MOS_STATUS_INVALID_PARAMETER;
                CODECHAL_DECODE_ASSERTMESSAGE("S2L or FE does not need secondary cmd buffer in FE separate submission!");
            }
            break;
        case CODECHAL_HCP_DECODE_PHASE_BE0:
        case CODECHAL_HCP_DECODE_PHASE_BE1:
        case CODECHAL_HCP_DECODE_PHASE_RESERVED:
            pOsInterface->pfnReturnCommandBuffer(pOsInterface, pSdryCmdBuf, dwBufIdxPlus1);
            break;
        default:
            //never comes here because other decode phase already checked invalid in function CodecHalDecodeScalability_CheckDecPhaseValidity,
            eStatus = MOS_STATUS_INVALID_PARAMETER;
            break;
    }

    return eStatus;
}

MOS_STATUS CodecHalDecodeScalablity_SetFECabacStreamoutOverflowStatus(
    PCODECHAL_DECODE_SCALABILITY_STATE  pScalabilityState,
    PMOS_COMMAND_BUFFER                 pCmdBufferInUse)
{
    MhwMiInterface                      *pMiInterface;
    CodechalHwInterface                 *pHwInterface;
    MmioRegistersMfx                    *pMmioRegistersMfx;
    MmioRegistersHcp                    *pMmioRegistersHcp;
    MHW_MI_STORE_REGISTER_MEM_PARAMS    StoreRegParams;
    MHW_MI_STORE_DATA_PARAMS            StoreDataParams;
    MHW_MI_LOAD_REGISTER_REG_PARAMS     LoadRegRegParams;
    MHW_MI_LOAD_REGISTER_IMM_PARAMS     LoadRegisterImmParams;
    MHW_MI_FLUSH_DW_PARAMS              FlushDwParams;
    MHW_MI_MATH_PARAMS                  MiMathParams;
    MHW_MI_ALU_PARAMS                   MiAluParams[4];
    MOS_STATUS                          eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_DECODE_FUNCTION_ENTER;

    CODECHAL_DECODE_CHK_NULL_RETURN(pCmdBufferInUse);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState);

    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState->pHwInterface);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState->pHwInterface->GetMiInterface());
    pMiInterface  = pScalabilityState->pHwInterface->GetMiInterface();
    pHwInterface = pScalabilityState->pHwInterface;

    //relative MMIO addressing
    pMmioRegistersMfx   = pHwInterface->GetMfxInterface()->GetMmioRegisters(MHW_VDBOX_NODE_1);
    pMmioRegistersHcp   = pHwInterface->GetHcpInterface()->GetMmioRegisters(MHW_VDBOX_NODE_1);

    MOS_ZeroMemory(&FlushDwParams, sizeof(FlushDwParams));
    CODECHAL_DECODE_CHK_STATUS_RETURN(pMiInterface->AddMiFlushDwCmd(pCmdBufferInUse, &FlushDwParams));

    // store stream out size register to general purpose register0 - Hi=0, Lo=FE stream out size
    MOS_ZeroMemory(&LoadRegRegParams, sizeof(LoadRegRegParams));
    LoadRegRegParams.dwSrcRegister = pMmioRegistersHcp->hcpDebugFEStreamOutSizeRegOffset;
    LoadRegRegParams.dwDstRegister = pMmioRegistersMfx->generalPurposeRegister0LoOffset;
    CODECHAL_DECODE_CHK_STATUS_RETURN(pMiInterface->AddMiLoadRegisterRegCmd(
        pCmdBufferInUse,
        &LoadRegRegParams));
    MOS_ZeroMemory(&LoadRegisterImmParams, sizeof(LoadRegisterImmParams));
    LoadRegisterImmParams.dwData        = 0;
    LoadRegisterImmParams.dwRegister    = pMmioRegistersMfx->generalPurposeRegister0HiOffset;
    CODECHAL_DECODE_CHK_STATUS_RETURN(pMiInterface->AddMiLoadRegisterImmCmd(
        pCmdBufferInUse,
        &LoadRegisterImmParams));

    // load allocated size to general purpose register4 - Hi = 0
    MOS_ZeroMemory(&LoadRegisterImmParams, sizeof(LoadRegisterImmParams));
    LoadRegisterImmParams.dwData        = pScalabilityState->dwCABACSyntaxStreamOutBufferSize;
    LoadRegisterImmParams.dwRegister    = pMmioRegistersMfx->generalPurposeRegister4LoOffset;
    CODECHAL_DECODE_CHK_STATUS_RETURN(pMiInterface->AddMiLoadRegisterImmCmd(
        pCmdBufferInUse,
        &LoadRegisterImmParams));
    MOS_ZeroMemory(&LoadRegisterImmParams, sizeof(LoadRegisterImmParams));
    LoadRegisterImmParams.dwData        = 0;
    LoadRegisterImmParams.dwRegister    = pMmioRegistersMfx->generalPurposeRegister4HiOffset;
    CODECHAL_DECODE_CHK_STATUS_RETURN(pMiInterface->AddMiLoadRegisterImmCmd(
        pCmdBufferInUse,
        &LoadRegisterImmParams));

    //perform the sub operation(reported size - allocated size)
    MOS_ZeroMemory(&MiMathParams, sizeof(MiMathParams));
    MOS_ZeroMemory(&MiAluParams, sizeof(MiAluParams));
    // load     srcA, reg0
    MiAluParams[0].AluOpcode    = MHW_MI_ALU_LOAD;
    MiAluParams[0].Operand1     = MHW_MI_ALU_SRCA;
    MiAluParams[0].Operand2     = MHW_MI_ALU_GPREG0;
    // load     srcB, reg4
    MiAluParams[1].AluOpcode    = MHW_MI_ALU_LOAD;
    MiAluParams[1].Operand1     = MHW_MI_ALU_SRCB;
    MiAluParams[1].Operand2     = MHW_MI_ALU_GPREG4;
    // sub      srcA, srcB
    MiAluParams[2].AluOpcode    = MHW_MI_ALU_SUB;
    MiAluParams[2].Operand1     = MHW_MI_ALU_SRCB;
    MiAluParams[2].Operand2     = MHW_MI_ALU_GPREG4;
    // store      reg0, CF
    MiAluParams[3].AluOpcode    = MHW_MI_ALU_STORE;
    MiAluParams[3].Operand1     = MHW_MI_ALU_GPREG0;
    MiAluParams[3].Operand2     = MHW_MI_ALU_CF;
    MiMathParams.pAluPayload    = MiAluParams;
    MiMathParams.dwNumAluParams = 4; // four ALU commands needed for this substract opertaion. see following ALU commands.
    CODECHAL_DECODE_CHK_STATUS_RETURN(pMiInterface->AddMiMathCmd(
        pCmdBufferInUse,
        &MiMathParams));
    // store the carry flag of (reported size - allocated size),
    // if reported size < allocated size,  the carry flag will be 0xFFFFFFFF, else carry flag will be 0x0.
    MOS_ZeroMemory(&StoreRegParams, sizeof(StoreRegParams));
    StoreRegParams.presStoreBuffer      = &pScalabilityState->resFEStatusBuffer;
    StoreRegParams.dwOffset             = CODECHAL_OFFSETOF(CODECHAL_DECODE_SCALABILITY_FE_STATUS, dwCarryFlagOfReportedSizeMinusAllocSize);
    StoreRegParams.dwRegister           = pMmioRegistersMfx->generalPurposeRegister0LoOffset;
    CODECHAL_DECODE_CHK_STATUS_RETURN(pMiInterface->AddMiStoreRegisterMemCmd(pCmdBufferInUse, &StoreRegParams));

    return eStatus;
}

MOS_STATUS CodecHalDecodeScalablity_GetFEReportedCabacStreamoutBufferSize(
    PCODECHAL_DECODE_SCALABILITY_STATE  pScalabilityState,
    PMOS_COMMAND_BUFFER                 pCmdBufferInUse)
{
    MhwMiInterface                      *pMiInterface;
    CodechalHwInterface                 *pHwInterface;
    MmioRegistersHcp                    *pMmioRegistersHcp;
    MHW_MI_STORE_REGISTER_MEM_PARAMS    StoreRegParams;
    MHW_MI_FLUSH_DW_PARAMS              FlushDwParams;
    MOS_STATUS                          eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_DECODE_FUNCTION_ENTER;

    CODECHAL_DECODE_CHK_NULL_RETURN(pCmdBufferInUse);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState);

    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState->pHwInterface);
    pMiInterface = pScalabilityState->pHwInterface->GetMiInterface();
    pHwInterface = pScalabilityState->pHwInterface;

    //relative MMIO addressing
    pMmioRegistersHcp = pHwInterface->GetHcpInterface()->GetMmioRegisters(MHW_VDBOX_NODE_1);

    MOS_ZeroMemory(&FlushDwParams, sizeof(FlushDwParams));
    CODECHAL_DECODE_CHK_STATUS_RETURN(pMiInterface->AddMiFlushDwCmd(pCmdBufferInUse, &FlushDwParams));

    //store the cabac streamout buff size in register into mem
    MOS_ZeroMemory(&StoreRegParams, sizeof(StoreRegParams));
    StoreRegParams.presStoreBuffer = &pScalabilityState->resFEStatusBuffer;
    StoreRegParams.dwOffset = CODECHAL_OFFSETOF(CODECHAL_DECODE_SCALABILITY_FE_CABAC_STREAMOUT_BUFF_SIZE, dwCabacStreamoutBuffSize);
    StoreRegParams.dwRegister = pMmioRegistersHcp->hcpDebugFEStreamOutSizeRegOffset;
    CODECHAL_DECODE_CHK_STATUS_RETURN(pMiInterface->AddMiStoreRegisterMemCmd(pCmdBufferInUse, &StoreRegParams));

    return eStatus;
}

MOS_STATUS CodecHalDecodeScalability_DetermineDecodePhase(
    PCODECHAL_DECODE_SCALABILITY_STATE  pScalabilityState,
    uint32_t                            *pHcpDecPhase)
{
    uint32_t                        CurPhase;
    MOS_STATUS                      eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_DECODE_FUNCTION_ENTER;

    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState);
    CODECHAL_DECODE_CHK_NULL_RETURN(pHcpDecPhase);

    CurPhase = *pHcpDecPhase;
    //Check if valid decode phase
    CODECHAL_DECODE_CHK_STATUS_RETURN(CodecHalDecodeScalability_CheckDecPhaseValidity(pScalabilityState, CurPhase));

    if (CodecHalDecodeScalabilityIsScalableMode(pScalabilityState))
    {
        switch (CurPhase)
        {
            case CodechalDecode::CodechalHcpDecodePhaseInitialized:
                if (pScalabilityState->bShortFormatInUse)
                {
                    *pHcpDecPhase = CodechalDecode::CodechalHcpDecodePhaseLegacyS2L;
                }
                else
                {
                    *pHcpDecPhase = CODECHAL_HCP_DECODE_PHASE_FE;
                }
                break;
            case CodechalDecode::CodechalHcpDecodePhaseLegacyS2L:
                *pHcpDecPhase = CODECHAL_HCP_DECODE_PHASE_FE;
                break;
            case CODECHAL_HCP_DECODE_PHASE_FE:
                *pHcpDecPhase = CODECHAL_HCP_DECODE_PHASE_BE0;
                break;
            case CODECHAL_HCP_DECODE_PHASE_BE0:
                *pHcpDecPhase = CODECHAL_HCP_DECODE_PHASE_BE1;
                break;
            case CODECHAL_HCP_DECODE_PHASE_BE1:
                *pHcpDecPhase = CODECHAL_HCP_DECODE_PHASE_RESERVED;
                break;
            default:
                //never comes here because other decode phase already checked invalid in function CodecHalDecodeScalability_CheckDecPhaseValidity,
                eStatus = MOS_STATUS_INVALID_PARAMETER;
                break;
        }
    }
    else
    {
        switch (CurPhase)
        {
            case CodechalDecode::CodechalHcpDecodePhaseInitialized:
                if (pScalabilityState->bShortFormatInUse)
                {
                    *pHcpDecPhase = CodechalDecode::CodechalHcpDecodePhaseLegacyS2L;
                }
                else
                {
                    *pHcpDecPhase = CodechalDecode::CodechalHcpDecodePhaseLegacyLong;
                }
                break;
            case CodechalDecode::CodechalHcpDecodePhaseLegacyS2L:
                *pHcpDecPhase = CodechalDecode::CodechalHcpDecodePhaseLegacyLong;
                break;
            default:
                //never comes here because other decode phase already checked invalid in function CodecHalDecodeScalability_CheckDecPhaseValidity,
                eStatus = MOS_STATUS_INVALID_PARAMETER;
                break;
        }
    }

    pScalabilityState->HcpDecPhase = *pHcpDecPhase;

    return eStatus;
}

MOS_STATUS CodecHalDecodeScalability_DetermineSendWatchdogTimerStart(
    PCODECHAL_DECODE_SCALABILITY_STATE  pScalabilityState,
    bool                                *pbSend)
{
    uint32_t                        HcpDecPhase;
    MOS_STATUS                      eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_DECODE_FUNCTION_ENTER;

    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState);
    HcpDecPhase  = pScalabilityState->HcpDecPhase;

    //Check if valid decode phase
    CODECHAL_DECODE_CHK_STATUS_RETURN(CodecHalDecodeScalability_CheckDecPhaseValidity(pScalabilityState, HcpDecPhase));

    switch (HcpDecPhase)
    {
        case CodechalDecode::CodechalHcpDecodePhaseLegacyS2L:
            *pbSend = true;
            break;
        case CODECHAL_HCP_DECODE_PHASE_FE:
            *pbSend = !pScalabilityState->bShortFormatInUse;
            break;
        case CODECHAL_HCP_DECODE_PHASE_BE0:
        case CODECHAL_HCP_DECODE_PHASE_BE1:
        case CODECHAL_HCP_DECODE_PHASE_RESERVED:
            *pbSend = true;
            break;
        default:
            //never comes here because other decode phase already checked invalid in function CodecHalDecodeScalability_CheckDecPhaseValidity,
            eStatus = MOS_STATUS_INVALID_PARAMETER;
            break;
    }

    return eStatus;
}

MOS_STATUS CodecHalDecodeScalability_SwitchGpuContext(
    PCODECHAL_DECODE_SCALABILITY_STATE  pScalabilityState)
{
    PMOS_INTERFACE                  pOsInterface;
    MOS_GPU_CONTEXT                 GpuContext;
    uint32_t                        HcpDecPhase;
    bool                            bFESepSwitchContextFlag = false;
    MOS_STATUS                      eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_DECODE_FUNCTION_ENTER;

    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState->pHwInterface);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState->pHwInterface->GetOsInterface());

    pOsInterface = pScalabilityState->pHwInterface->GetOsInterface();
    HcpDecPhase  = pScalabilityState->HcpDecPhase;

    if (pScalabilityState->bFESeparateSubmission)
    {
        if (CodecHalDecodeScalability1stPhaseofSubmission(pScalabilityState))
        {
            bFESepSwitchContextFlag = true;
            if (pScalabilityState->bFESeparateSubmission && HcpDecPhase == CODECHAL_HCP_DECODE_PHASE_BE0)
            {
                GpuContext = pScalabilityState->VideoContext;
            }
            else
            {
                GpuContext = pScalabilityState->VideoContextForFE;
            }
        }
    }

#if (_DEBUG || _RELEASE_INTERNAL)
    if (CodecHalDecodeScalability1stDecPhase(pScalabilityState))
    {
        // report in-use
        MOS_USER_FEATURE_VALUE_WRITE_DATA UserFeatureWriteData;
        MOS_ZeroMemory(&UserFeatureWriteData, sizeof(UserFeatureWriteData));
        UserFeatureWriteData.Value.i32Data = pScalabilityState->bFESeparateSubmission;
        UserFeatureWriteData.ValueID = __MEDIA_USER_FEATURE_VALUE_SCALABILITY_FE_SEPARATE_SUBMISSION_IN_USE_ID;
        MOS_UserFeature_WriteValues_ID(nullptr, &UserFeatureWriteData, 1, pOsInterface->pOsContext);
    }
#endif

    if (bFESepSwitchContextFlag)
    {
        CODECHAL_DECODE_VERBOSEMESSAGE("Change Decode GPU Ctxt to %d.", GpuContext);

        // Switch GPU context
        CODECHAL_DECODE_CHK_STATUS_RETURN(pOsInterface->pfnSetGpuContext(pOsInterface, GpuContext));
        // Reset allocation list and house keeping
        pOsInterface->pfnResetOsStates(pOsInterface);
    }

    return eStatus;
}

MOS_STATUS CodecHalDecodeScalability_InitSemaMemResources(
    PCODECHAL_DECODE_SCALABILITY_STATE  pScalabilityState,
    PMOS_COMMAND_BUFFER                 pCmdBuffer)
{
    CodechalHwInterface      *pHwInterface;
    MhwMiInterface           *pMiInterface;
    MOS_STATUS               eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_DECODE_FUNCTION_ENTER;

    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState->pHwInterface);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState->pHwInterface->GetMiInterface());
    pHwInterface       = pScalabilityState->pHwInterface;
    pMiInterface = pHwInterface->GetMiInterface();

    MHW_MI_STORE_DATA_PARAMS StoreDataParams;
    MOS_ZeroMemory(&StoreDataParams, sizeof(StoreDataParams));

    if (!Mos_ResourceIsNull(&pScalabilityState->resSemaMemCompletion))
    {
        StoreDataParams.pOsResource       = &pScalabilityState->resSemaMemCompletion;
        StoreDataParams.dwResourceOffset  = 0;
        StoreDataParams.dwValue           = 0;
        CODECHAL_DECODE_CHK_STATUS_RETURN(pMiInterface->AddMiStoreDataImmCmd(
            pCmdBuffer,
            &StoreDataParams));
    }

    return eStatus;

}

MOS_STATUS CodecHalDecodeScalability_DecidePipeNum(
    PCODECHAL_DECODE_SCALABILITY_STATE         pScalState,
    PCODECHAL_DECODE_SCALABILITY_INIT_PARAMS   pInitParams)
{
    PMOS_VIRTUALENGINE_INTERFACE pVEInterface;
    MOS_STATUS                   eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_DECODE_FUNCTION_ENTER;

    CODECHAL_DECODE_CHK_NULL_RETURN(pScalState);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalState->pVEInterface);
    CODECHAL_DECODE_CHK_NULL_RETURN(pInitParams);

    pVEInterface = pScalState->pVEInterface;

    pScalState->ucScalablePipeNum = CODECHAL_DECODE_HCP_Legacy_PIPE_NUM_1;
    if (pInitParams->usingSFC)
    {
        //using SFC can only work in single pipe mode.
        return MOS_STATUS_SUCCESS;
    }

#if (_DEBUG || _RELEASE_INTERNAL)
    if (pScalState->bAlwaysFrameSplit)
    {
        if (pScalState->ucNumVdbox != 1)
        {
            if (pScalState->ucNumVdbox == 2)
            {
                pScalState->ucScalablePipeNum = CODECHAL_DECODE_HCP_SCALABLE_PIPE_NUM_2;
            }
            else
            {
                pScalState->ucScalablePipeNum = CODECHAL_DECODE_HCP_SCALABLE_PIPE_NUM_RESERVED;
            }
        }
    }
    else
#endif
    {
        if (pScalState->ucNumVdbox != 1)
        {
            if (pScalState->ucNumVdbox == 2)
            {
                if (pScalState->dwHcpDecModeSwtichTh1Width != 0)
                {
                    if (pInitParams->u32PicWidthInPixel >= pScalState->dwHcpDecModeSwtichTh1Width)
                    {
                        pScalState->ucScalablePipeNum = CODECHAL_DECODE_HCP_SCALABLE_PIPE_NUM_2;
                    }
                }
                else if ((!CodechalDecodeNonRextFormat(pInitParams->format)
                                && CodechalDecodeResolutionEqualLargerThan4k(pInitParams->u32PicWidthInPixel, pInitParams->u32PicHeightInPixel))
                            || (CodechalDecodeNonRextFormat(pInitParams->format)
                                && CodechalDecodeResolutionEqualLargerThan5k(pInitParams->u32PicWidthInPixel, pInitParams->u32PicHeightInPixel)))
                {
                    pScalState->ucScalablePipeNum = CODECHAL_DECODE_HCP_SCALABLE_PIPE_NUM_2;
                }

                if (pScalState->bIsEvenSplit == false)
                {
                    // disable scalability for clips with width less than split condition when MMC is on
                    if (pInitParams->u32PicWidthInPixel <= CODEC_SCALABILITY_FIRST_TILE_WIDTH_4K)
                    {
                        pScalState->ucScalablePipeNum = CODECHAL_DECODE_HCP_Legacy_PIPE_NUM_1;
                    }
                }
            }
            else
            {
                if (pScalState->dwHcpDecModeSwtichTh1Width != 0 &&
                    pScalState->dwHcpDecModeSwtichTh2Width != 0)
                {
                    if (pInitParams->u32PicWidthInPixel >= pScalState->dwHcpDecModeSwtichTh2Width)
                    {
                        pScalState->ucScalablePipeNum = CODECHAL_DECODE_HCP_SCALABLE_PIPE_NUM_RESERVED;
                    }
                    else if (pInitParams->u32PicWidthInPixel >= pScalState->dwHcpDecModeSwtichTh1Width)
                    {
                        pScalState->ucScalablePipeNum = CODECHAL_DECODE_HCP_SCALABLE_PIPE_NUM_2;
                    }
                }
                else
                {
                    if ((pInitParams->u32PicWidthInPixel * pInitParams->u32PicHeightInPixel) >= (CODECHAL_HCP_DECODE_SCALABLE_THRESHOLD4_WIDTH * CODECHAL_HCP_DECODE_SCALABLE_THRESHOLD4_HEIGHT))
                    {
                        pScalState->ucScalablePipeNum = CODECHAL_DECODE_HCP_SCALABLE_PIPE_NUM_RESERVED;
                    }
                    else if ((!CodechalDecodeNonRextFormat(pInitParams->format)
                                && CodechalDecodeResolutionEqualLargerThan4k(pInitParams->u32PicWidthInPixel, pInitParams->u32PicHeightInPixel))
                            || (CodechalDecodeNonRextFormat(pInitParams->format)
                                && CodechalDecodeResolutionEqualLargerThan5k(pInitParams->u32PicWidthInPixel, pInitParams->u32PicHeightInPixel)))
                    {
                        pScalState->ucScalablePipeNum = CODECHAL_DECODE_HCP_SCALABLE_PIPE_NUM_2;
                    }
                }
            }
        }
    }

    return eStatus;
}

MOS_STATUS CodechalDecodeScalability_MapPipeNumToLRCACount(
    PCODECHAL_DECODE_SCALABILITY_STATE   pScalState,
    uint32_t                             *LRCACount)
{
    MOS_STATUS    eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_DECODE_FUNCTION_ENTER;

    CODECHAL_DECODE_CHK_NULL_RETURN(pScalState);
    CODECHAL_DECODE_CHK_NULL_RETURN(LRCACount);

    *LRCACount = 1; // initialzed to 1.

    switch (pScalState->ucScalablePipeNum)
    {
    case CODECHAL_DECODE_HCP_SCALABLE_PIPE_NUM_RESERVED:
        *LRCACount = pScalState->bFESeparateSubmission ? 3 : 4;
        break;
    case CODECHAL_DECODE_HCP_SCALABLE_PIPE_NUM_2:
        // on GT2 or debug override enabled, FE separate submission = false, FE run on the same engine of BEs;
        // on GT3, FE separate submission = true, scalability submission includes only BEs.
        *LRCACount = 2;
        break;
    case CODECHAL_DECODE_HCP_Legacy_PIPE_NUM_1:
        *LRCACount = 1;
        break;
    default:
        CODECHAL_DECODE_ASSERTMESSAGE("invalid pipe number.")
            return MOS_STATUS_INVALID_PARAMETER;
    }

    if (*LRCACount > pScalState->ucNumVdbox)
    {
        CODECHAL_DECODE_ASSERTMESSAGE("LRCA count can not exceed vdbox number.");
        return MOS_STATUS_INVALID_PARAMETER;
    }

    return eStatus;
}

MOS_STATUS CodechalDecodeScalability_ChkGpuCtxReCreation(
    PCODECHAL_DECODE_SCALABILITY_STATE         pScalabilityState,
    PMOS_GPUCTX_CREATOPTIONS_ENHANCED          CurgpuCtxCreatOpts)
{
    PMOS_INTERFACE      pOsInterface;
    bool                changed = false;
    uint32_t            NewLRCACount = 0, PreLRCACount = 0;
    MOS_STATUS          eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_DECODE_FUNCTION_ENTER;

    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState);
    CODECHAL_DECODE_CHK_NULL_RETURN(CurgpuCtxCreatOpts);

    pOsInterface    = pScalabilityState->pHwInterface->GetOsInterface();

#if (_DEBUG || _RELEASE_INTERNAL)
    if (pOsInterface->bEnableDbgOvrdInVE)
    {
        changed = false;
    }
    else
#endif
    {
        CODECHAL_DECODE_CHK_STATUS_RETURN(pScalabilityState->pfnMapPipeNumToLRCACount(
            pScalabilityState,
            &NewLRCACount));

        if (CurgpuCtxCreatOpts->LRCACount != NewLRCACount)
        {
            changed = true;
            PreLRCACount = CurgpuCtxCreatOpts->LRCACount;
            CurgpuCtxCreatOpts->LRCACount = NewLRCACount;
        }
        else
        {
            changed = false;
        }
    }

    if (changed)
    {
         auto contextToCreate = MOS_GPU_CONTEXT_VIDEO;

         switch (NewLRCACount)
         {
         case 2:
             contextToCreate = pScalabilityState->VideoContextForMP;
             break;
         case 3:
             contextToCreate = pScalabilityState->VideoContextFor3P;
             break;
         default:
             contextToCreate = pScalabilityState->VideoContextForSP;
             break;
         }

         CODECHAL_DECODE_CHK_STATUS_RETURN(pOsInterface->pfnCreateGpuContext(
             pOsInterface,
             contextToCreate,
             MOS_GPU_NODE_VIDEO,
             CurgpuCtxCreatOpts));
         CODECHAL_DECODE_CHK_STATUS_RETURN(pOsInterface->pfnRegisterBBCompleteNotifyEvent(
             pOsInterface,
             contextToCreate));

        // Switch across single pipe/ scalable mode gpu contexts
        MOS_GPU_CONTEXT GpuContext = contextToCreate;
        CODECHAL_DECODE_VERBOSEMESSAGE("Change Decode GPU Ctxt to %d.", GpuContext);
        CODECHAL_DECODE_CHK_STATUS_RETURN(pOsInterface->pfnSetGpuContext(pOsInterface, GpuContext));
        // Reset allocation list and house keeping
        pOsInterface->pfnResetOsStates(pOsInterface);
        // Gpu context re-use
        pScalabilityState->VideoContext = GpuContext;
    }

    return eStatus;
}

#if (_DEBUG || _RELEASE_INTERNAL)
MOS_STATUS CodechalDecodeScalability_DebugOvrdDecidePipeNum(
    PCODECHAL_DECODE_SCALABILITY_STATE         pScalState)
{
    PMOS_VIRTUALENGINE_INTERFACE pVEInterface;
    MOS_STATUS                   eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_DECODE_CHK_NULL_RETURN(pScalState);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalState->pVEInterface);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalState->pHwInterface);

    pVEInterface = pScalState->pVEInterface;
    PMOS_INTERFACE pOsInterface = pScalState->pHwInterface->GetOsInterface();
    CODECHAL_DECODE_CHK_NULL_RETURN(pOsInterface);

    if (pOsInterface->apoMosEnabled)
    {
        CODECHAL_DECODE_CHK_NULL_RETURN(pVEInterface->veInterface);
        auto veInterface = pVEInterface->veInterface;
        if (veInterface->GetEngineCount() == 1)
        {
            pScalState->ucScalablePipeNum = CODECHAL_DECODE_HCP_Legacy_PIPE_NUM_1;
        }
        else if (veInterface->GetEngineCount() == 2)
        {
            //engine count = 2, only support FE run on the same engine as one of BE for now.
            pScalState->ucScalablePipeNum = CODECHAL_DECODE_HCP_SCALABLE_PIPE_NUM_2;
        }
        else if (veInterface->GetEngineCount() == 4 &&
                 veInterface->GetEngineLogicId(3) != veInterface->GetEngineLogicId(0) &&
                 veInterface->GetEngineLogicId(3) != veInterface->GetEngineLogicId(1) &&
                 veInterface->GetEngineLogicId(3) != veInterface->GetEngineLogicId(2))
        {
            pScalState->ucScalablePipeNum = CODECHAL_DECODE_HCP_SCALABLE_PIPE_NUM_RESERVED;
        }
        else
        {
            CODECHAL_DECODE_ASSERTMESSAGE("invalid parameter settings in debug override.");
            return MOS_STATUS_INVALID_PARAMETER;
        }

        return eStatus;
    }

    // debug override for virtual tile
    if (pVEInterface->ucEngineCount == 1)
    {
        pScalState->ucScalablePipeNum = CODECHAL_DECODE_HCP_Legacy_PIPE_NUM_1;
    }
    else if (pVEInterface->ucEngineCount == 2)
    {
        //engine count = 2, only support FE run on the same engine as one of BE for now.
        pScalState->ucScalablePipeNum = CODECHAL_DECODE_HCP_SCALABLE_PIPE_NUM_2;
    }
    else if (pVEInterface->ucEngineCount == 4 &&
        pVEInterface->EngineLogicId[3] != pVEInterface->EngineLogicId[0] &&
        pVEInterface->EngineLogicId[3] != pVEInterface->EngineLogicId[1] &&
        pVEInterface->EngineLogicId[3] != pVEInterface->EngineLogicId[2])
    {
        pScalState->ucScalablePipeNum = CODECHAL_DECODE_HCP_SCALABLE_PIPE_NUM_RESERVED;
    }
    else
    {
        CODECHAL_DECODE_ASSERTMESSAGE("invalid parameter settings in debug override.");
        return MOS_STATUS_INVALID_PARAMETER;
    }

    return eStatus;
}
#endif

MOS_STATUS CodechalDecodeScalability_ConstructParmsForGpuCtxCreation(
    PCODECHAL_DECODE_SCALABILITY_STATE         pScalState,
    PMOS_GPUCTX_CREATOPTIONS_ENHANCED          gpuCtxCreatOpts,
    CodechalSetting *                          codecHalSetting)
{
    PMOS_INTERFACE                           pOsInterface;
    CODECHAL_DECODE_SCALABILITY_INIT_PARAMS  initParams;
    MOS_STATUS                               eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_DECODE_FUNCTION_ENTER;

    CODECHAL_DECODE_CHK_NULL_RETURN(pScalState);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalState->pHwInterface);
    CODECHAL_DECODE_CHK_NULL_RETURN(gpuCtxCreatOpts);
    CODECHAL_DECODE_CHK_NULL_RETURN(codecHalSetting);
    bool sfcInUse = codecHalSetting->sfcInUseHinted && codecHalSetting->downsamplingHinted
                       && (MEDIA_IS_SKU(pScalState->pHwInterface->GetSkuTable(), FtrSFCPipe)
                       && !MEDIA_IS_SKU(pScalState->pHwInterface->GetSkuTable(), FtrDisableVDBox2SFC));
    pOsInterface    = pScalState->pHwInterface->GetOsInterface();
    MEDIA_FEATURE_TABLE *m_skuTable = pOsInterface->pfnGetSkuTable(pOsInterface);
#if (_DEBUG || _RELEASE_INTERNAL)
    if (pOsInterface->bEnableDbgOvrdInVE)
    {
        PMOS_VIRTUALENGINE_INTERFACE pVEInterface = pScalState->pVEInterface;
        CODECHAL_DECODE_CHK_NULL_RETURN(pVEInterface);
        gpuCtxCreatOpts->DebugOverride      = true;
        if (MEDIA_IS_SKU(m_skuTable, FtrSfcScalability))
        {
            gpuCtxCreatOpts->UsingSFC = false;// this param ignored when dbgoverride enabled
        }
        else
        {
            gpuCtxCreatOpts->UsingSFC = sfcInUse;  // this param ignored when dbgoverride enabled
        }
        CODECHAL_DECODE_CHK_STATUS_RETURN(pScalState->pfnDebugOvrdDecidePipeNum(pScalState));

        if (pOsInterface->apoMosEnabled)
        {
            CODECHAL_DECODE_CHK_NULL_RETURN(pVEInterface->veInterface);
            for (uint32_t i = 0; i < pVEInterface->veInterface->GetEngineCount(); i++)
            {
                gpuCtxCreatOpts->EngineInstance[i] = pVEInterface->veInterface->GetEngineLogicId(i);
            }
        }
        else
        {
            for (uint32_t i = 0; i < pVEInterface->ucEngineCount; i++)
            {
                gpuCtxCreatOpts->EngineInstance[i] = pVEInterface->EngineLogicId[i];
            }
        }
    }
    else
#endif
    {
        if (MEDIA_IS_SKU(m_skuTable, FtrSfcScalability))
        {
            gpuCtxCreatOpts->UsingSFC = false;
        }
        else
        {
            gpuCtxCreatOpts->UsingSFC = sfcInUse;
        }

        MOS_ZeroMemory(&initParams, sizeof(initParams));
        initParams.u32PicWidthInPixel   = MOS_ALIGN_CEIL(codecHalSetting->width, 8);
        initParams.u32PicHeightInPixel  = MOS_ALIGN_CEIL(codecHalSetting->height, 8);
        if (((codecHalSetting->standard == CODECHAL_VP9) || (codecHalSetting->standard == CODECHAL_HEVC))
                && (codecHalSetting->chromaFormat == HCP_CHROMA_FORMAT_YUV420))
        {
            initParams.format = Format_NV12;
            if (codecHalSetting->lumaChromaDepth == CODECHAL_LUMA_CHROMA_DEPTH_10_BITS)
            {
                initParams.format = Format_P010;
            }
        }
        initParams.usingSFC             = sfcInUse;
        initParams.usingSecureDecode    = codecHalSetting->secureMode;
        CODECHAL_DECODE_CHK_STATUS_RETURN(pScalState->pfnDecidePipeNum(
            pScalState,
            &initParams));
    }

    CODECHAL_DECODE_CHK_STATUS_RETURN(pScalState->pfnMapPipeNumToLRCACount(
        pScalState,
        &gpuCtxCreatOpts->LRCACount));

    return eStatus;
}

MOS_STATUS CodecHalDecodeScalability_InitScalableParams(
    PCODECHAL_DECODE_SCALABILITY_STATE         pScalabilityState,
    PCODECHAL_DECODE_SCALABILITY_INIT_PARAMS   pInitParams,
    uint16_t                                   *pucDecPassNum)
{
    PMOS_INTERFACE                  pOsInterface;
    PMOS_VIRTUALENGINE_INTERFACE    pVEInterface;
    MOS_STATUS                      eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_DECODE_FUNCTION_ENTER;

    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState->pHwInterface);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState->pHwInterface->GetOsInterface());

    pOsInterface    = pScalabilityState->pHwInterface->GetOsInterface();
    pVEInterface    = pScalabilityState->pVEInterface;

    if (!pOsInterface->bSupportVirtualEngine)
    {
        eStatus = MOS_STATUS_INVALID_PARAMETER;
        CODECHAL_DECODE_ASSERTMESSAGE("Scalability decode must run with virtual engine interface.\n");
        return eStatus;
    }

    pScalabilityState->bScalableDecodeMode = false; // initialized to false

#if (_DEBUG || _RELEASE_INTERNAL)
    if (pOsInterface->bEnableDbgOvrdInVE)
    {
        if (!MOS_VE_CTXBASEDSCHEDULING_SUPPORTED(pOsInterface))
        {
            if (pOsInterface->apoMosEnabled)
            {
                CODECHAL_DECODE_CHK_NULL_RETURN(pVEInterface->veInterface);
                pScalabilityState->ucScalablePipeNum = pVEInterface->veInterface->GetEngineCount() - 1;
            }
            else
            {
                pScalabilityState->ucScalablePipeNum = pVEInterface->ucEngineCount - 1;
            }
            pScalabilityState->bScalableDecodeMode = true;
        }
        else
        {
            // do nothing since pipe number already decided at the gpu context creation.
        }
    }
    else
#endif
    {
        // Decide pipe number
        CODECHAL_DECODE_CHK_STATUS_RETURN(pScalabilityState->pfnDecidePipeNum(pScalabilityState, pInitParams));
    }

    // Decide scalable mode or single pipe mode
    if (pScalabilityState->ucScalablePipeNum > 1 && pOsInterface->frameSplit)
    {
        pScalabilityState->bScalableDecodeMode = true;
    }

    CODECHAL_DECODE_CHK_NULL_RETURN(pucDecPassNum);
    // Decide Decode pass number - pucDecPassNum
    if (pScalabilityState->bScalableDecodeMode)
    {
        *pucDecPassNum = pScalabilityState->ucScalablePipeNum + 1; // FE + all BEs
    }
    else
    {
        *pucDecPassNum = 1;
    }

    // Add one pass for S2L conversion in short format.
    if (pScalabilityState->bShortFormatInUse)
    {
        *pucDecPassNum = *pucDecPassNum + 1;
    }

    pScalabilityState->VideoContext = pInitParams->gpuCtxInUse;

    return eStatus;
}

MOS_STATUS CodecHalDecodeScalability_SetHintParams(
    PCODECHAL_DECODE_SCALABILITY_STATE         pScalabilityState,
    PCODECHAL_DECODE_SCALABILITY_SETHINT_PARMS pSetHintParms)
{
    PMOS_VIRTUALENGINE_INTERFACE    pVEInterface;
    MOS_VIRTUALENGINE_SET_PARAMS    VEParams;
    PMOS_INTERFACE                  pOsInterface;
    MOS_STATUS                      eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_DECODE_FUNCTION_ENTER;

    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState);
    CODECHAL_DECODE_CHK_NULL_RETURN(pSetHintParms);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState->pHwInterface);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState->pHwInterface->GetOsInterface());

    pOsInterface    = pScalabilityState->pHwInterface->GetOsInterface();
    pVEInterface    = pScalabilityState->pVEInterface;

    MOS_ZeroMemory(&VEParams, sizeof(VEParams));
    if(!MOS_VE_CTXBASEDSCHEDULING_SUPPORTED(pOsInterface))
    {
        //not used by VE2.0
        VEParams.bNeedSyncWithPrevious       = pSetHintParms->bNeedSyncWithPrevious;
        VEParams.bSameEngineAsLastSubmission = pSetHintParms->bSameEngineAsLastSubmission;
        VEParams.bSFCInUse                   = pSetHintParms->bSFCInUse;
    }

    VEParams.ucScalablePipeNum  = pScalabilityState->ucScalablePipeNum;

    if (pScalabilityState->bScalableDecodeMode)
    {
        if (pScalabilityState->bFESeparateSubmission)
        {
            //set Hint parameter for FE submission
            VEParams.bScalableMode = false;
            if (pVEInterface->pfnVESetHintParams)
            {
                CODECHAL_DECODE_CHK_STATUS_RETURN(pVEInterface->pfnVESetHintParams(pVEInterface, &VEParams));
            }
        }

        VEParams.bScalableMode = true;
        VEParams.bHaveFrontEndCmds = (pScalabilityState->bFESeparateSubmission ? false : true);
        if (pVEInterface->pfnVESetHintParams)
        {
            CODECHAL_DECODE_CHK_STATUS_RETURN(pVEInterface->pfnVESetHintParams(pVEInterface, &VEParams));
        }
    }
    else
    {
        VEParams.bScalableMode = false;
        if (pVEInterface->pfnVESetHintParams)
        {
            CODECHAL_DECODE_CHK_STATUS_RETURN(pVEInterface->pfnVESetHintParams(pVEInterface, &VEParams));
        }
    }

    return eStatus;
}

#if (_DEBUG || _RELEASE_INTERNAL)
MOS_STATUS CodecHalDecodeScalability_DbgDumpCmdBuffer(
    CodechalDecode                      *pDecoder,
    PCODECHAL_DECODE_SCALABILITY_STATE  pScalabilityState,
    CodechalDebugInterface              *debugInterface,
    PMOS_COMMAND_BUFFER                 pPrimCmdBuf)
{
    MOS_STATUS                      eStatus = MOS_STATUS_SUCCESS;
    MOS_COMMAND_BUFFER              ScdryCmdBuffer;
    PMOS_COMMAND_BUFFER             pCmdBufferInUse;

    CODECHAL_DECODE_FUNCTION_ENTER;

    CODECHAL_DECODE_CHK_NULL_RETURN(pDecoder);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState);
    CODECHAL_DECODE_CHK_NULL_RETURN(pPrimCmdBuf);

    if (!pScalabilityState->bScalableDecodeMode)
    {
        eStatus = MOS_STATUS_INVALID_PARAMETER;
        CODECHAL_DECODE_ASSERTMESSAGE("Invalid decode parameters!");
        return eStatus;
    }

    if (CodecHalDecodeScalabilityIsFESeparateSubmission(pScalabilityState)
        && CodecHalDecodeScalabilityIsFEPhase(pScalabilityState))
    {
        pCmdBufferInUse = pPrimCmdBuf;
    }
    else
    {
        CODECHAL_DECODE_CHK_STATUS_RETURN(CodecHalDecodeScalability_GetVESecondaryCmdBuffer(pScalabilityState, &ScdryCmdBuffer));
        pCmdBufferInUse = &ScdryCmdBuffer;
    }

    CODECHAL_DECODE_CHK_STATUS_RETURN(debugInterface->DumpCmdBuffer(
        pCmdBufferInUse,
        CODECHAL_NUM_MEDIA_STATES,
        "_DEC"));

    return eStatus;
}
#endif

MOS_STATUS CodecHalDecodeScalability_PopulateHintParams(
    PCODECHAL_DECODE_SCALABILITY_STATE  pScalabilityState,
    PMOS_COMMAND_BUFFER                 pPrimCmdBuf)
{
    MOS_STATUS                      eStatus = MOS_STATUS_SUCCESS;
    PMOS_CMD_BUF_ATTRI_VE          pAttriVe;

    CODECHAL_DECODE_FUNCTION_ENTER;

    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState);
    CODECHAL_DECODE_CHK_NULL_RETURN(pPrimCmdBuf);
    pAttriVe = (PMOS_CMD_BUF_ATTRI_VE)(pPrimCmdBuf->Attributes.pAttriVe);

    if (pAttriVe)
    {
        if ((CodecHalDecodeScalabilityIsScalableMode(pScalabilityState) &&
             !CodecHalDecodeScalabilityIsFESeparateSubmission(pScalabilityState)) ||
            (CodecHalDecodeScalabilityIsFESeparateSubmission(pScalabilityState) &&
             CodecHalDecodeScalabilityIsBEPhase(pScalabilityState)))
        {
            CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState->pScalHintParms);
            pAttriVe->VEngineHintParams = *(pScalabilityState->pScalHintParms);
        }
        else
        {
            CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState->pSingleHintParms);
            pAttriVe->VEngineHintParams = *(pScalabilityState->pSingleHintParms);
        }

        pAttriVe->bUseVirtualEngineHint = true;
    }

    return eStatus;
}

MOS_STATUS CodecHalDecodeScalability_SignalFE2BESemaphore(
    PCODECHAL_DECODE_SCALABILITY_STATE  pScalabilityState,
    PMOS_COMMAND_BUFFER                 pCmdBufferInUse)
{
    PMOS_INTERFACE                      pOsInterface;
    MhwMiInterface                      *pMiInterface;
    MOS_SYNC_PARAMS                     SyncParams;
    MOS_STATUS                          eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_DECODE_FUNCTION_ENTER;

    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState->pHwInterface);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState->pHwInterface->GetOsInterface());
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState->pHwInterface->GetMiInterface());
    CODECHAL_DECODE_CHK_NULL_RETURN(pCmdBufferInUse);

    pOsInterface  = pScalabilityState->pHwInterface->GetOsInterface();
    pMiInterface  = pScalabilityState->pHwInterface->GetMiInterface();

    // FE semaphore to BE0 for FE/BE Sync.
    if (pScalabilityState->bScalableDecodeMode && pScalabilityState->ucNumVdbox > 2)
    {
        // When FE separate submission enabled, use SW semaphore between FE/BE0. Otherwise use HW semaphore
        if (pScalabilityState->bFESeparateSubmission)
        {
            SyncParams = g_cInitSyncParams;
            SyncParams.GpuContext = pScalabilityState->VideoContextForFE;
            SyncParams.presSyncResource = &pScalabilityState->resFeBeSyncObject;
            CODECHAL_DECODE_CHK_STATUS_RETURN(pOsInterface->pfnEngineSignal(pOsInterface, &SyncParams));
        }
        else
        {
            //post HW semaphore (FE-BE) after FE completion , mi atomic increase 1
            CODECHAL_DECODE_CHK_STATUS_RETURN(pScalabilityState->pHwInterface->SendMiAtomicDwordCmd(&pScalabilityState->resSemaMemFEBE, 1, MHW_MI_ATOMIC_INC, pCmdBufferInUse));
        }
    }

    return eStatus;
}

MOS_STATUS CodecHalDecodeScalability_FEBESync(
    PCODECHAL_DECODE_SCALABILITY_STATE  pScalabilityState,
    PMOS_COMMAND_BUFFER                 pCmdBufferInUse)
{
    PMOS_INTERFACE                      pOsInterface;
    MhwMiInterface                      *pMiInterface;
    uint32_t                            HcpDecPhase;
    MOS_STATUS                          eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_DECODE_FUNCTION_ENTER;

    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState->pHwInterface);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState->pHwInterface->GetOsInterface());
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState->pHwInterface->GetMiInterface());
    CODECHAL_DECODE_CHK_NULL_RETURN(pCmdBufferInUse);

    pOsInterface        = pScalabilityState->pHwInterface->GetOsInterface();
    pMiInterface        = pScalabilityState->pHwInterface->GetMiInterface();
    HcpDecPhase         = pScalabilityState->HcpDecPhase;

    //FE& BE0 Sync.
    if (HcpDecPhase == CODECHAL_HCP_DECODE_PHASE_BE0 && pScalabilityState->pHwInterface->GetMfxInterface()->GetNumVdbox() > 2)
    {
        if (pScalabilityState->bFESeparateSubmission)
        {
            MOS_SYNC_PARAMS SyncParams;

            SyncParams = g_cInitSyncParams;
            SyncParams.GpuContext = pScalabilityState->VideoContext;
            SyncParams.presSyncResource = &pScalabilityState->resFeBeSyncObject;

            CODECHAL_DECODE_CHK_STATUS_RETURN(pOsInterface->pfnEngineWait(pOsInterface, &SyncParams));
        }
        else
        {
            pMiInterface->AddWatchdogTimerStopCmd(pCmdBufferInUse);

            CODECHAL_DECODE_CHK_STATUS_RETURN(pScalabilityState->pHwInterface->SendHwSemaphoreWaitCmd(&pScalabilityState->resSemaMemFEBE, 1, MHW_MI_SAD_EQUAL_SDD, pCmdBufferInUse));
            //reset semaphore. mi atomic decrease 1
            CODECHAL_DECODE_CHK_STATUS_RETURN(pScalabilityState->pHwInterface->SendMiAtomicDwordCmd(&pScalabilityState->resSemaMemFEBE, 1, MHW_MI_ATOMIC_DEC, pCmdBufferInUse));
        }
    }

    if (CodecHalDecodeScalabilityIsBEPhase(pScalabilityState))
    {
        // Stop Watchdog before BEs wait
        pMiInterface->AddWatchdogTimerStopCmd(pCmdBufferInUse);

        //HW Semaphore for BEs Starting at the same time
        CODECHAL_DECODE_CHK_STATUS_RETURN(pScalabilityState->pHwInterface->SendMiAtomicDwordCmd(&pScalabilityState->resSemaMemBEs, 1, MHW_MI_ATOMIC_INC, pCmdBufferInUse));
        CODECHAL_DECODE_CHK_STATUS_RETURN(pScalabilityState->pHwInterface->SendHwSemaphoreWaitCmd(
            &pScalabilityState->resSemaMemBEs,
            pScalabilityState->ucScalablePipeNum,
            MHW_MI_SAD_EQUAL_SDD,
            pCmdBufferInUse));

        // Program some placeholder cmds to resolve the hazard between BEs sync
        MHW_MI_STORE_DATA_PARAMS dataParams;
        dataParams.pOsResource = &pScalabilityState->resDelayMinus;
        dataParams.dwResourceOffset = 0;
        dataParams.dwValue = 0xDE1A;
        for (uint32_t i = 0; i < pScalabilityState->numDelay; i++)
        {
            CODECHAL_DECODE_CHK_STATUS_RETURN(pScalabilityState->pHwInterface->GetMiInterface()->AddMiStoreDataImmCmd(
                pCmdBufferInUse,
                &dataParams));
        }

        //reset HW semaphore
        CODECHAL_DECODE_CHK_STATUS_RETURN(pScalabilityState->pHwInterface->SendMiAtomicDwordCmd(&pScalabilityState->resSemaMemBEs, 1, MHW_MI_ATOMIC_DEC, pCmdBufferInUse));

        // Condidtional BB END for streamout buffer writing over allocated size
        CODECHAL_DECODE_CHK_STATUS_RETURN(pScalabilityState->pHwInterface->SendCondBbEndCmd(
            &pScalabilityState->resFEStatusBuffer,
            CODECHAL_OFFSETOF(CODECHAL_DECODE_SCALABILITY_FE_STATUS, dwCarryFlagOfReportedSizeMinusAllocSize),
            0,
            true,
            pCmdBufferInUse));

    }

    return eStatus;
}

MOS_STATUS CodecHalDecodeScalability_BEsCompletionSync(
    PCODECHAL_DECODE_SCALABILITY_STATE  pScalabilityState,
    PMOS_COMMAND_BUFFER                 pCmdBufferInUse)
{
    MhwMiInterface             *pMiInterface;
    uint32_t                   HcpDecPhase;
    MOS_STATUS                 eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_DECODE_FUNCTION_ENTER;

    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState->pHwInterface);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState->pHwInterface->GetMiInterface());
    CODECHAL_DECODE_CHK_NULL_RETURN(pCmdBufferInUse);
    HcpDecPhase        = pScalabilityState->HcpDecPhase;

    if (CodecHalDecodeScalabilityIsLastCompletePhase(pScalabilityState))
    {
        CODECHAL_DECODE_CHK_STATUS_RETURN(pScalabilityState->pHwInterface->SendHwSemaphoreWaitCmd(
            &pScalabilityState->resSemaMemCompletion,
            pScalabilityState->ucScalablePipeNum - 1,
            MHW_MI_SAD_EQUAL_SDD,
            pCmdBufferInUse));

        for (int i = 0; i < pScalabilityState->ucScalablePipeNum - 1; i++)
        {
            CODECHAL_DECODE_CHK_STATUS_RETURN(pScalabilityState->pHwInterface->SendMiAtomicDwordCmd(
                &pScalabilityState->resSemaMemCompletion,
                1,
                MHW_MI_ATOMIC_DEC,
                pCmdBufferInUse));
        }
    }
    else
    {
        CODECHAL_DECODE_CHK_STATUS_RETURN(pScalabilityState->pHwInterface->SendMiAtomicDwordCmd(
            &pScalabilityState->resSemaMemCompletion,
            1,
            MHW_MI_ATOMIC_INC,
            pCmdBufferInUse));
    }

    return eStatus;
}

MOS_STATUS CodecHalDecodeScalability_ReadCSEngineIDReg(
    PCODECHAL_DECODE_SCALABILITY_STATE pScalabilityState,
    CodechalDecodeStatusBuffer        *pDecodeStatusBuf,
    PMOS_COMMAND_BUFFER                pCmdBufferInUse)
{
    MHW_MI_STORE_REGISTER_MEM_PARAMS  StoreRegParams;
    MhwMiInterface                    *pMiInterface;
    MmioRegistersHcp                  *pMmioRegisters;
    uint8_t                           ucPhaseIndex = 0;
    uint32_t                          dwOffset = 0;
    uint32_t                          dwCurrIndex = 0;
    uint32_t                          dwPreIndex = 0;
    MOS_STATUS                        eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_DECODE_FUNCTION_ENTER;

    CODECHAL_DECODE_CHK_NULL_RETURN(pDecodeStatusBuf);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState->pHwInterface);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState->pHwInterface->GetMiInterface());

    pMiInterface = pScalabilityState->pHwInterface->GetMiInterface();
    pMmioRegisters = pScalabilityState->pHwInterface->GetHcpInterface()->GetMmioRegisters(MHW_VDBOX_NODE_1); //relative mmio addressing.

    ucPhaseIndex = (pScalabilityState->HcpDecPhase < CODECHAL_HCP_DECODE_PHASE_FE) ?
        0 : (pScalabilityState->HcpDecPhase - CODECHAL_HCP_DECODE_PHASE_FE);

    if (ucPhaseIndex >= CODECHAL_HCP_DECODE_SCALABLE_MAX_PHASE_NUM)
    {
        eStatus = MOS_STATUS_INVALID_PARAMETER;
        CODECHAL_DECODE_ASSERTMESSAGE("Invalid HCP decode phase!");
        return eStatus;
    }

    if (CodecHalDecodeScalabilityIsScalableMode(pScalabilityState) &&
        pScalabilityState->HcpDecPhase > CODECHAL_HCP_DECODE_PHASE_BE0)
    {
        if (pDecodeStatusBuf->m_currIndex == 0)
        {
            dwPreIndex = CODECHAL_DECODE_STATUS_NUM - 1;
        }
        else
        {
            dwPreIndex = pDecodeStatusBuf->m_currIndex - 1;
        }
    }

    dwCurrIndex = (CodecHalDecodeScalabilityIsScalableMode(pScalabilityState) &&
        pScalabilityState->HcpDecPhase > CODECHAL_HCP_DECODE_PHASE_BE0) ?
        dwPreIndex : pDecodeStatusBuf->m_currIndex;

    dwOffset = (dwCurrIndex * sizeof(CodechalDecodeStatus)) +
        pDecodeStatusBuf->m_csEngineIdOffset + sizeof(uint32_t)* ucPhaseIndex +
        sizeof(uint32_t)* 2;

    StoreRegParams.presStoreBuffer  = &pDecodeStatusBuf->m_statusBuffer;
    StoreRegParams.dwOffset         = dwOffset;
    StoreRegParams.dwRegister       = pMmioRegisters->csEngineIdOffset;
    CODECHAL_DECODE_CHK_STATUS_RETURN(pMiInterface->AddMiStoreRegisterMemCmd(pCmdBufferInUse, &StoreRegParams));

    return eStatus;
}

MOS_STATUS IsHevcBufferReallocNeeded(
    CodechalHwInterface                  *hwInterface,
    MHW_VDBOX_HCP_INTERNAL_BUFFER_TYPE   bufferType,
    PMHW_VDBOX_HCP_BUFFER_REALLOC_PARAMS reallocParam)
{
    return hwInterface->GetHcpInterface()->IsHevcBufferReallocNeeded(bufferType, reallocParam);
}

MOS_STATUS GetHevcBufferSize(
    CodechalHwInterface                 *hwInterface,
    MHW_VDBOX_HCP_INTERNAL_BUFFER_TYPE  bufferType,
    PMHW_VDBOX_HCP_BUFFER_SIZE_PARAMS   hcpBufSizeParam)
{
    return hwInterface->GetHcpInterface()->GetHevcBufferSize(bufferType, hcpBufSizeParam);
}

MOS_STATUS IsVp9BufferReallocNeeded(
    CodechalHwInterface                  *hwInterface,
    MHW_VDBOX_HCP_INTERNAL_BUFFER_TYPE   bufferType,
    PMHW_VDBOX_HCP_BUFFER_REALLOC_PARAMS reallocParam)
{
    return hwInterface->GetHcpInterface()->IsVp9BufferReallocNeeded(bufferType, reallocParam);
}

MOS_STATUS GetVp9BufferSize(
    CodechalHwInterface                 *hwInterface,
    MHW_VDBOX_HCP_INTERNAL_BUFFER_TYPE  bufferType,
    PMHW_VDBOX_HCP_BUFFER_SIZE_PARAMS   hcpBufSizeParam)
{
    return hwInterface->GetHcpInterface()->GetVp9BufferSize(bufferType, hcpBufSizeParam);
}

MOS_STATUS CodecHalDecodeScalability_InitializeState (
    CodechalDecode                     *pDecoder,
    PCODECHAL_DECODE_SCALABILITY_STATE  pScalabilityState,
    CodechalHwInterface                *hwInterface,
    bool                                bShortFormat)
{
    PMOS_VIRTUALENGINE_INTERFACE   pVEInterface;
    MOS_VIRTUALENGINE_INIT_PARAMS  VEInitParms;
    MOS_USER_FEATURE_VALUE_DATA    UserFeatureData;
    PMOS_INTERFACE                 osInterface;
    MhwVdboxMfxInterface           *vdboxMfxInterface;
    uint8_t                        vdboxNum;
    MOS_STATUS                     eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_DECODE_FUNCTION_ENTER;

    CODECHAL_DECODE_CHK_NULL_RETURN(pDecoder);
    CODECHAL_DECODE_CHK_NULL_RETURN(pScalabilityState);
    CODECHAL_DECODE_CHK_NULL_RETURN(hwInterface);
    osInterface = hwInterface->GetOsInterface();
    CODECHAL_DECODE_CHK_NULL_RETURN(osInterface);

    vdboxMfxInterface = hwInterface->GetMfxInterface();
    CODECHAL_DECODE_CHK_NULL_RETURN(vdboxMfxInterface);
    vdboxNum = vdboxMfxInterface->GetNumVdbox();

    if (vdboxNum < 2
#if (_DEBUG || _RELEASE_INTERNAL)
        || !osInterface->bHcpDecScalabilityMode
#endif
    )
   {
        eStatus = MOS_STATUS_INVALID_PARAMETER;
        CODECHAL_DECODE_ASSERTMESSAGE("not support scalability on this platform.");
        return eStatus;
    }

    pScalabilityState->VideoContextForSP = MOS_GPU_CONTEXT_VIDEO;
    pScalabilityState->VideoContextForMP = MOS_VE_MULTINODESCALING_SUPPORTED(osInterface) ? MOS_GPU_CONTEXT_VIDEO5 : MOS_GPU_CONTEXT_VDBOX2_VIDEO;
    pScalabilityState->VideoContextFor3P = MOS_VE_MULTINODESCALING_SUPPORTED(osInterface) ? MOS_GPU_CONTEXT_VIDEO7 : MOS_GPU_CONTEXT_VDBOX2_VIDEO2;

    pScalabilityState->numDelay = 15;

#if (_DEBUG || _RELEASE_INTERNAL)
    // Reg key of the threshold for mode switch single pipe <-> 2 pipe. Using pic width value to control mode switch for now
    MOS_ZeroMemory(&UserFeatureData, sizeof(UserFeatureData));
    MOS_UserFeature_ReadValue_ID(
        nullptr,
        __MEDIA_USER_FEATURE_VALUE_HCP_DECODE_MODE_SWITCH_THRESHOLD1_ID,
        &UserFeatureData,
        osInterface->pOsContext);
    pScalabilityState->dwHcpDecModeSwtichTh1Width = UserFeatureData.u32Data;

    MOS_ZeroMemory(&UserFeatureData, sizeof(UserFeatureData));
    MOS_UserFeature_ReadValue_ID(
        nullptr,
        __MEDIA_USER_FEATURE_VALUE_HCP_DECODE_MODE_SWITCH_THRESHOLD2_ID,
        &UserFeatureData,
        osInterface->pOsContext);
    pScalabilityState->dwHcpDecModeSwtichTh2Width = UserFeatureData.u32Data;

    MOS_ZeroMemory(&UserFeatureData, sizeof(UserFeatureData));
    MOS_UserFeature_ReadValue_ID(
        nullptr,
        __MEDIA_USER_FEATURE_VALUE_SCALABILITY_OVERRIDE_SPLIT_WIDTH_IN_MINCB,
        &UserFeatureData,
        osInterface->pOsContext);
    pScalabilityState->dbgOvrdWidthInMinCb = UserFeatureData.u32Data;
#endif

    // enable FE separate submission by default in multi-pipe mode
    if (hwInterface->GetMfxInterface()->GetNumVdbox() > 2)
    {
        pScalabilityState->bFESeparateSubmission = true;
    }
    else
    {
        // no benefit to enable FE separate submission on 2 vdbox config.
        pScalabilityState->bFESeparateSubmission = false;
    }

#if (_DEBUG || _RELEASE_INTERNAL)
    if (osInterface->bEnableDbgOvrdInVE || Mos_Solo_IsInUse(osInterface))
    {
        //if DbgOverride is enabled, FE separate submission is not supported
        pScalabilityState->bFESeparateSubmission = false;
    }
#endif

    if (pScalabilityState->bFESeparateSubmission)
    {
        MOS_GPU_CONTEXT         GpuContext = MOS_VE_CTXBASEDSCHEDULING_SUPPORTED(osInterface) ? MOS_GPU_CONTEXT_VIDEO : MOS_GPU_CONTEXT_VIDEO4;
        GpuContext = MOS_VE_MULTINODESCALING_SUPPORTED(osInterface) ? MOS_GPU_CONTEXT_VIDEO4 : GpuContext;

        MHW_VDBOX_GPUNODE_LIMIT gpuNodeLimit;
        CODECHAL_DECODE_CHK_STATUS_RETURN(vdboxMfxInterface->FindGpuNodeToUse(
            &gpuNodeLimit));
        MOS_GPU_NODE videoGpuNode = (MOS_GPU_NODE)(gpuNodeLimit.dwGpuNodeToUse);

        MOS_GPUCTX_CREATOPTIONS createOpts;
        CODECHAL_DECODE_CHK_STATUS_RETURN(osInterface->pfnCreateGpuContext(
            osInterface,
            GpuContext,
            videoGpuNode,
            &createOpts));
        pScalabilityState->VideoContextForFE = GpuContext;
    }

    pScalabilityState->Standard              = pDecoder->GetStandard();
    pScalabilityState->VideoContext          = pDecoder->GetVideoContext();
    pScalabilityState->bShortFormatInUse     = bShortFormat;
    pScalabilityState->ucNumVdbox            = vdboxNum;
    pScalabilityState->pHwInterface          = hwInterface;

    //virtual engine init with scalability
    MOS_ZeroMemory(&VEInitParms, sizeof(VEInitParms));
    VEInitParms.bScalabilitySupported     = true;
    VEInitParms.bFESeparateSubmit         = pScalabilityState->bFESeparateSubmission;
    VEInitParms.ucMaxNumPipesInUse        = (vdboxNum == 2) ? 2 : 3;
    VEInitParms.ucNumOfSdryCmdBufSets     = CODECHAL_SCALABILITY_DECODE_SECONDARY_CMDBUFSET_NUM;
    VEInitParms.ucMaxNumOfSdryCmdBufInOneFrame = (pScalabilityState->bFESeparateSubmission) ? VEInitParms.ucMaxNumPipesInUse : (VEInitParms.ucMaxNumPipesInUse + 1);
    CODECHAL_DECODE_CHK_STATUS_RETURN(Mos_VirtualEngineInterface_Initialize(osInterface, &VEInitParms));
    pScalabilityState->pVEInterface = pVEInterface = osInterface->pVEInterf;

    if (pVEInterface->pfnVEGetHintParams)
    {
        CODECHAL_DECODE_CHK_STATUS_RETURN(pVEInterface->pfnVEGetHintParams(pVEInterface, true, &pScalabilityState->pScalHintParms));
    }
    if (pVEInterface->pfnVEGetHintParams)
    {
        CODECHAL_DECODE_CHK_STATUS_RETURN(pVEInterface->pfnVEGetHintParams(pVEInterface, false, &pScalabilityState->pSingleHintParms));
    }

#if (_DEBUG || _RELEASE_INTERNAL)
    MOS_ZeroMemory(&UserFeatureData, sizeof(UserFeatureData));
    MOS_UserFeature_ReadValue_ID(
        nullptr,
        __MEDIA_USER_FEATURE_VALUE_HCP_DECODE_ALWAYS_FRAME_SPLIT_ID,
        &UserFeatureData,
        osInterface->pOsContext);
    pScalabilityState->bAlwaysFrameSplit = UserFeatureData.u32Data ? true : false;
#endif

    pScalabilityState->bIsEvenSplit = true;

    if (pDecoder->GetStandard() == CODECHAL_HEVC)
    {
        pScalabilityState->pfnGetHcpBufferSize         = GetHevcBufferSize;
        pScalabilityState->pfnIsHcpBufferReallocNeeded = IsHevcBufferReallocNeeded;
    }
    else if (pDecoder->GetStandard() == CODECHAL_VP9)
    {
        pScalabilityState->pfnGetHcpBufferSize         = GetVp9BufferSize;
        pScalabilityState->pfnIsHcpBufferReallocNeeded = IsVp9BufferReallocNeeded;
    }
    else
    {
        eStatus = MOS_STATUS_INVALID_PARAMETER;
        CODECHAL_DECODE_ASSERTMESSAGE("unsupported decode format for scalability mode.");
        return eStatus;
    }

    pScalabilityState->bToggleCABACStreamOutBuffer = false;
    pScalabilityState->sliceStateCLs = CODECHAL_SCALABILITY_SLICE_STATE_CACHELINES_PER_SLICE;
    pScalabilityState->pfnDecidePipeNum = CodecHalDecodeScalability_DecidePipeNum;
    pScalabilityState->pfnMapPipeNumToLRCACount = CodechalDecodeScalability_MapPipeNumToLRCACount;
#if (_DEBUG || _RELEASE_INTERNAL)
    pScalabilityState->pfnDebugOvrdDecidePipeNum = CodechalDecodeScalability_DebugOvrdDecidePipeNum;
#endif

    CODECHAL_DECODE_CHK_STATUS_RETURN(CodecHalDecodeScalability_AllocateResources_FixedSizes(pScalabilityState));

    return eStatus;
}

bool CodecHalDecodeScalabilityIsToSubmitCmdBuffer(
    PCODECHAL_DECODE_SCALABILITY_STATE pScalabilityState)
{
    if (pScalabilityState == nullptr)
    {
        return false;
    }
    else
    {
        return (CodecHalDecodeScalabilityIsFinalBEPhase(pScalabilityState) ||
            (pScalabilityState->HcpDecPhase == CODECHAL_HCP_DECODE_PHASE_FE && pScalabilityState->bFESeparateSubmission));
    }
}

void CodecHalDecodeScalability_DecPhaseToSubmissionType(
    PCODECHAL_DECODE_SCALABILITY_STATE pScalabilityState,
    PMOS_COMMAND_BUFFER pCmdBuffer)
{
    switch (pScalabilityState->HcpDecPhase)
    {
        case CodechalDecode::CodechalHcpDecodePhaseLegacyS2L:
            //Note: no break here, S2L and FE commands put in one secondary command buffer.
        case CODECHAL_HCP_DECODE_PHASE_FE:
            pCmdBuffer->iSubmissionType = SUBMISSION_TYPE_MULTI_PIPE_ALONE;
            break;
        case CODECHAL_HCP_DECODE_PHASE_BE0:
            pCmdBuffer->iSubmissionType = SUBMISSION_TYPE_MULTI_PIPE_MASTER;
            break;
        case CODECHAL_HCP_DECODE_PHASE_BE1:
            pCmdBuffer->iSubmissionType = SUBMISSION_TYPE_MULTI_PIPE_SLAVE | SUBMISSION_TYPE_MULTI_PIPE_FLAGS_LAST_PIPE;
            break;
        case CODECHAL_HCP_DECODE_PHASE_RESERVED:
        default:
            pCmdBuffer->iSubmissionType = SUBMISSION_TYPE_MULTI_PIPE_ALONE;
            break;
    }
}