/*
* Copyright (c) 2017-2018, 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_huc_cmd_initializer.cpp
//! \brief    Defines base class for command initializer encoder.
//!

#include "codec_def_encode_hevc.h"
#if defined (_HEVC_ENCODE_VME_SUPPORTED) || defined (_HEVC_ENCODE_VDENC_SUPPORTED)
#include "codechal_encode_hevc_base.h"
#endif
#include "codechal_huc_cmd_initializer.h"
#include "codechal_encoder_base.h"
#ifdef _VP9_ENCODE_VDENC_SUPPORTED
#include "codechal_vdenc_vp9_base.h"
#endif

#define VDBOX_HUC_CMD_INITIALIZER_HEVC_CMD1_STARTOFFSERT 28
#define VDBOX_HUC_CMD_INITIALIZER_HEVC_CMD2_STARTOFFSERT 276
#define VDBOX_HUC_CMD_INITIALIZER_HEVC_CQP_CMD2_STARTOFFSET 248

CodechalCmdInitializer::CodechalCmdInitializer(
    CodechalEncoderState *encoder)
{
    m_encoder = encoder;
    m_cmdCount = 0;
    m_currentPass = 0;
}

MOS_STATUS CodechalCmdInitializer::CmdInitializerAllocateResources(
    CodechalHwInterface *hwInterface)
{
    MOS_STATUS eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_ENCODE_FUNCTION_ENTER;

    m_hwInterface = hwInterface;
    m_osInterface = m_hwInterface->GetOsInterface();
    m_miInterface = m_hwInterface->GetMiInterface();
    MOS_ALLOC_GFXRES_PARAMS allocParamsForBufferLinear;
    MOS_ZeroMemory(&allocParamsForBufferLinear, sizeof(MOS_ALLOC_GFXRES_PARAMS));
    allocParamsForBufferLinear.Type = MOS_GFXRES_BUFFER;
    allocParamsForBufferLinear.TileType = MOS_TILE_LINEAR;
    allocParamsForBufferLinear.Format = Format_Buffer;

    //Allocate buffers for every pass.
    for (int i = 0; i < CODECHAL_ENCODE_RECYCLED_BUFFER_NUM; i++)
    {
        for(int j = 0; j < 3; j++)
        {
            // Cmd Initializer DMEM
            allocParamsForBufferLinear.dwBytes  = MOS_ALIGN_CEIL(sizeof(HucComDmem), CODECHAL_CACHELINE_SIZE);
            allocParamsForBufferLinear.pBufName = "VDEnc CmdInitializer Dmem Buffer";
            CODECHAL_ENCODE_CHK_STATUS_RETURN(
                m_osInterface->pfnAllocateResource(
                    m_osInterface,
                    &allocParamsForBufferLinear,
                    &m_cmdInitializerDmemBuffer[i][j]));

            // Cmd Initializer Data buffer
            allocParamsForBufferLinear.dwBytes  = MOS_ALIGN_CEIL(sizeof(HucComData), CODECHAL_PAGE_SIZE);
            allocParamsForBufferLinear.pBufName = "VDEnc CmdInitializer Data Buffer";
            CODECHAL_ENCODE_CHK_STATUS_RETURN(
                m_osInterface->pfnAllocateResource(
                    m_osInterface,
                    &allocParamsForBufferLinear,
                    &m_cmdInitializerDataBuffer[i][j]));

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

            uint8_t *pData = (uint8_t *)m_osInterface->pfnLockResource(
                m_osInterface,
                &m_cmdInitializerDataBuffer[i][j],
                &lockFlagsWriteOnly);
            CODECHAL_ENCODE_CHK_NULL_RETURN(pData);
            MOS_ZeroMemory(pData, allocParamsForBufferLinear.dwBytes);
            m_osInterface->pfnUnlockResource(m_osInterface, &m_cmdInitializerDataBuffer[i][j]);
        }
    }

    //Allocate extra buffers for dynamic scaling
    // Cmd Initializer DMEM
    allocParamsForBufferLinear.dwBytes = MOS_ALIGN_CEIL(sizeof(HucComDmem), CODECHAL_CACHELINE_SIZE);
    allocParamsForBufferLinear.pBufName = "VDEnc Dynamic Sclaing CmdInitializer Dmem Buffer";
    CODECHAL_ENCODE_CHK_STATUS_RETURN(
        m_osInterface->pfnAllocateResource(
            m_osInterface,
            &allocParamsForBufferLinear,
            &m_cmdInitializerDysScalingDmemBuffer));

    // Cmd Initializer Data buffer
    allocParamsForBufferLinear.dwBytes = MOS_ALIGN_CEIL(sizeof(HucComData), CODECHAL_PAGE_SIZE);
    allocParamsForBufferLinear.pBufName = "VDEnc Dynamic Sclaing CmdInitializer Data Buffer";
    CODECHAL_ENCODE_CHK_STATUS_RETURN(
        m_osInterface->pfnAllocateResource(
            m_osInterface,
            &allocParamsForBufferLinear,
            &m_cmdInitializerDysScalingDataBuffer));

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

    uint8_t* pData = (uint8_t*)m_osInterface->pfnLockResource(
        m_osInterface,
        &m_cmdInitializerDysScalingDataBuffer,
        &lockFlagsWriteOnly);
    CODECHAL_ENCODE_CHK_NULL_RETURN(pData);
    MOS_ZeroMemory(pData, allocParamsForBufferLinear.dwBytes);
    m_osInterface->pfnUnlockResource(m_osInterface, &m_cmdInitializerDysScalingDataBuffer);

    return eStatus;
}

void CodechalCmdInitializer::CmdInitializerFreeResources()
{
    for (int i = 0; i < CODECHAL_ENCODE_RECYCLED_BUFFER_NUM; i++)
    {
        for(int j = 0; j < 3; j++)
        {
            m_osInterface->pfnFreeResource(m_osInterface, &m_cmdInitializerDmemBuffer[i][j]);
            m_osInterface->pfnFreeResource(m_osInterface, &m_cmdInitializerDataBuffer[i][j]);
        }
    }
    m_osInterface->pfnFreeResource(m_osInterface, &m_cmdInitializerDysScalingDmemBuffer);
    m_osInterface->pfnFreeResource(m_osInterface, &m_cmdInitializerDysScalingDataBuffer);

}

#if defined (_HEVC_ENCODE_VME_SUPPORTED) || defined (_HEVC_ENCODE_VDENC_SUPPORTED)
MOS_STATUS CodechalCmdInitializer::CmdInitializerSetDmem(bool brcEnabled)
{
    HucComDmem*             hucCmdInitializerDmem;
    MOS_LOCK_PARAMS         lockFlagsWriteOnly;
    uint16_t                offset = 0;
    MOS_STATUS              eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_ENCODE_FUNCTION_ENTER;

    m_osInterface = m_encoder->GetOsInterface();

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

    // Setup CmdInitializer DMEM
    hucCmdInitializerDmem = (HucComDmem *)m_osInterface->pfnLockResource(
        m_osInterface, &m_cmdInitializerDmemBuffer[m_encoder->m_currRecycledBufIdx][m_currentPass], &lockFlagsWriteOnly);

    MOS_ZeroMemory(hucCmdInitializerDmem, sizeof(HucComDmem));
    
    CODECHAL_ENCODE_ASSERT(m_cmdCount == 2);
    hucCmdInitializerDmem->TotalOutputCommands = 2;

    hucCmdInitializerDmem->TargetUsage         = 4;
    switch (m_encoder->m_standard)
    {
        case CODECHAL_HEVC:
            hucCmdInitializerDmem->Codec       = 0;
            hucCmdInitializerDmem->TargetUsage = (uint8_t)m_encoder->m_targetUsage;
            break;
        case CODECHAL_VP9:
            hucCmdInitializerDmem->Codec       = 1;
            break;
        default:
            hucCmdInitializerDmem->Codec       = 0;
            break;
    }
    hucCmdInitializerDmem->FrameType = m_encoder->m_pictureCodingType - 1;
    hucCmdInitializerDmem->OutputCOM[0].ID           = 2;
    hucCmdInitializerDmem->OutputCOM[0].Type         = 1;
    hucCmdInitializerDmem->OutputCOM[0].StartInBytes = GetCmd1StartOffset(brcEnabled);

    offset += CODECHAL_CMDINITIALIZER_MAX_CMD_SIZE;

    // Command ID 1
    hucCmdInitializerDmem->OutputCOM[1].ID           = 1;
    hucCmdInitializerDmem->OutputCOM[1].Type         = 1;
    hucCmdInitializerDmem->OutputCOM[1].StartInBytes = GetCmd2StartOffset(brcEnabled);

    offset += CODECHAL_CMDINITIALIZER_MAX_CMD_SIZE;

    hucCmdInitializerDmem->OutputSize = offset;

    m_osInterface->pfnUnlockResource(m_osInterface, &m_cmdInitializerDmemBuffer[m_encoder->m_currRecycledBufIdx][m_currentPass]);

    return eStatus;
}

MOS_STATUS CodechalCmdInitializer::ConstructHevcHucCmd1ConstData(
    PCODEC_HEVC_ENCODE_SEQUENCE_PARAMS seqParams,
    PCODEC_HEVC_ENCODE_PICTURE_PARAMS  picParams,
    PCODEC_HEVC_ENCODE_SLICE_PARAMS    sliceParams,
    struct HucComData *                hucConstData)
{
    hucConstData->InputCOM[1].ID         = 1;
    hucConstData->InputCOM[1].SizeOfData = sizeof(HucInputCmd1) / sizeof(uint8_t);

    auto   qpPrimeYAC = 10;  //This is constant from Arch C Model
    double qpScale    = (picParams->CodingType == I_TYPE) ? 0.60 : 0.65;

    HucInputCmd1 cmd1;
    MOS_ZeroMemory(&cmd1, sizeof(HucInputCmd1));

    // Shared HEVC/VP9
    cmd1.FrameWidthInMinCbMinus1  = seqParams->wFrameWidthInMinCbMinus1;
    cmd1.FrameHeightInMinCbMinus1 = seqParams->wFrameHeightInMinCbMinus1;

    cmd1.log2_min_coding_block_size_minus3 = seqParams->log2_min_coding_block_size_minus3;

    cmd1.VdencStreamInEnabled         = (uint8_t)m_streamInEnabled;
    cmd1.PakOnlyMultipassEnable       = m_pakOnlyPass;
    cmd1.num_ref_idx_l0_active_minus1 = sliceParams->num_ref_idx_l0_active_minus1;

    auto   qpPrimeYac = CodecHal_Clip3(0, 51, picParams->QpY + sliceParams->slice_qp_delta);
    double lambda     = sqrt(qpScale * pow(2.0, MOS_MAX(0, qpPrimeYac - 12) / 3.0));
    cmd1.SADQPLambda  = (uint16_t)(lambda * 4 + 0.5);
    cmd1.RDQPLambda   = (uint16_t)(qpScale * pow(2.0, MOS_MAX(0, picParams->QpY - 12) / 3.0) * 4 + 0.5);  //U14.2

    cmd1.num_ref_idx_l1_active_minus1 = sliceParams->num_ref_idx_l1_active_minus1;
    cmd1.ROIStreamInEnabled           = (uint8_t)m_roiStreamInEnabled;
    cmd1.UseDefaultQpDeltas           = (m_acqpEnabled && seqParams->QpAdjustment) || (m_brcEnabled && seqParams->MBBRC != mbBrcDisabled);
    cmd1.TemporalMvpEnableFlag        = seqParams->sps_temporal_mvp_enable_flag;
    cmd1.PanicEnabled                 = m_panicEnabled;

    if (m_roiStreamInEnabled)
    {
        for (int8_t i = 0; i < ENCODE_VDENC_HEVC_MAX_STREAMINROI_G10; i++)
        {
            cmd1.ROIDeltaQp[i] = picParams->ROIDistinctDeltaQp[i];
        }
    }

    // default
    cmd1.FwdPocNumForRefId0inL0 = 0x01;
    cmd1.FwdPocNumForRefId0inL1 = 0xff;
    cmd1.FwdPocNumForRefId1inL0 = 0x02;
    cmd1.FwdPocNumForRefId1inL1 = 0xfe;
    cmd1.FwdPocNumForRefId2inL0 = 0x03;
    cmd1.FwdPocNumForRefId2inL1 = 0xfd;
    cmd1.FwdPocNumForRefId3inL0 = 0x04;
    cmd1.FwdPocNumForRefId3inL1 = 0xfc;

    if (picParams->CodingType != I_TYPE)
    {
        uint8_t refFrameID;
        char    diff_poc;

        refFrameID                  = sliceParams->RefPicList[0][0].FrameIdx;
        diff_poc                    = picParams->RefFramePOCList[refFrameID] - picParams->CurrPicOrderCnt;
        cmd1.FwdPocNumForRefId0inL0 = -diff_poc;
        cmd1.FwdPocNumForRefId0inL1 = -diff_poc;

        refFrameID                  = sliceParams->RefPicList[0][1].FrameIdx;
        diff_poc                    = picParams->RefFramePOCList[refFrameID] - picParams->CurrPicOrderCnt;
        cmd1.FwdPocNumForRefId1inL0 = -diff_poc;
        cmd1.FwdPocNumForRefId1inL1 = -diff_poc;

        refFrameID                  = sliceParams->RefPicList[0][2].FrameIdx;
        diff_poc                    = picParams->RefFramePOCList[refFrameID] - picParams->CurrPicOrderCnt;
        cmd1.FwdPocNumForRefId2inL0 = -diff_poc;
        cmd1.FwdPocNumForRefId2inL1 = -diff_poc;
    }

    cmd1.EnableRollingIntraRefresh          = picParams->bEnableRollingIntraRefresh;
    cmd1.QpDeltaForInsertedIntra            = picParams->QpDeltaForInsertedIntra;
    cmd1.IntraInsertionSize                 = picParams->IntraInsertionSize;
    cmd1.IntraInsertionLocation             = picParams->IntraInsertionLocation;
    cmd1.IntraInsertionReferenceLocation[0] = picParams->RollingIntraReferenceLocation[0];
    cmd1.IntraInsertionReferenceLocation[1] = picParams->RollingIntraReferenceLocation[1];
    cmd1.IntraInsertionReferenceLocation[2] = picParams->RollingIntraReferenceLocation[2];

    cmd1.QpY             = picParams->QpY + sliceParams->slice_qp_delta;
    cmd1.RoundingEnabled = (uint8_t)m_roundingEnabled;

    MOS_SecureMemcpy(hucConstData->InputCOM[1].data, sizeof(HucInputCmd1), &cmd1, sizeof(HucInputCmd1));

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS CodechalCmdInitializer::ConstructHevcHucCmd2ConstData(
    PCODEC_HEVC_ENCODE_SEQUENCE_PARAMS seqParams,
    PCODEC_HEVC_ENCODE_PICTURE_PARAMS  picParams,
    PCODEC_HEVC_ENCODE_SLICE_PARAMS    sliceParams,
    struct HucComData *                hucConstData)
{
    hucConstData->InputCOM[0].ID         = 2;
    hucConstData->InputCOM[0].SizeOfData = 2;

    auto qpPrimeYAC = 10;  //This is constant from Arch C Model

    double qpScale        = (picParams->CodingType == I_TYPE) ? 0.60 : 0.65;
    double lambdaInputCom = sqrt(qpScale * pow(2.0, MOS_MAX(0, qpPrimeYAC - 12) / 3.0));

    // SADQPLambda
    hucConstData->InputCOM[0].data[0] = (uint32_t)(lambdaInputCom * 4 + 0.5);
    hucConstData->InputCOM[0].data[1] = m_roiStreamInEnabled;

    return MOS_STATUS_SUCCESS;
}

uint16_t CodechalCmdInitializer::GetCmd1StartOffset(bool brcEnabled)
{
    return brcEnabled ? VDBOX_HUC_CMD_INITIALIZER_HEVC_CMD1_STARTOFFSERT : 0;
}

uint16_t CodechalCmdInitializer::GetCmd2StartOffset(bool brcEnabled)
{
    return brcEnabled ? VDBOX_HUC_CMD_INITIALIZER_HEVC_CMD2_STARTOFFSERT : VDBOX_HUC_CMD_INITIALIZER_HEVC_CQP_CMD2_STARTOFFSET;
}

MOS_STATUS CodechalCmdInitializer::CmdInitializerSetConstData(
    PMOS_INTERFACE                              osInterface,
    MhwMiInterface                              *miInterface,
    MhwVdboxVdencInterface                      *vdencInterface,
    void*                                       sequenceParams,
    void*                                       pictureParams,
    void*                                       slcParams,
    bool                                        pakOnlyPass,
    bool                                        acqpEnabled,
    bool                                        brcEnabled,
    bool                                        streaminEnabled,
    bool                                        roiStreamInEnabled,
    bool                                        roundingEnabled,
    bool                                        panicEnabled,
    int32_t                                     currentPass
    )
{
    HucComData*                                     hucConstData;
    MOS_LOCK_PARAMS                                 lockFlagsWriteOnly;
    MOS_STATUS                                      eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_ENCODE_FUNCTION_ENTER;

    CODECHAL_ENCODE_CHK_NULL_RETURN(osInterface);
    CODECHAL_ENCODE_CHK_NULL_RETURN(miInterface);
    CODECHAL_ENCODE_CHK_NULL_RETURN(vdencInterface);
    CODECHAL_ENCODE_CHK_NULL_RETURN(sequenceParams);
    CODECHAL_ENCODE_CHK_NULL_RETURN(pictureParams);
    CODECHAL_ENCODE_CHK_NULL_RETURN(slcParams);

    m_osInterface    = osInterface;
    m_miInterface    = miInterface;
    m_vdencInterface = vdencInterface;
    m_seqParams      = sequenceParams;
    m_picParams      = pictureParams;
    m_sliceParams    = slcParams;

    PCODEC_HEVC_ENCODE_SEQUENCE_PARAMS seqParams = (PCODEC_HEVC_ENCODE_SEQUENCE_PARAMS)sequenceParams;
    PCODEC_HEVC_ENCODE_PICTURE_PARAMS picParams = (PCODEC_HEVC_ENCODE_PICTURE_PARAMS)pictureParams;
    PCODEC_HEVC_ENCODE_SLICE_PARAMS sliceParams = (PCODEC_HEVC_ENCODE_SLICE_PARAMS)slcParams;

    m_pakOnlyPass        = pakOnlyPass;
    m_acqpEnabled        = acqpEnabled;
    m_brcEnabled         = brcEnabled;
    m_streamInEnabled    = streaminEnabled;
    m_roundingEnabled    = roundingEnabled;
    m_panicEnabled       = panicEnabled;
    m_roiStreamInEnabled = roiStreamInEnabled;
    m_currentPass        = currentPass;

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

    hucConstData = (HucComData *)m_osInterface->pfnLockResource(m_osInterface, &m_cmdInitializerDataBuffer[m_encoder->m_currRecycledBufIdx][currentPass], &lockFlagsWriteOnly);

    MOS_ZeroMemory(hucConstData, sizeof(HucComData));
    m_cmdCount = 0;

    // Command ID 2
    ConstructHevcHucCmd2ConstData(seqParams, picParams, sliceParams, hucConstData);
    m_cmdCount++;

    // Command ID 1
    ConstructHevcHucCmd1ConstData(seqParams, picParams, sliceParams, hucConstData);
    m_cmdCount++;

    hucConstData->TotalCommands = m_cmdCount;

    m_osInterface->pfnUnlockResource(m_osInterface, &m_cmdInitializerDataBuffer[m_encoder->m_currRecycledBufIdx][currentPass]);

    return eStatus;
}

MOS_STATUS CodechalCmdInitializer::CmdInitializerExecute(
    bool                brcEnabled,
    PMOS_RESOURCE       secondlevelBB,
    MOS_COMMAND_BUFFER* cmdBuffer)
{
    MHW_MI_FLUSH_DW_PARAMS                  flushDwParams;
    MHW_VDBOX_PIPE_MODE_SELECT_PARAMS       pipeModeSelectParams;
    MHW_VDBOX_HUC_IMEM_STATE_PARAMS         imemParams;
    MHW_VDBOX_HUC_DMEM_STATE_PARAMS         dmemParams;
    MHW_VDBOX_HUC_VIRTUAL_ADDR_PARAMS       virtualAddrParams;
    MOS_LOCK_PARAMS                         lockFlagsWriteOnly;
    MHW_VDBOX_VD_PIPE_FLUSH_PARAMS          vdPipeFlushParams;
    bool                                    requestFrameTracking;
    uint8_t                                 codec;
    MOS_STATUS                              eStatus = MOS_STATUS_SUCCESS;
    bool                                    renderingFlags;
    CodechalHwInterface                     *hwInterface;
    bool                                    externCmdBuffer = (cmdBuffer != nullptr);

    CODECHAL_ENCODE_FUNCTION_ENTER;

    hwInterface    = m_encoder->GetHwInterface();
    m_osInterface  = m_encoder->GetOsInterface();
    m_miInterface  = hwInterface->GetMiInterface();

    // for scalability, the cmdbuffer is passed outside
    // otherwise the cmdbuffer is fetched here
    if (cmdBuffer == nullptr)
        CODECHAL_ENCODE_CHK_STATUS_RETURN(m_osInterface->pfnGetCommandBuffer(m_osInterface, cmdBuffer, 0));

    if (!m_encoder->m_singleTaskPhaseSupported || m_encoder->m_firstTaskInPhase)
    {
        // Send command buffer header at the beginning (OS dependent)
        requestFrameTracking = m_encoder->m_singleTaskPhaseSupported ? m_encoder->m_firstTaskInPhase : 0;
        CODECHAL_ENCODE_CHK_STATUS_RETURN(m_encoder->SendPrologWithFrameTracking(cmdBuffer, requestFrameTracking));
    }

    // load kernel from WOPCM into L2 storage RAM
    MOS_ZeroMemory(&imemParams, sizeof(imemParams));
    imemParams.dwKernelDescriptor = m_hucCmdInitializerKernelDescriptor;

    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_encoder->GetHwInterface()->GetHucInterface()->AddHucImemStateCmd(cmdBuffer, &imemParams));

    // HUC_PIPE_MODE_SELECT    
    pipeModeSelectParams.Mode = m_encoder->m_mode;
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_encoder->GetHwInterface()->GetHucInterface()->AddHucPipeModeSelectCmd(cmdBuffer, &pipeModeSelectParams));

    CODECHAL_ENCODE_CHK_STATUS_RETURN(CmdInitializerSetDmem(brcEnabled));

    // set HuC DMEM param
    MOS_ZeroMemory(&dmemParams, sizeof(dmemParams));
    dmemParams.presHucDataSource = &m_cmdInitializerDmemBuffer[m_encoder->m_currRecycledBufIdx][m_currentPass];
    dmemParams.dwDataLength      = MOS_ALIGN_CEIL(sizeof(HucComDmem), CODECHAL_CACHELINE_SIZE);
    dmemParams.dwDmemOffset      = HUC_DMEM_OFFSET_RTOS_GEMS;
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_encoder->GetHwInterface()->GetHucInterface()->AddHucDmemStateCmd(cmdBuffer, &dmemParams));

    MOS_ZeroMemory(&virtualAddrParams, sizeof(virtualAddrParams));
    virtualAddrParams.regionParams[0].presRegion = &m_cmdInitializerDataBuffer[m_encoder->m_currRecycledBufIdx][m_currentPass];

    virtualAddrParams.regionParams[1].presRegion = secondlevelBB;

    virtualAddrParams.regionParams[1].isWritable = true;

    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_encoder->GetHwInterface()->GetHucInterface()->AddHucVirtualAddrStateCmd(cmdBuffer, &virtualAddrParams));

    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_encoder->GetHwInterface()->GetHucInterface()->AddHucStartCmd(cmdBuffer, true));

    // wait Huc completion (use HEVC bit for now)
    MOS_ZeroMemory(&vdPipeFlushParams, sizeof(vdPipeFlushParams));
    vdPipeFlushParams.Flags.bFlushHEVC = 1;
    vdPipeFlushParams.Flags.bWaitDoneHEVC = 1;
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_encoder->GetHwInterface()->GetVdencInterface()->AddVdPipelineFlushCmd(cmdBuffer, &vdPipeFlushParams));

    // Flush the engine to ensure memory written out
    MOS_ZeroMemory(&flushDwParams, sizeof(flushDwParams));
    flushDwParams.bVideoPipelineCacheInvalidate = true;
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_miInterface->AddMiFlushDwCmd(cmdBuffer, &flushDwParams));

    if (!m_encoder->m_singleTaskPhaseSupported && (m_osInterface->bNoParsingAssistanceInKmd))
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(m_miInterface->AddMiFlushDwCmd(cmdBuffer, &flushDwParams));
    }

    if ((!m_encoder->m_singleTaskPhaseSupported))
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(m_miInterface->AddMiBatchBufferEnd(cmdBuffer, nullptr));
    }

    // if the cmdbuffer is passed outside, then we don't need to submit, just return
    if (externCmdBuffer)
        return eStatus;

    m_osInterface->pfnReturnCommandBuffer(m_osInterface, cmdBuffer, 0);

    if (!m_encoder->m_singleTaskPhaseSupported)
    {
        renderingFlags = m_encoder->m_videoContextUsesNullHw;

        CODECHAL_DEBUG_TOOL(CODECHAL_ENCODE_CHK_STATUS_RETURN(m_encoder->GetDebugInterface()->DumpCmdBuffer(
            cmdBuffer,
            CODECHAL_NUM_MEDIA_STATES,
            "HucCmd")));

        CODECHAL_ENCODE_CHK_STATUS_RETURN(m_osInterface->pfnSubmitCommandBuffer(m_osInterface, cmdBuffer, renderingFlags));
        CODECHAL_DEBUG_TOOL(CODECHAL_ENCODE_CHK_STATUS_RETURN(DumpHucCmdInit(secondlevelBB)));
    }

    return eStatus;
}
#endif

#ifdef _VP9_ENCODE_VDENC_SUPPORTED
//VP9 Specific functions

MOS_STATUS CodechalCmdInitializer::CommandInitializerSetVp9Params(CodechalVdencVp9State *state)
{
    HucComData *    hucConstData;
    MOS_LOCK_PARAMS lockFlagsWriteOnly;
    MOS_STATUS      eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_ENCODE_CHK_NULL_RETURN(state);
    MOS_ZeroMemory(&m_vp9Params, sizeof(m_vp9Params));
    m_vp9Params.prevFrameSegEnabled = state->m_prevFrameSegEnabled;
    m_vp9Params.seqParams                    = state->m_vp9SeqParams;
    m_vp9Params.picParams                    = state->m_vp9PicParams;
    m_vp9Params.segmentationEnabled          = state->m_vp9PicParams->PicFlags.fields.segmentation_enabled;
    m_vp9Params.segmentMapProvided           = state->m_segmentMapProvided;
    m_vp9Params.prevFrameSegEnabled          = state->m_prevFrameSegEnabled;
    m_vp9Params.numRefFrames                 = state->m_numRefFrames;
    m_vp9Params.hmeEnabled                   = state->m_hmeEnabled;
    m_vp9Params.dysVdencMultiPassEnabled     = state->m_dysVdencMultiPassEnabled;
    m_vp9Params.vdencPakOnlyMultipassEnabled = state->m_vdencPakonlyMultipassEnabled;
    m_vp9Params.pictureCodingType            = state->m_pictureCodingType;
    m_vp9Params.currentPass                  = state->GetCurrentPass();
    m_currentPass                            = m_vp9Params.currentPass;
    m_vp9Params.singleTaskPhaseSupported     = state->m_singleTaskPhaseSupported;
    m_vp9Params.lastTaskInPhase              = state->m_lastTaskInPhase;
    m_vp9Params.firstTaskInPhase             = state->m_firstTaskInPhase;
    m_vp9Params.mode                         = state->m_mode;
    m_vdencInterface                         = state->m_vdencInterface;
    m_vp9Params.videoContextUsesNullHw       = state->m_videoContextUsesNullHw;
    m_vp9Params.debugInterface               = state->GetDebugInterface();
    m_vp9Params.dynamicScalingEnabled        = (state->m_dysRefFrameFlags != DYS_REF_NONE) ? true : false;
    m_vp9Params.segmentParams                = state->m_vp9SegmentParams;
    m_vp9Params.bPrevFrameKey                = state->m_prevFrameInfo.KeyFrame;
    return eStatus;
}

MOS_STATUS CodechalCmdInitializer::CmdInitializerVp9Execute(PMOS_COMMAND_BUFFER cmdBuffer, PMOS_RESOURCE picStateBuffer)
{
    MHW_VDBOX_PIPE_MODE_SELECT_PARAMS pipeModeSelectParams;
    MHW_VDBOX_HUC_IMEM_STATE_PARAMS   imemParams;
    MHW_VDBOX_HUC_DMEM_STATE_PARAMS   dmemParams;
    MHW_VDBOX_HUC_VIRTUAL_ADDR_PARAMS virtualAddrParams;
    MOS_LOCK_PARAMS                   lockFlagsWriteOnly;
    bool                              requestFrameTracking;
    uint8_t                           codec;
    MOS_STATUS                        eStatus = MOS_STATUS_SUCCESS;
    bool                              renderingFlags;

    CODECHAL_ENCODE_FUNCTION_ENTER;
    CODECHAL_ENCODE_CHK_NULL_RETURN(m_encoder);
    CODECHAL_ENCODE_CHK_NULL_RETURN(cmdBuffer);

    // load kernel from WOPCM into L2 storage RAM
    MOS_ZeroMemory(&imemParams, sizeof(imemParams));
    imemParams.dwKernelDescriptor = m_hucCmdInitializerKernelDescriptor;

    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_hwInterface->GetHucInterface()->AddHucImemStateCmd(cmdBuffer, &imemParams));

    // HUC_PIPE_MODE_SELECT
    pipeModeSelectParams.Mode = m_vp9Params.mode;
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_hwInterface->GetHucInterface()->AddHucPipeModeSelectCmd(cmdBuffer, &pipeModeSelectParams));

    CODECHAL_ENCODE_CHK_STATUS_RETURN(CmdInitializerVp9SetDmem());

    // set HuC DMEM param
    MOS_ZeroMemory(&dmemParams, sizeof(dmemParams));
    if (m_vp9Params.dynamicScalingEnabled)
    {
        dmemParams.presHucDataSource = &m_cmdInitializerDysScalingDmemBuffer;
    }
    else
    {
        dmemParams.presHucDataSource = &m_cmdInitializerDmemBuffer[m_encoder->m_currRecycledBufIdx][m_vp9Params.currentPass];
    }
    dmemParams.dwDataLength = MOS_ALIGN_CEIL(sizeof(HucComDmem), CODECHAL_CACHELINE_SIZE);
    dmemParams.dwDmemOffset = HUC_DMEM_OFFSET_RTOS_GEMS;
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_hwInterface->GetHucInterface()->AddHucDmemStateCmd(cmdBuffer, &dmemParams));

    MOS_ZeroMemory(&virtualAddrParams, sizeof(virtualAddrParams));
    if (m_vp9Params.dynamicScalingEnabled)
    {
        virtualAddrParams.regionParams[0].presRegion = &m_cmdInitializerDysScalingDataBuffer;
        virtualAddrParams.regionParams[1].presRegion = picStateBuffer; // Region 1  Output SLB Buffer Pass 1 (Output)
    }
    else
    {
        virtualAddrParams.regionParams[0].presRegion = &m_cmdInitializerDataBuffer[m_encoder->m_currRecycledBufIdx][m_vp9Params.currentPass];
        virtualAddrParams.regionParams[1].presRegion = picStateBuffer; // Region 1  Output SLB Buffer Pass 1 (Output)
    }

    virtualAddrParams.regionParams[1].isWritable = true;

    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_hwInterface->GetHucInterface()->AddHucVirtualAddrStateCmd(cmdBuffer, &virtualAddrParams));

    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_hwInterface->GetHucInterface()->AddHucStartCmd(cmdBuffer, true));

    // wait Huc completion (use HEVC bit for now)
    MHW_VDBOX_VD_PIPE_FLUSH_PARAMS vdPipeFlushParams;
    MOS_ZeroMemory(&vdPipeFlushParams, sizeof(vdPipeFlushParams));
    vdPipeFlushParams.Flags.bFlushHEVC = 1;
    vdPipeFlushParams.Flags.bWaitDoneHEVC = 1;
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_hwInterface->GetVdencInterface()->AddVdPipelineFlushCmd(cmdBuffer, &vdPipeFlushParams));

    // Flush the engine to ensure memory written out
    MHW_MI_FLUSH_DW_PARAMS flushDwParams;
    MOS_ZeroMemory(&flushDwParams, sizeof(flushDwParams));
    flushDwParams.bVideoPipelineCacheInvalidate = true;
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_miInterface->AddMiFlushDwCmd(cmdBuffer, &flushDwParams));

    if (!m_vp9Params.singleTaskPhaseSupported)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(m_miInterface->AddMiBatchBufferEnd(cmdBuffer, nullptr));
    }

    m_osInterface->pfnReturnCommandBuffer(m_osInterface, cmdBuffer, 0);

    if (!m_vp9Params.singleTaskPhaseSupported)
    {
        bool renderFlags = m_vp9Params.videoContextUsesNullHw;
        CODECHAL_ENCODE_CHK_STATUS_RETURN(m_osInterface->pfnSubmitCommandBuffer(m_osInterface, cmdBuffer, renderFlags));
    }

    return eStatus;
}

MOS_STATUS CodechalCmdInitializer::CmdInitializerVp9SetDmem()
{
    HucComDmem *    hucCmdInitializerDmem;
    MOS_LOCK_PARAMS lockFlagsWriteOnly;
    uint16_t        offset  = 0;
    MOS_STATUS      eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_ENCODE_FUNCTION_ENTER;
    CODECHAL_ENCODE_CHK_NULL_RETURN(m_encoder);

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

    HucComData *hucConstData;
    if (m_vp9Params.dynamicScalingEnabled)
    {
        hucConstData = (HucComData *)m_osInterface->pfnLockResource(m_osInterface, &m_cmdInitializerDysScalingDataBuffer, &lockFlagsWriteOnly);
    }
    else
    {
        hucConstData = (HucComData *)m_osInterface->pfnLockResource(m_osInterface, &m_cmdInitializerDataBuffer[m_encoder->m_currRecycledBufIdx][m_vp9Params.currentPass], &lockFlagsWriteOnly);
    }

    MOS_ZeroMemory(hucConstData, sizeof(HucComData));

    hucConstData->TotalCommands = 2;

    // Command ID 2
    hucConstData->InputCOM[0].ID = 2;
    hucConstData->InputCOM[0].SizeOfData = 2;

    double qpScale = (m_vp9Params.pictureCodingType == I_TYPE) ? 0.31 : 0.33;
    uint8_t qp = m_vp9Params.picParams->LumaACQIndex;
    double lambda = qpScale * CODECHAL_VP9_QUANT_AC[qp] / 8;

    // SADQPLambda
    hucConstData->InputCOM[0].data[0] = (uint32_t)(lambda * 4 + 0.5);

    // Command ID 1
    hucConstData->InputCOM[1].ID = 1;
    hucConstData->InputCOM[1].SizeOfData = 0x17;

    HucInputCmd1  hucInputCmd1;
    MOS_ZeroMemory(&hucInputCmd1, sizeof(hucInputCmd1));
    hucInputCmd1.VdencStreamInEnabled = m_vp9Params.segmentMapProvided || m_vp9Params.hmeEnabled;
    hucInputCmd1.SegMapStreamInEnabled = m_vp9Params.segmentMapProvided || m_vp9Params.hmeEnabled;
    hucInputCmd1.PakOnlyMultipassEnable = m_vp9Params.vdencPakOnlyMultipassEnabled;
    hucInputCmd1.num_ref_idx_l0_active_minus1 = (m_vp9Params.picParams->PicFlags.fields.frame_type) ? m_vp9Params.numRefFrames - 1 : 0;

    hucInputCmd1.SADQPLambda = (uint16_t)(lambda * 4 + 0.5);
    hucInputCmd1.RDQPLambda = (uint16_t)(lambda * lambda * 4 + 0.5); //U14.2

    hucInputCmd1.SrcFrameHeightMinus1 = m_vp9Params.picParams->SrcFrameHeightMinus1;
    hucInputCmd1.SrcFrameWidthMinus1 = m_vp9Params.picParams->SrcFrameWidthMinus1;
    hucInputCmd1.SegmentationEnabled = m_vp9Params.segmentationEnabled;
    hucInputCmd1.PrevFrameSegEnabled = m_vp9Params.prevFrameSegEnabled;
    hucInputCmd1.LumaACQIndex = m_vp9Params.picParams->LumaACQIndex;
    hucInputCmd1.LumaDCQIndexDelta = m_vp9Params.picParams->LumaDCQIndexDelta;

    if (m_vp9Params.segmentationEnabled)
    {
        for (int i = 0; i < 8; i++)
        {
            hucInputCmd1.SegmentQIndexDelta[i] = m_vp9Params.segmentParams->SegData[i].SegmentQIndexDelta;
        }
    }

    MOS_SecureMemcpy(hucConstData->InputCOM[1].data, sizeof(HucInputCmd1), &hucInputCmd1, sizeof(HucInputCmd1));
    if (m_vp9Params.dynamicScalingEnabled)
    {
        m_osInterface->pfnUnlockResource(m_osInterface, &m_cmdInitializerDysScalingDataBuffer);
    }
    else
    {
        m_osInterface->pfnUnlockResource(m_osInterface, &m_cmdInitializerDataBuffer[m_encoder->m_currRecycledBufIdx][m_vp9Params.currentPass]);
    }
    MOS_ZeroMemory(&lockFlagsWriteOnly, sizeof(MOS_LOCK_PARAMS));
    lockFlagsWriteOnly.WriteOnly = 1;

    // Setup CmdInitializer DMEM
    if (m_vp9Params.dynamicScalingEnabled)
    {
        hucCmdInitializerDmem = (HucComDmem *)m_osInterface->pfnLockResource(
            m_osInterface, &m_cmdInitializerDysScalingDmemBuffer, &lockFlagsWriteOnly);
    }
    else
    {
        hucCmdInitializerDmem = (HucComDmem *)m_osInterface->pfnLockResource(
            m_osInterface, &m_cmdInitializerDmemBuffer[m_encoder->m_currRecycledBufIdx][m_vp9Params.currentPass], &lockFlagsWriteOnly);
    }
    MOS_ZeroMemory(hucCmdInitializerDmem, sizeof(HucComDmem));

    hucCmdInitializerDmem->TotalOutputCommands = 2;

    hucCmdInitializerDmem->TargetUsage = 4;

    hucCmdInitializerDmem->Codec = 1;
    hucCmdInitializerDmem->TargetUsage = (uint8_t)m_vp9Params.seqParams->TargetUsage;
    hucCmdInitializerDmem->FrameType = (uint8_t)m_vp9Params.picParams->PicFlags.fields.frame_type;
    hucCmdInitializerDmem->OutputCOM[0].ID = 2;
    hucCmdInitializerDmem->OutputCOM[0].Type = 1;
    hucCmdInitializerDmem->OutputCOM[0].StartInBytes = 0;

                                                          // Command ID 1
    hucCmdInitializerDmem->OutputCOM[1].ID = 1;
    hucCmdInitializerDmem->OutputCOM[1].Type = 1;
    hucCmdInitializerDmem->OutputCOM[1].StartInBytes = 544;

    hucCmdInitializerDmem->OutputSize = 544 + CODECHAL_CMD2_SIZE;
    if (m_vp9Params.dynamicScalingEnabled)
    {
        m_osInterface->pfnUnlockResource(m_osInterface, &m_cmdInitializerDysScalingDmemBuffer);
    }
    else
    {
        m_osInterface->pfnUnlockResource(m_osInterface, &m_cmdInitializerDmemBuffer[m_encoder->m_currRecycledBufIdx][m_vp9Params.currentPass]);
    }

    return eStatus;
}
#if USE_CODECHAL_DEBUG_TOOL
MOS_STATUS CodechalCmdInitializer::DumpHucCmdInit(PMOS_RESOURCE secondlevelBB)
{
    CODECHAL_ENCODE_FUNCTION_ENTER;
    int idx = (m_encoder == nullptr) ? 0 : m_encoder->m_currRecycledBufIdx;
    // Dump DMEM
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_encoder->GetDebugInterface()->DumpHucDmem(
        &m_cmdInitializerDmemBuffer[idx][m_currentPass],
        sizeof(HucComDmem),
        m_currentPass,
        hucRegionDumpCmdInitializer));

    // Region 0 - input data buffer
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_encoder->GetDebugInterface()->DumpHucRegion(
        &m_cmdInitializerDataBuffer[idx][m_currentPass],
        0,
        sizeof(HucComData),
        0,
        "",
        true,
        m_currentPass,
        hucRegionDumpCmdInitializer));

    // Region 1 - output cmd
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_encoder->GetDebugInterface()->DumpHucRegion(
        secondlevelBB,
        0,
        m_hwInterface->m_vdencReadBatchBufferSize,
        1,
        "",
        false,
        m_currentPass,
        hucRegionDumpCmdInitializer));

    return MOS_STATUS_SUCCESS;
}
#endif
#endif
