/*
* Copyright (c) 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_g11.cpp
//! \brief    Defines class for g11 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_g11.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_G11 36
#define VDBOX_HUC_CMD_INITIALIZER_HEVC_CMD2_STARTOFFSERT_G11 308
#define VDBOX_HUC_CMD_INITIALIZER_HEVC_CQP_CMD2_STARTOFFSET_G11 272
#define VDBOX_HUC_CMD_INITIALIZER_HEVC_CMD5_BRC_STARTOFFSET_G11 156
#define VDBOX_HUC_CMD_INITIALIZER_HEVC_CMD5_CQP_STARTOFFSET_G11 120

#define CMD_LIST_MODE 1
#define BATCH_BUFFER_END 0x05000000

CodechalCmdInitializerG11::CodechalCmdInitializerG11(
    CodechalEncoderState *encoder)
    : CodechalCmdInitializer(encoder)
{
}

#if defined (_HEVC_ENCODE_VME_SUPPORTED) || defined (_HEVC_ENCODE_VDENC_SUPPORTED)

MOS_STATUS CodechalCmdInitializerG11::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(HucInputCmd1G11) / sizeof(uint32_t);

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

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

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

    cmd1.log2_min_coding_block_size_minus3 = seqParams->log2_min_coding_block_size_minus3;

    cmd1.tiles_enabled_flag           = picParams->tiles_enabled_flag;
    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;

    cmd1.BRCMinQp = picParams->BRCMinQp < 0x0a ? 0x0a : picParams->BRCMinQp;                                        // Setting values from arch spec
    cmd1.BRCMaxQp = picParams->BRCMaxQp < 0x0a ? 0x33 : (picParams->BRCMaxQp > 0x33 ? 0x33 : picParams->BRCMaxQp);  // Setting values from arch spec

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

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS CodechalCmdInitializerG11::AddCmdConstData(
    uint16_t cmdId,
    uint32_t* data,
    uint16_t size,
    uint32_t startOffset
)
{
    MOS_LOCK_PARAMS lockFlagsWriteOnly;
    HucComData* hucConstData;
    int idx = m_cmdCount++;

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

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

    hucConstData->InputCOM[idx].ID = cmdId;
    hucConstData->InputCOM[idx].SizeOfData = size;
    
    MOS_SecureMemcpy(hucConstData->InputCOM[idx].data, size, data, size);

    m_osInterface->pfnUnlockResource(m_osInterface, &m_cmdInitializerDataBuffer[m_encoder->m_currRecycledBufIdx][m_currentPass]);
    return MOS_STATUS_SUCCESS;
}

MOS_STATUS CodechalCmdInitializerG11::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 == 3);
    hucCmdInitializerDmem->TotalOutputCommands = 3;

    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);
    hucCmdInitializerDmem->OutputCOM[1].BBEnd        = BATCH_BUFFER_END;

    offset += CODECHAL_CMDINITIALIZER_MAX_CMD_SIZE;

    // Command ID 5
    hucCmdInitializerDmem->OutputCOM[2].ID           = 5;
    hucCmdInitializerDmem->OutputCOM[2].Type         = 1;
    hucCmdInitializerDmem->OutputCOM[2].StartInBytes = GetCmd5StartOffset(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;
}

uint16_t CodechalCmdInitializerG11::GetCmd2StartOffset(bool brcEnabled)
{
    return brcEnabled ? VDBOX_HUC_CMD_INITIALIZER_HEVC_CMD2_STARTOFFSERT_G11 : VDBOX_HUC_CMD_INITIALIZER_HEVC_CQP_CMD2_STARTOFFSET_G11;
}

uint16_t CodechalCmdInitializerG11::GetCmd1StartOffset(bool brcEnabled)
{
    return brcEnabled ? VDBOX_HUC_CMD_INITIALIZER_HEVC_CMD1_STARTOFFSERT_G11 : 0;
}

uint16_t CodechalCmdInitializerG11::GetCmd5StartOffset(bool brcEnabled)
{
    return brcEnabled ? VDBOX_HUC_CMD_INITIALIZER_HEVC_CMD5_BRC_STARTOFFSET_G11 : VDBOX_HUC_CMD_INITIALIZER_HEVC_CMD5_CQP_STARTOFFSET_G11;
}


#endif

#ifdef _VP9_ENCODE_VDENC_SUPPORTED
//VP9 Specific functions
MOS_STATUS CodechalCmdInitializerG11::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));

    return eStatus;
}

MOS_STATUS CodechalCmdInitializerG11::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 = 0x18;

    HucInputCmd1G11 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;
    hucInputCmd1.log2_tile_columns = m_vp9Params.picParams->log2_tile_columns;
    hucInputCmd1.log2_tile_rows = m_vp9Params.picParams->log2_tile_rows;
    hucInputCmd1.DynamicScalingEnabled = m_vp9Params.dynamicScalingEnabled;
    hucInputCmd1.dysVdencMultiPassEnabled = m_vp9Params.dysVdencMultiPassEnabled;

    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(HucInputCmd1G11), &hucInputCmd1, sizeof(HucInputCmd1G11));
    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->Codec = 1;
    hucCmdInitializerDmem->TargetUsage = (uint8_t)m_vp9Params.seqParams->TargetUsage;
    hucCmdInitializerDmem->FrameType = (uint8_t)m_vp9Params.picParams->PicFlags.fields.frame_type;

    // Command ID 2
    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;
}
#endif

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

    CODECHAL_ENCODE_CHK_STATUS_RETURN(CodechalCmdInitializer::CmdInitializerAllocateResources(hwInterface));

    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;

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

            // Cmd Initializer Copy Data buffer
            allocParamsForBufferLinear.dwBytes = MOS_ALIGN_CEIL(sizeof(HucComData), CODECHAL_PAGE_SIZE);
            allocParamsForBufferLinear.pBufName = "VDEnc CmdInitializer Copy Data Buffer";
            CODECHAL_ENCODE_CHK_STATUS_RETURN(
                m_osInterface->pfnAllocateResource(
                    m_osInterface,
                    &allocParamsForBufferLinear,
                    &m_cmdInitializerCopyDataBuffer[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_cmdInitializerCopyDataBuffer[i][j],
                &lockFlagsWriteOnly);
            CODECHAL_ENCODE_CHK_NULL_RETURN(pData);
            MOS_ZeroMemory(pData, allocParamsForBufferLinear.dwBytes);
            m_osInterface->pfnUnlockResource(m_osInterface, &m_cmdInitializerCopyDataBuffer[i][j]);
        }
    }

    MOS_ZeroMemory(&m_vdencCopyBatchBuffer, sizeof(m_vdencCopyBatchBuffer));
    m_vdencCopyBatchBuffer.bSecondLevel = true;
    CODECHAL_ENCODE_CHK_STATUS_RETURN(Mhw_AllocateBb(
        m_osInterface,
        &m_vdencCopyBatchBuffer,
        nullptr,
        m_hwInterface->m_vdencCopyBatchBufferSize));

    return eStatus;
}

void CodechalCmdInitializerG11::CmdInitializerFreeResources()
{
    CodechalCmdInitializer::CmdInitializerFreeResources();

    for (int i = 0; i < CODECHAL_VDENC_BRC_NUM_OF_PASSES; i++)
    {
        for (int j = 0; j < CODECHAL_ENCODE_RECYCLED_BUFFER_NUM; j++)
        {
            m_osInterface->pfnFreeResource(m_osInterface, &m_cmdInitializerCopyDmemBuffer[j][i]);
            m_osInterface->pfnFreeResource(m_osInterface, &m_cmdInitializerCopyDataBuffer[j][i]);
        }
    }

    Mhw_FreeBb(m_osInterface, &m_vdencCopyBatchBuffer, nullptr);
}

MOS_STATUS CodechalCmdInitializerG11::AddCopyCmds(
    PMOS_COMMAND_BUFFER cmdBuffer,
    HucCopyParams* params)
{
    MOS_STATUS eStatus = MOS_STATUS_SUCCESS;

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

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

    CODECHAL_ENCODE_CHK_STATUS_RETURN(SetCopyDmem());

    CODECHAL_ENCODE_CHK_STATUS_RETURN(SetCopyData(params));

    // pipe mode select
    MHW_VDBOX_PIPE_MODE_SELECT_PARAMS pipeModeSelectParams;
    CODECHAL_ENCODE_CHK_NULL_RETURN(m_encoder);
    pipeModeSelectParams.Mode = m_encoder->m_mode;
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_hwInterface->GetHucInterface()->AddHucPipeModeSelectCmd(cmdBuffer, &pipeModeSelectParams));

    // set HuC DMEM param
    MHW_VDBOX_HUC_DMEM_STATE_PARAMS dmemParams;
    MOS_ZeroMemory(&dmemParams, sizeof(dmemParams));
    dmemParams.presHucDataSource = &m_cmdInitializerCopyDmemBuffer[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_hwInterface->GetHucInterface()->AddHucDmemStateCmd(cmdBuffer, &dmemParams));

    MHW_VDBOX_HUC_VIRTUAL_ADDR_PARAMS virtualAddrParams;
    MOS_ZeroMemory(&virtualAddrParams, sizeof(virtualAddrParams));
    virtualAddrParams.regionParams[0].presRegion = &m_cmdInitializerCopyDataBuffer[m_encoder->m_currRecycledBufIdx][m_currentPass];
    virtualAddrParams.regionParams[1].presRegion = &m_vdencCopyBatchBuffer.OsResource;
    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));

    // current location to add cmds in 2nd level batch buffer
    m_vdencCopyBatchBuffer.iCurrent = 0;
    // reset starting location (offset) executing 2nd level batch buffer for each frame & each pass
    m_vdencCopyBatchBuffer.dwOffset = 0;

    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_miInterface->AddMiBatchBufferStartCmd(cmdBuffer, &m_vdencCopyBatchBuffer));

    // This wait cmd is needed to make sure copy command is done as suggested by HW folk in encode cases
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_miInterface->AddMfxWaitCmd(cmdBuffer, nullptr, m_osInterface->osCpInterface->IsCpEnabled() ? true : false));

    return eStatus;
}

MOS_STATUS CodechalCmdInitializerG11::SetCopyDmem()
{
    MOS_STATUS eStatus = MOS_STATUS_SUCCESS;

    HucComDmem* hucCmdCopyDmem;
    MOS_LOCK_PARAMS lockFlagsWriteOnly;

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

    hucCmdCopyDmem = (HucComDmem*)m_osInterface->pfnLockResource(
        m_osInterface, &m_cmdInitializerCopyDmemBuffer[m_encoder->m_currRecycledBufIdx][m_currentPass], &lockFlagsWriteOnly);

    MOS_ZeroMemory(hucCmdCopyDmem, sizeof(hucCmdCopyDmem));

    hucCmdCopyDmem->TotalOutputCommands = 1;

    hucCmdCopyDmem->FrameType = m_encoder->m_pictureCodingType - 1;
    hucCmdCopyDmem->OutputCOM[0].ID = 3;
    hucCmdCopyDmem->OutputCOM[0].Type = 1;
    hucCmdCopyDmem->OutputCOM[0].StartInBytes = 0;
    hucCmdCopyDmem->OutputCOM[0].BBEnd = BATCH_BUFFER_END;

    hucCmdCopyDmem->OutputSize = m_hwInterface->m_vdencCopyBatchBufferSize;

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

    return eStatus;
}

MOS_STATUS CodechalCmdInitializerG11::SetCopyData(
    HucCopyParams* params)
{
    MOS_STATUS eStatus = MOS_STATUS_SUCCESS;

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

    auto hucConstData = (HucComData*)m_osInterface->pfnLockResource(m_osInterface, &m_cmdInitializerCopyDataBuffer[m_encoder->m_currRecycledBufIdx][m_currentPass], &lockFlagsWriteOnly);

    MOS_ZeroMemory(hucConstData, sizeof(HucComData));
    hucConstData->TotalCommands = 1;

    hucConstData->InputCOM[0].ID = CODECHAL_CMD3;
    hucConstData->InputCOM[0].SizeOfData = 0xF;

    HucInputCmd3G11 hucInputCmd3;
    MOS_ZeroMemory(&hucInputCmd3, sizeof(HucInputCmd3G11));

    hucInputCmd3.SelectionForIndData = m_osInterface->osCpInterface->IsCpEnabled() ? 4 : 0;
    hucInputCmd3.CmdMode = CMD_LIST_MODE;
    hucInputCmd3.LengthOfTable = params->lengthOfTable;
    hucInputCmd3.CopySize = params->size;

    uint64_t srcAddr = m_osInterface->pfnGetResourceGfxAddress(m_osInterface, params->presSrc);
    uint64_t destAddr = m_osInterface->pfnGetResourceGfxAddress(m_osInterface, params->presDst);
    hucInputCmd3.SrcAddrBottom = (uint32_t)(srcAddr & 0x00000000FFFFFFFF);
    hucInputCmd3.SrcAddrTop = (uint32_t)((srcAddr & 0xFFFFFFFF00000000) >> 32);

    hucInputCmd3.DestAddrBottom = (uint32_t)(destAddr & 0x00000000FFFFFFFF);
    hucInputCmd3.DestAddrTop = (uint32_t)((destAddr & 0xFFFFFFFF00000000) >> 32);

    MOS_SecureMemcpy(hucConstData->InputCOM[0].data, sizeof(HucInputCmd3G11), &hucInputCmd3, sizeof(HucInputCmd3G11));

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

    return eStatus;
}