/*
* Copyright (c) 2017, 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_histogram_vebox.cpp
//! \brief    implements the decode histogram through vebox.
//! \details  decode histogram through vebox.
//!
#include "codechal_decode_histogram_vebox.h"
#include "mos_utilities.h"

CodechalDecodeHistogramVebox::CodechalDecodeHistogramVebox(
    CodechalHwInterface *hwInterface,
    MOS_INTERFACE *osInterface):
    CodechalDecodeHistogram(hwInterface, osInterface),
    m_veboxInterface(hwInterface->GetVeboxInterface())
{
    MOS_ZeroMemory(&m_resSyncObject, sizeof(m_resSyncObject));
    MOS_ZeroMemory(&m_resStatisticsOutput, sizeof(m_resStatisticsOutput));
    MOS_ZeroMemory(&m_outputSurface, sizeof(m_outputSurface));
    // allocate heap
    m_veboxInterface->CreateHeap();

    // create Vebox context
    MOS_GPUCTX_CREATOPTIONS  createOpts;
    m_osInterface->pfnCreateGpuContext(
        m_osInterface,
        MOS_GPU_CONTEXT_VEBOX,
        MOS_GPU_NODE_VE,
        &createOpts);

    // register Vebox GPU context with the Batch Buffer completion event
    m_osInterface->pfnRegisterBBCompleteNotifyEvent(
        m_osInterface,
        MOS_GPU_CONTEXT_VEBOX);
}

CodechalDecodeHistogramVebox::~CodechalDecodeHistogramVebox()
{
    if (!Mos_ResourceIsNull(&m_resSyncObject))
    {
        m_osInterface->pfnDestroySyncResource(m_osInterface, &m_resSyncObject);
    }
    if (!Mos_ResourceIsNull(&m_resStatisticsOutput))
    {
        m_osInterface->pfnFreeResource(m_osInterface, &m_resStatisticsOutput);
    }
    if (!Mos_ResourceIsNull(&m_outputSurface.OsResource))
    {
        m_osInterface->pfnFreeResource(m_osInterface, &m_outputSurface.OsResource);
    }
}

MOS_STATUS CodechalDecodeHistogramVebox::AllocateResources()
{
    MOS_STATUS eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_HW_FUNCTION_ENTER;

    // allocate sync object
    if (Mos_ResourceIsNull(&m_resSyncObject))
    {
        CODECHAL_DECODE_CHK_STATUS_RETURN(m_osInterface->pfnCreateSyncResource(
            m_osInterface, &m_resSyncObject));
    }

    uint32_t size = 0;
    // allocate internal histogram resource
    if (Mos_ResourceIsNull(&m_resHistogram) ||
        m_preWidth != m_inputSurface->dwWidth ||
        m_preHeight != m_inputSurface->dwHeight)
    {
        // Need to reallocate
        if (m_preWidth != m_inputSurface->dwWidth ||
            m_preHeight != m_inputSurface->dwHeight)
        {
            m_osInterface->pfnFreeResource(m_osInterface, &m_resHistogram);
        }

        m_hwInterface->GetHcpInterface()->GetOsResLaceOrAceOrRgbHistogramBufferSize(
            m_inputSurface->dwWidth,
            m_inputSurface->dwHeight,
            &size);

        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;
        allocParamsForBufferLinear.dwBytes = size;
        allocParamsForBufferLinear.pBufName = "ResLaceOrAceOrRgbHistogram";

        eStatus = m_osInterface->pfnAllocateResource(
            m_osInterface,
            &allocParamsForBufferLinear,
            &m_resHistogram);

        if (eStatus != MOS_STATUS_SUCCESS)
        {
            CODECHAL_DECODE_ASSERTMESSAGE("Failed to allocate histogram buffer.");
            return eStatus;
        }
    }

    // allocate statistics output resource to avoid Page Fault issue since HW will access it
    if (Mos_ResourceIsNull(&m_resStatisticsOutput) ||
        m_preWidth != m_inputSurface->dwWidth ||
        m_preHeight != m_inputSurface->dwHeight)
    {
        // Need to reallocate
        if (m_preWidth != m_inputSurface->dwWidth ||
            m_preHeight != m_inputSurface->dwHeight)
        {
            m_osInterface->pfnFreeResource(m_osInterface, &m_resStatisticsOutput);
        }

        m_hwInterface->GetHcpInterface()->GetOsResStatisticsOutputBufferSize(
            m_inputSurface->dwWidth,
            m_inputSurface->dwHeight,
            &size);

        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;
        allocParamsForBufferLinear.dwBytes = size;
        allocParamsForBufferLinear.pBufName = "m_resStatisticsOutput";

        eStatus = m_osInterface->pfnAllocateResource(
            m_osInterface,
            &allocParamsForBufferLinear,
            &m_resStatisticsOutput);

        if (eStatus != MOS_STATUS_SUCCESS)
        {
            CODECHAL_DECODE_ASSERTMESSAGE("Failed to allocate statistics output buffer.");
            return eStatus;
        }
    }

    // allocate vebox output surface
    if (Mos_ResourceIsNull(&m_outputSurface.OsResource) ||
        m_preWidth != m_inputSurface->dwWidth ||
        m_preHeight != m_inputSurface->dwHeight)
    {
        // Need to reallocate
        if (m_preWidth != m_inputSurface->dwWidth ||
            m_preHeight != m_inputSurface->dwHeight)
        {
            m_osInterface->pfnFreeResource(m_osInterface, &m_outputSurface.OsResource);
        }

        CODECHAL_DECODE_CHK_STATUS_MESSAGE_RETURN(m_decoder->AllocateSurface(
                                                      &m_outputSurface,
                                                      m_inputSurface->dwWidth,
                                                      m_inputSurface->dwHeight,
                                                      "VeboxOutputBuffer"),
            "Failed to allocate vebox output surface buffer.");
    }

    m_preWidth  = m_inputSurface->dwWidth;
    m_preHeight = m_inputSurface->dwHeight;

    return eStatus;
}

MOS_STATUS CodechalDecodeHistogramVebox::SetVeboxStateParams(
    PMHW_VEBOX_STATE_CMD_PARAMS veboxCmdParams)
{
    MOS_STATUS eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_HW_FUNCTION_ENTER;

    veboxCmdParams->bNoUseVeboxHeap                         = 0;

    veboxCmdParams->VeboxMode.ColorGamutExpansionEnable     = 0;
    veboxCmdParams->VeboxMode.ColorGamutCompressionEnable   = 0;
    // On SKL, GlobalIECP must be enabled when the output pipe is Vebox or SFC
    veboxCmdParams->VeboxMode.GlobalIECPEnable              = 1;
    veboxCmdParams->VeboxMode.DNEnable                      = 0;
    veboxCmdParams->VeboxMode.DIEnable                      = 0;
    veboxCmdParams->VeboxMode.DNDIFirstFrame                = 0;
    veboxCmdParams->VeboxMode.DIOutputFrames                = 0;
    veboxCmdParams->VeboxMode.PipeSynchronizeDisable        = 0;
    veboxCmdParams->VeboxMode.DemosaicEnable                = 0;
    veboxCmdParams->VeboxMode.VignetteEnable                = 0;
    veboxCmdParams->VeboxMode.AlphaPlaneEnable              = 0;
    veboxCmdParams->VeboxMode.HotPixelFilteringEnable       = 0;
    // 0-both slices enabled   1-Slice 0 enabled   2-Slice 1 enabled
    // On SKL GT3 and GT4, there are 2 Veboxes. But only Vebox0 can be used,Vebox1 cannot be used
    veboxCmdParams->VeboxMode.SingleSliceVeboxEnable        = 1;
    veboxCmdParams->VeboxMode.LACECorrectionEnable          = 0;
    veboxCmdParams->VeboxMode.DisableEncoderStatistics      = 1;
    veboxCmdParams->VeboxMode.DisableTemporalDenoiseFilter  = 1;
    veboxCmdParams->VeboxMode.SinglePipeIECPEnable          = 0;
    veboxCmdParams->VeboxMode.SFCParallelWriteEnable        = 0;
    veboxCmdParams->VeboxMode.ScalarMode                    = 0;
    veboxCmdParams->VeboxMode.ForwardGammaCorrectionEnable  = 0;

    return eStatus;
}

MOS_STATUS CodechalDecodeHistogramVebox::SetVeboxSurfaceStateParams(
    PMHW_VEBOX_SURFACE_STATE_CMD_PARAMS veboxSurfParams)
{
    MOS_STATUS eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_HW_FUNCTION_ENTER;

    // Initialize SurfInput
    veboxSurfParams->SurfInput.bActive          = true;
    veboxSurfParams->SurfInput.Format           = m_inputSurface->Format;
    veboxSurfParams->SurfInput.dwWidth          = m_inputSurface->dwWidth;
    veboxSurfParams->SurfInput.dwHeight         = m_inputSurface->UPlaneOffset.iYOffset;  // For Planar formats, pParams->SurfInput.dwHeight will be assigned to VEBOX U.Y offset, which is only used for PLANAR surface formats.
    veboxSurfParams->SurfInput.dwUYoffset       = m_inputSurface->UPlaneOffset.iYOffset;
    veboxSurfParams->SurfInput.dwPitch          = m_inputSurface->dwPitch;
    veboxSurfParams->SurfInput.TileType         = m_inputSurface->TileType;
    veboxSurfParams->SurfInput.TileModeGMM      = m_inputSurface->TileModeGMM;
    veboxSurfParams->SurfInput.bGMMTileEnabled  = m_inputSurface->bGMMTileEnabled;
    veboxSurfParams->SurfInput.pOsResource      = &m_inputSurface->OsResource;
    veboxSurfParams->SurfInput.rcMaxSrc.left    = 0;
    veboxSurfParams->SurfInput.rcMaxSrc.top     = 0;
    veboxSurfParams->SurfInput.rcMaxSrc.right =
        MOS_ALIGN_CEIL(m_inputSurface->dwWidth, MHW_SFC_VE_WIDTH_ALIGN);
    veboxSurfParams->SurfInput.rcMaxSrc.bottom =
        MOS_ALIGN_CEIL(m_inputSurface->dwHeight, MHW_SFC_VE_HEIGHT_ALIGN);

    // Initialize SurfSTMM
    veboxSurfParams->SurfSTMM.dwPitch = m_inputSurface->dwPitch;

    veboxSurfParams->bDIEnable                  = false;
    veboxSurfParams->bOutputValid               = false;

    return eStatus;
}

MOS_STATUS CodechalDecodeHistogramVebox::SetVeboxDiIecpParams(
    PMHW_VEBOX_DI_IECP_CMD_PARAMS veboxDiIecpParams)
{
    MOS_STATUS eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_HW_FUNCTION_ENTER;

    veboxDiIecpParams->dwStartingX              = 0;
    veboxDiIecpParams->dwEndingX                = m_inputSurface->dwWidth - 1;
    veboxDiIecpParams->dwCurrInputSurfOffset    = m_inputSurface->dwOffset;
    veboxDiIecpParams->pOsResCurrInput          = &m_inputSurface->OsResource;
    veboxDiIecpParams->pOsResCurrOutput         = &m_outputSurface.OsResource;
    veboxDiIecpParams->CurrInputSurfCtrl.Value  = 0;
    veboxDiIecpParams->CurrOutputSurfCtrl.Value = 0;

    CodecHalGetResourceInfo(m_osInterface, m_inputSurface);
    CodecHalGetResourceInfo(m_osInterface, &m_outputSurface);

    veboxDiIecpParams->CurInputSurfMMCState =
        (MOS_MEMCOMP_STATE)(m_inputSurface->CompressionMode);
    veboxDiIecpParams->pOsResLaceOrAceOrRgbHistogram    = &m_resHistogram;
    veboxDiIecpParams->pOsResStatisticsOutput           = &m_resStatisticsOutput;

    return eStatus;
}

MOS_STATUS CodechalDecodeHistogramVebox::SetVeboxIecpParams(
    PMHW_VEBOX_IECP_PARAMS veboxIecpParams)
{
    MOS_STATUS eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_HW_FUNCTION_ENTER;

    veboxIecpParams->ColorPipeParams.bActive    = true;
    veboxIecpParams->ColorPipeParams.bEnableACE = true;
    veboxIecpParams->AceParams.bActive          = true;
    veboxIecpParams->srcFormat                  = m_inputSurface->Format;
    veboxIecpParams->bCSCEnable                 = false;

    return eStatus;
}

MOS_STATUS CodechalDecodeHistogramVebox::RenderHistogram(
    CodechalDecode *codechalDecoder,
    MOS_SURFACE *inputSurface)
{
    MOS_STATUS eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_HW_FUNCTION_ENTER;

    if (Mos_ResourceIsNull(&m_inputHistogramSurfaces[m_histogramComponent].OsResource))
    {
        CODECHAL_DECODE_VERBOSEMESSAGE("Input histogram surface is null");
        return MOS_STATUS_INVALID_PARAMETER;
    }

    m_decoder       = codechalDecoder;
    m_inputSurface  = inputSurface;

    AllocateResources();

    MOS_SYNC_PARAMS syncParams  = g_cInitSyncParams;
    syncParams.GpuContext       = m_decoder->GetVideoContext();
    syncParams.presSyncResource = &m_resSyncObject;

    CODECHAL_DECODE_CHK_STATUS_RETURN(m_osInterface->pfnEngineSignal(
        m_osInterface,
        &syncParams));

    syncParams                  = g_cInitSyncParams;
    syncParams.GpuContext       = MOS_GPU_CONTEXT_VEBOX;
    syncParams.presSyncResource = &m_resSyncObject;

    CODECHAL_DECODE_CHK_STATUS_RETURN(m_osInterface->pfnEngineWait(
        m_osInterface,
        &syncParams));

    // Switch GPU context to VEBOX
    m_osInterface->pfnSetGpuContext(m_osInterface, MOS_GPU_CONTEXT_VEBOX);
    // Reset allocation list and house keeping
    m_osInterface->pfnResetOsStates(m_osInterface);

    // Send command buffer header at the beginning
    MOS_COMMAND_BUFFER cmdBuffer;
    MOS_ZeroMemory(&cmdBuffer, sizeof(MOS_COMMAND_BUFFER));
    CODECHAL_HW_CHK_STATUS_RETURN(m_osInterface->pfnGetCommandBuffer(
        m_osInterface,
        &cmdBuffer,
        0));
    CODECHAL_HW_CHK_STATUS_RETURN(m_decoder->SendPrologWithFrameTracking(
        &cmdBuffer,
        true));

    // Setup cmd prameters
    MHW_VEBOX_STATE_CMD_PARAMS veboxStateCmdParams;
    MOS_ZeroMemory(&veboxStateCmdParams, sizeof(veboxStateCmdParams));
    CODECHAL_HW_CHK_STATUS_RETURN(SetVeboxStateParams(&veboxStateCmdParams));

    MHW_VEBOX_SURFACE_STATE_CMD_PARAMS veboxSurfaceStateCmdParams;
    MOS_ZeroMemory(&veboxSurfaceStateCmdParams, sizeof(veboxSurfaceStateCmdParams));
    CODECHAL_HW_CHK_STATUS_RETURN(SetVeboxSurfaceStateParams(&veboxSurfaceStateCmdParams));

    MHW_VEBOX_DI_IECP_CMD_PARAMS veboxDiIecpCmdParams;
    MOS_ZeroMemory(&veboxDiIecpCmdParams, sizeof(veboxDiIecpCmdParams));
    CODECHAL_HW_CHK_STATUS_RETURN(SetVeboxDiIecpParams(&veboxDiIecpCmdParams));

    MHW_VEBOX_IECP_PARAMS veboxIecpParams;
    MOS_ZeroMemory(&veboxIecpParams, sizeof(veboxIecpParams));
    CODECHAL_HW_CHK_STATUS_RETURN(SetVeboxIecpParams(&veboxIecpParams));

    // send Vebox cmd
    CODECHAL_HW_CHK_STATUS_RETURN(m_veboxInterface->AddVeboxIecpState(
        &veboxIecpParams));

    CODECHAL_HW_CHK_STATUS_RETURN(m_veboxInterface->AddVeboxState(
        &cmdBuffer,
        &veboxStateCmdParams, 0));

    CODECHAL_HW_CHK_STATUS_RETURN(m_veboxInterface->AddVeboxSurfaces(
        &cmdBuffer,
        &veboxSurfaceStateCmdParams));

    CODECHAL_HW_CHK_STATUS_RETURN(m_veboxInterface->AddVeboxDiIecp(
        &cmdBuffer,
        &veboxDiIecpCmdParams));

    CODECHAL_DECODE_CHK_STATUS_RETURN(m_hwInterface->GetMiInterface()->AddMiBatchBufferEnd(
        &cmdBuffer,
        nullptr));

    m_osInterface->pfnReturnCommandBuffer(m_osInterface, &cmdBuffer, 0);
    CODECHAL_HW_CHK_STATUS_RETURN(m_osInterface->pfnSubmitCommandBuffer(
        m_osInterface,
        &cmdBuffer,
        m_decoder->GetVideoContextUsesNullHw()));

    m_osInterface->pfnFreeResource(
        m_osInterface,
        &veboxStateCmdParams.DummyIecpResource);

    // copy histogram to input buffer
    CODECHAL_DECODE_CHK_STATUS_RETURN(m_osInterface->pfnSetGpuContext(
        m_osInterface, m_decoder->GetVideoWAContext()));
    m_osInterface->pfnResetOsStates(m_osInterface);

    m_osInterface->pfnSetPerfTag(
        m_osInterface,
        (uint16_t)(((m_decoder->GetMode() << 4) & 0xF0) | COPY_TYPE));
    m_osInterface->pfnResetPerfBufferID(m_osInterface);

    MOS_ZeroMemory(&cmdBuffer, sizeof(MOS_COMMAND_BUFFER));
    CODECHAL_DECODE_CHK_STATUS_RETURN(m_osInterface->pfnGetCommandBuffer(
        m_osInterface,
        &cmdBuffer,
        0));

    CODECHAL_DECODE_CHK_STATUS_RETURN(m_decoder->SendPrologWithFrameTracking(
        &cmdBuffer,
        false));

    CODECHAL_DECODE_CHK_STATUS_RETURN(m_decoder->HucCopy(
        &cmdBuffer,
        &m_resHistogram,
        &m_inputHistogramSurfaces[m_histogramComponent].OsResource,
        HISTOGRAM_BINCOUNT * 4,
        m_veboxHistogramOffset,
        m_inputHistogramSurfaces[m_histogramComponent].dwOffset));

    MHW_MI_FLUSH_DW_PARAMS flushDwParams;
    MOS_ZeroMemory(&flushDwParams, sizeof(flushDwParams));
    CODECHAL_DECODE_CHK_STATUS_RETURN(m_hwInterface->GetMiInterface()->AddMiFlushDwCmd(
        &cmdBuffer,
        &flushDwParams));

    CODECHAL_DECODE_CHK_STATUS_RETURN(m_hwInterface->GetMiInterface()->AddMiBatchBufferEnd(
        &cmdBuffer,
        nullptr));

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

    // sync resource
    syncParams = g_cInitSyncParams;
    syncParams.GpuContext = MOS_GPU_CONTEXT_VEBOX;
    syncParams.presSyncResource = &m_resSyncObject;
    CODECHAL_DECODE_CHK_STATUS_RETURN(m_osInterface->pfnEngineSignal(
        m_osInterface,
        &syncParams));

    syncParams = g_cInitSyncParams;
    syncParams.GpuContext = m_decoder->GetVideoWAContext();
    syncParams.presSyncResource = &m_resSyncObject;
    CODECHAL_DECODE_CHK_STATUS_RETURN(m_osInterface->pfnEngineWait(
        m_osInterface,
        &syncParams));

    CODECHAL_DECODE_CHK_STATUS_RETURN(m_osInterface->pfnSubmitCommandBuffer(
        m_osInterface,
        &cmdBuffer,
        m_decoder->GetVideoContextUsesNullHw()));

    CODECHAL_DECODE_CHK_STATUS_RETURN(m_osInterface->pfnSetGpuContext(
        m_osInterface,
        m_decoder->GetVideoContext()));

    MOS_USER_FEATURE_VALUE_WRITE_DATA userFeatureWriteData;
    MOS_ZeroMemory(&userFeatureWriteData, sizeof(userFeatureWriteData));
    userFeatureWriteData.Value.i32Data = 1;
    userFeatureWriteData.ValueID       = __MEDIA_USER_FEATURE_VALUE_DECODE_HISTOGRAM_FROM_VEBOX_ID;
    MOS_UserFeature_WriteValues_ID(nullptr, &userFeatureWriteData, 1, m_osInterface->pOsContext);

    return eStatus;
}
