/*
* Copyright (c) 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_encode_sfc_base.cpp
//! \brief    Implements the encode interface extension for CSC via VEBox/SFC.
//! \details  Downsampling in this case is supported by the VEBox fixed function HW unit.
//!

#include "codechal_encode_sfc_base.h"
#include "codechal_encoder_base.h"
#include "hal_oca_interface.h"

#define CODECHAL_IS_BT601_CSPACE(format)                \
        ( (format == MHW_CSpace_BT601)               || \
          (format == MHW_CSpace_xvYCC601)            || \
          (format == MHW_CSpace_BT601Gray)           || \
          (format == MHW_CSpace_BT601_FullRange)     || \
          (format == MHW_CSpace_BT601Gray_FullRange) )

#define CODECHAL_IS_BT709_CSPACE(format)                \
        ( (format == MHW_CSpace_BT709)               || \
          (format == MHW_CSpace_xvYCC709)            || \
          (format == MHW_CSpace_BT709_FullRange) )

// Generic YUV to RGB conversion matrix from BT.601 standard
const float CODECHAL_CSC_BT601_YUV_RGB[9] =
{
    1.000000f,  0.000000f,  1.402000f,
    1.000000f, -0.344136f, -0.714136f,
    1.000000f,  1.772000f,  0.000000f
};

// Generic YUV to RGB conversion matrix from BT.709 standard
const float CODECHAL_CSC_BT709_YUV_RGB[9] =
{
    1.000000f,  0.000000f,  1.574800f,
    1.000000f, -0.187324f, -0.468124f,
    1.000000f,  1.855600f,  0.000000f
};

// Generic RGB to YUV conversion matrix from BT.601 standard
const float CODECHAL_CSC_BT601_RGB_YUV[9] =
{
    0.299000f,  0.587000f,  0.114000f,
   -0.168736f, -0.331264f,  0.500000f,
    0.500000f, -0.418688f, -0.081312f
};

// Generic RGB to YUV conversion matrix from BT.709 standard
const float CODECHAL_CSC_BT709_RGB_YUV[9] =
{
    0.212600f,  0.715200f,  0.072200f,
   -0.114572f, -0.385428f,  0.500000f,
    0.500000f, -0.454153f, -0.045847f
};

// BT2020 RGB to Non-constant YUV conversion matrix from R-REC-BT.2020-1-201406-I!!PDF-E.pdf
const float CODECHAL_CSC_BT2020_RGB_YUV[9] =
{
    0.262700f,  0.678000f,  0.059300f,     // Y
    -0.139630f, -0.360370f, 0.500000f,     // U
    0.500000f,  -0.459786f, -0.040214f     // V
};

// BT2020 Non-constant YUV to RGB conversion matrix from R-REC-BT.2020-1-201406-I!!PDF-E.pdf
const float CODECHAL_CSC_BT2020_YUV_RGB[9] =
{
    1.000000f, 0.000000f,  1.474600f,     //R
    1.000000f, -0.164553f, -0.571353f,    //G
    1.000000f, 1.881400f,  0.000000f      //B
};

CodecHalEncodeSfcBase::~CodecHalEncodeSfcBase()
{
    MOS_STATUS                eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_ENCODE_FUNCTION_ENTER;

    // pVeboxInterface->pfnDestroy() will be called in CodecHal_HwDestroy(), no need to destroy here

    FreeResources();
}

bool CodecHalEncodeSfcBase::IsCspace(MHW_CSPACE srcCspace, MHW_CSPACE dstCspace)
{
    switch (dstCspace)
    {
    case MHW_CSpace_RGB:
        return (srcCspace == MHW_CSpace_sRGB ||
            srcCspace == MHW_CSpace_stRGB);

    case MHW_CSpace_YUV:
        return (srcCspace == MHW_CSpace_BT709 ||
            srcCspace == MHW_CSpace_BT601 ||
            srcCspace == MHW_CSpace_BT601_FullRange ||
            srcCspace == MHW_CSpace_BT709_FullRange ||
            srcCspace == MHW_CSpace_xvYCC709 ||
            srcCspace == MHW_CSpace_xvYCC601);

    case MHW_CSpace_Gray:
        return (srcCspace == MHW_CSpace_BT601Gray ||
            srcCspace == MHW_CSpace_BT601Gray_FullRange);

    case MHW_CSpace_Any:
        return (srcCspace != MHW_CSpace_None);

    case MHW_CSpace_BT2020:
        return (srcCspace == MHW_CSpace_BT2020 ||
            srcCspace == MHW_CSpace_BT2020_FullRange);

    case MHW_CSpace_BT2020_RGB:
        return (srcCspace == MHW_CSpace_BT2020_RGB ||
            srcCspace == MHW_CSpace_BT2020_stRGB);

    default:
        return (srcCspace == dstCspace);
    }

    return false;
}

bool CodecHalEncodeSfcBase::GetRgbRangeAndOffset(
    MHW_CSPACE          srcCspace,
    float               *rgbOffset,
    float               *rgbExcursion)
{
    bool ret = true;

    switch (srcCspace)
    {
    case MHW_CSpace_sRGB:
    case MHW_CSpace_BT2020_RGB:
        *rgbOffset = 0.0f;
        *rgbExcursion = 255.0f;
        break;

    case MHW_CSpace_stRGB:
    case MHW_CSpace_BT2020_stRGB:
        *rgbOffset = 16.0f;
        *rgbExcursion = 219.0f;
        break;

    default:
        ret = false;
        break;
    }

    return ret;
}

bool CodecHalEncodeSfcBase::GetYuvRangeAndOffset(
    MHW_CSPACE          srcCspace,
    float               *lumaOffset,
    float               *lumaExcursion,
    float               *chromaZero,
    float               *chromaExcursion)
{
    bool ret = true;

    switch (srcCspace)
    {
    case MHW_CSpace_BT601_FullRange:
    case MHW_CSpace_BT709_FullRange:
    case MHW_CSpace_BT601Gray_FullRange:
    case MHW_CSpace_BT2020_FullRange:
        *lumaOffset = 0.0f;
        *lumaExcursion = 255.0f;
        *chromaZero = 128.0f;
        *chromaExcursion = 255.0f;
        break;

    case MHW_CSpace_BT601:
    case MHW_CSpace_BT709:
    case MHW_CSpace_xvYCC601: // since matrix is the same as 601, use the same range
    case MHW_CSpace_xvYCC709: // since matrix is the same as 709, use the same range
    case MHW_CSpace_BT601Gray:
    case MHW_CSpace_BT2020:
        *lumaOffset = 16.0f;
        *lumaExcursion = 219.0f;
        *chromaZero = 128.0f;
        *chromaExcursion = 224.0f;
        break;

    default:
        ret = false;
        break;
    }

    return ret;
}

bool CodecHalEncodeSfcBase::CalcYuvToRgbMatrix(
    MHW_CSPACE      srcCspace,                          // [in] YUV Color space
    MHW_CSPACE      dstCspace,                          // [in] RGB Color space
    float           *transferMatrix,                    // [in] Transfer matrix (3x3)
    float           *outMatrix)                         // [out] Conversion matrix (3x4)
{
    bool    ret = true;
    float   lumaOffset, lumaExcursion, chromaZero, chromaExcursion;
    float   rgbOffset, rgbExcursion;

    ret = GetRgbRangeAndOffset(dstCspace, &rgbOffset, &rgbExcursion);
    if (ret)
    {
        ret = GetYuvRangeAndOffset(srcCspace, &lumaOffset, &lumaExcursion, &chromaZero, &chromaExcursion);
    }
    if (ret)
    {

        // after + (3x3)(3x3)
        outMatrix[0] = transferMatrix[0] * rgbExcursion / lumaExcursion;
        outMatrix[4] = transferMatrix[3] * rgbExcursion / lumaExcursion;
        outMatrix[8] = transferMatrix[6] * rgbExcursion / lumaExcursion;
        outMatrix[1] = transferMatrix[1] * rgbExcursion / chromaExcursion;
        outMatrix[5] = transferMatrix[4] * rgbExcursion / chromaExcursion;
        outMatrix[9] = transferMatrix[7] * rgbExcursion / chromaExcursion;
        outMatrix[2] = transferMatrix[2] * rgbExcursion / chromaExcursion;
        outMatrix[6] = transferMatrix[5] * rgbExcursion / chromaExcursion;
        outMatrix[10] = transferMatrix[8] * rgbExcursion / chromaExcursion;

        // (3x1) - (3x3)(3x3)(3x1)
        outMatrix[3] = rgbOffset - (outMatrix[0] * lumaOffset + outMatrix[1] * chromaZero + outMatrix[2] * chromaZero);
        outMatrix[7] = rgbOffset - (outMatrix[4] * lumaOffset + outMatrix[5] * chromaZero + outMatrix[6] * chromaZero);
        outMatrix[11] = rgbOffset - (outMatrix[8] * lumaOffset + outMatrix[9] * chromaZero + outMatrix[10] * chromaZero);
    }
    return ret;
}

bool CodecHalEncodeSfcBase::CalcRgbToYuvMatrix(
    MHW_CSPACE      srcCspace,                      // [in] RGB Color space
    MHW_CSPACE      dstCspace,                      // [in] YUV Color space
    float           *transferMatrix,                // [in] Transfer matrix (3x3)
    float           *outMatrix)                     // [out] Conversion matrix (3x4)
{
    bool    ret = true;
    float   lumaOffset, lumaExcursion, chromaZero, chromaExcursion;
    float   rgbOffset, rgbExcursion;

    ret = GetRgbRangeAndOffset(srcCspace, &rgbOffset, &rgbExcursion);
    if (ret)
    {
        ret = GetYuvRangeAndOffset(dstCspace, &lumaOffset, &lumaExcursion, &chromaZero, &chromaExcursion);
    }
    if (ret)
    {
        // multiplication of + onwards
        outMatrix[0] = transferMatrix[0] * lumaExcursion / rgbExcursion;
        outMatrix[1] = transferMatrix[1] * lumaExcursion / rgbExcursion;
        outMatrix[2] = transferMatrix[2] * lumaExcursion / rgbExcursion;
        outMatrix[4] = transferMatrix[3] * chromaExcursion / rgbExcursion;
        outMatrix[5] = transferMatrix[4] * chromaExcursion / rgbExcursion;
        outMatrix[6] = transferMatrix[5] * chromaExcursion / rgbExcursion;
        outMatrix[8] = transferMatrix[6] * chromaExcursion / rgbExcursion;
        outMatrix[9] = transferMatrix[7] * chromaExcursion / rgbExcursion;
        outMatrix[10] = transferMatrix[8] * chromaExcursion / rgbExcursion;

        // before +
        outMatrix[3] = lumaOffset - lumaExcursion * rgbOffset / rgbExcursion;
        outMatrix[7] = chromaZero;
        outMatrix[11] = chromaZero;
    }
    return ret;
}

void CodecHalEncodeSfcBase::GetCSCMatrix(
    MHW_CSPACE          srcCspace,                      // [in] Source Color space
    MHW_CSPACE          dstCspace,                      // [in] Destination Color space
    float               *cscMatrix)                     // [out] CSC matrix to use
{
    int32_t         i;

    // BT601/709 YUV to sRGB/stRGB conversion
    if (IsCspace(srcCspace, MHW_CSpace_YUV))
    {
        if(IsCspace(dstCspace, MHW_CSpace_RGB))
        {
            if (CODECHAL_IS_BT601_CSPACE(srcCspace))
            {
                CalcYuvToRgbMatrix(srcCspace, dstCspace, (float *) CODECHAL_CSC_BT601_YUV_RGB, cscMatrix);
            }
            else // if (IS_BT709_CSPACE(srcCspace))
            {
                CalcYuvToRgbMatrix(srcCspace, dstCspace, (float *) CODECHAL_CSC_BT709_YUV_RGB, cscMatrix);
            }
        }
    }
    // sRGB/stRGB to BT601/709 YUV conversion
    else if (IsCspace(srcCspace, MHW_CSpace_RGB))
    {
        if (IsCspace(dstCspace, MHW_CSpace_YUV))
        {
            if (CODECHAL_IS_BT601_CSPACE(dstCspace))
            {
                CalcRgbToYuvMatrix(srcCspace, dstCspace, (float *)CODECHAL_CSC_BT601_RGB_YUV, cscMatrix);
            }
            else // if (IS_BT709_CSPACE(srcCspace))
            {
                CalcRgbToYuvMatrix(srcCspace, dstCspace, (float *)CODECHAL_CSC_BT709_RGB_YUV, cscMatrix);
            }
        }
    }
    // BT2020 YUV to RGB conversion
    else if (IsCspace(srcCspace, MHW_CSpace_BT2020))
    {
        if (IsCspace(dstCspace, MHW_CSpace_BT2020_RGB))
        {
            CalcYuvToRgbMatrix(srcCspace, dstCspace, (float *)CODECHAL_CSC_BT2020_YUV_RGB, cscMatrix);
        }
    }
    // BT2020 RGB to YUV conversion
    else if (IsCspace(srcCspace, MHW_CSpace_BT2020_RGB))
    {
        if (IsCspace(dstCspace, MHW_CSpace_BT2020))
        {
            CalcRgbToYuvMatrix(srcCspace, dstCspace, (float *)CODECHAL_CSC_BT2020_RGB_YUV, cscMatrix);
        }
    }
    else
    {
        CODECHAL_ENCODE_ASSERTMESSAGE("Not supported color space conversion(from %d to %d)", srcCspace, dstCspace);
    }

    CODECHAL_ENCODE_NORMALMESSAGE("");
    for(i = 0; i < 3; i++)
    {
        CODECHAL_ENCODE_NORMALMESSAGE("%f\t%f\t%f\t%f",
                            cscMatrix[4 * i],
                            cscMatrix[4 * i + 1],
                            cscMatrix[4 * i + 2],
                            cscMatrix[4 * i + 3]);
    }
}

void CodecHalEncodeSfcBase::GetCscMatrix(
    MHW_CSPACE             srcCspace,                                    // [in] Source Cspace
    MHW_CSPACE             dstCspace,                                    // [in] Destination Cspace
    float                  *cscCoeff,                                    // [out] [3x3] Coefficients matrix
    float                  *cscInOffset,                                 // [out] [3x1] Input Offset matrix
    float                  *cscOutOffset)                                // [out] [3x1] Output Offset matrix
{
    float   cscMatrix[12]{};
    int32_t i;

    GetCSCMatrix(
        srcCspace,
        dstCspace,
        cscMatrix);

    // Copy [3x3] into Coeff
    for (i = 0; i < 3; i++)
    {
        MOS_SecureMemcpy(
            &cscCoeff[i*3],
            sizeof(float) * 3,
            &cscMatrix[i*4],
            sizeof(float) * 3);
    }

    // Get the input offsets
    switch(srcCspace)
    {
        case MHW_CSpace_BT601:
        case MHW_CSpace_BT601Gray:
        case MHW_CSpace_xvYCC601:
        case MHW_CSpace_BT709:
        case MHW_CSpace_xvYCC709:
            cscInOffset[0] =  -16.0F;
            cscInOffset[1] = -128.0F;
            cscInOffset[2] = -128.0F;
            break;

        case MHW_CSpace_BT601_FullRange:
        case MHW_CSpace_BT601Gray_FullRange:
        case MHW_CSpace_BT709_FullRange:
            cscInOffset[0] =    0.0F;
            cscInOffset[1] = -128.0F;
            cscInOffset[2] = -128.0F;
            break;

        case MHW_CSpace_sRGB:
            cscInOffset[0] = 0.0F;
            cscInOffset[1] = 0.0F;
            cscInOffset[2] = 0.0F;
            break;

        case MHW_CSpace_stRGB:
            cscInOffset[0] = -16.0F;
            cscInOffset[1] = -16.0F;
            cscInOffset[2] = -16.0F;
            break;

        //BT2020 YUV->RGB
        case MHW_CSpace_BT2020:
            cscInOffset[0] = -16.0F;
            cscInOffset[1] = -128.0F;
            cscInOffset[2] = -128.0F;
            break;

        case MHW_CSpace_BT2020_FullRange:
            cscInOffset[0] =    0.0F;
            cscInOffset[1] = -128.0F;
            cscInOffset[2] = -128.0F;
            break;

        //BT2020 RGB->YUV
        case MHW_CSpace_BT2020_RGB:
            cscInOffset[0] = 0.0F;
            cscInOffset[1] = 0.0F;
            cscInOffset[2] = 0.0F;
            break;

        //BT2020 RGB->YUV
        case MHW_CSpace_BT2020_stRGB:
            cscInOffset[0] = -16.0F;
            cscInOffset[1] = -16.0F;
            cscInOffset[2] = -16.0F;
            break;

        default:
            CODECHAL_PUBLIC_ASSERTMESSAGE("Unsupported Input ColorSpace for Vebox.");
    }

    // Get the output offsets
    switch(dstCspace)
    {
        case MHW_CSpace_BT601:
        case MHW_CSpace_BT601Gray:
        case MHW_CSpace_xvYCC601:
        case MHW_CSpace_BT709:
        case MHW_CSpace_xvYCC709:
            cscOutOffset[0] =  16.0F;
            cscOutOffset[1] = 128.0F;
            cscOutOffset[2] = 128.0F;
            break;

        case MHW_CSpace_BT601_FullRange:
        case MHW_CSpace_BT601Gray_FullRange:
        case MHW_CSpace_BT709_FullRange:
            cscOutOffset[0] =   0.0F;
            cscOutOffset[1] = 128.0F;
            cscOutOffset[2] = 128.0F;
            break;

        case MHW_CSpace_sRGB:
            cscOutOffset[0] = 0.0F;
            cscOutOffset[1] = 0.0F;
            cscOutOffset[2] = 0.0F;
            break;

        case MHW_CSpace_stRGB:
            cscOutOffset[0] = 16.0F;
            cscOutOffset[1] = 16.0F;
            cscOutOffset[2] = 16.0F;
            break;

        //BT2020 RGB->YUV
        case MHW_CSpace_BT2020:
            cscOutOffset[0] = 16.0F;
            cscOutOffset[1] = 128.0F;
            cscOutOffset[2] = 128.0F;
            break;

        case MHW_CSpace_BT2020_FullRange:
            cscOutOffset[0] =   0.0F;
            cscOutOffset[1] = 128.0F;
            cscOutOffset[2] = 128.0F;
            break;

        case MHW_CSpace_BT2020_RGB:
            cscOutOffset[0] = 0.0F;
            cscOutOffset[1] = 0.0F;
            cscOutOffset[2] = 0.0F;
            break;

        case MHW_CSpace_BT2020_stRGB:
            cscOutOffset[0] = 16.0F;
            cscOutOffset[1] = 16.0F;
            cscOutOffset[2] = 16.0F;
            break;

        default:
            CODECHAL_PUBLIC_ASSERTMESSAGE("Unsupported Output ColorSpace for Vebox.");
    }
}

inline int32_t CodecHalEncodeSfcBase::GetAvsLineBufferSize() const
{
    if (!m_inputSurface || m_inputSurface->dwHeight == 0)
    {
        CODECHAL_ENCODE_ASSERTMESSAGE("Can't calculate buffer size! m_inputSurface == nullptr or dwHeight == 0");
        return 0;
    }
    return MOS_ROUNDUP_DIVIDE(m_inputSurface->dwHeight, 8) * CODECHAL_SFC_AVS_LINEBUFFER_SIZE_PER_VERTICAL_PIXEL;
}

inline int32_t CodecHalEncodeSfcBase::GetStatisticsOutputBufferSize() const
{
    if (!m_inputSurface || m_inputSurface->dwHeight == 0 || m_inputSurface->dwWidth == 0)
    {
        CODECHAL_ENCODE_ASSERTMESSAGE("Can't calculate buffer size! m_inputSurface == nullptr or dwHeight == 0 or dwWidth == 0");
        return 0;
    }
    return MOS_ALIGN_CEIL(m_inputSurface->dwWidth, 64) *
           (MOS_ROUNDUP_DIVIDE(m_inputSurface->dwHeight, 4) + MOS_ROUNDUP_DIVIDE(GetSfcVeboxStatisticsSize() * sizeof(uint32_t), m_inputSurface->dwWidth));
}

inline int32_t CodecHalEncodeSfcBase::GetVeboxRgbHistogramSize() const
{
    return CODECHAL_SFC_VEBOX_ACE_HISTOGRAM_SIZE_PER_FRAME_PER_SLICE *
           CODECHAL_SFC_NUM_RGB_CHANNEL *
           GetVeboxMaxSlicesNum();
}

inline int32_t CodecHalEncodeSfcBase::GetResLaceOrAceOrRgbHistogramBufferSize() const
{
    if (!m_inputSurface || m_inputSurface->dwHeight == 0 || m_inputSurface->dwWidth == 0)
    {
        CODECHAL_ENCODE_ASSERTMESSAGE("Can't calculate buffer size! m_inputSurface == nullptr or dwHeight == 0 or dwWidth == 0");
        return 0;
    }

    int32_t sizeLace = MOS_ROUNDUP_DIVIDE(m_inputSurface->dwHeight, 64) *
        MOS_ROUNDUP_DIVIDE(m_inputSurface->dwWidth, 64) *
        CODECHAL_SFC_VEBOX_LACE_HISTOGRAM_256_BIN_PER_BLOCK;

    int32_t sizeNoLace = CODECHAL_SFC_VEBOX_ACE_HISTOGRAM_SIZE_PER_FRAME_PER_SLICE *
        CODECHAL_SFC_NUM_FRAME_PREVIOUS_CURRENT *
        GetVeboxMaxSlicesNum();

    return GetVeboxRgbHistogramSize() + GetVeboxRgbAceHistogramSizeReserved() + MOS_MAX(sizeLace, sizeNoLace);
}

MOS_STATUS CodecHalEncodeSfcBase::AllocateResources()
{
    MOS_ALLOC_GFXRES_PARAMS    allocParamsForBufferLinear;
    uint32_t                   ycoeffTableSize;
    uint32_t                   uvcoeffTableSize;
    int32_t                    size;
    char*                      ptr;
    MOS_STATUS                 eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_ENCODE_FUNCTION_ENTER;

    CODECHAL_ENCODE_CHK_NULL_RETURN(m_osInterface);

    // Allocate AVS line buffer
    if (Mos_ResourceIsNull(&m_resAvsLineBuffer))
    {
        size = GetAvsLineBufferSize();
        CODECHAL_ENCODE_CHK_COND_RETURN(size == 0, "Invalid buffer size");
        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 = "SfcAvsLineBuffer";

        CODECHAL_ENCODE_CHK_STATUS_RETURN(m_osInterface->pfnAllocateResource(
            m_osInterface,
            &allocParamsForBufferLinear,
            &m_resAvsLineBuffer));
    }

    //Initialize AVS parameters, try to do once
    if (m_scaling && !m_avsParams.piYCoefsX)
    {
        m_avsParams.Format    = Format_None;
        m_avsParams.fScaleX   = 0.0F;
        m_avsParams.fScaleY   = 0.0F;
        m_avsParams.piYCoefsX = nullptr;

        ycoeffTableSize  = POLYPHASE_Y_COEFFICIENT_TABLE_SIZE_G9;
        uvcoeffTableSize = POLYPHASE_UV_COEFFICIENT_TABLE_SIZE_G9;

        size = (ycoeffTableSize + uvcoeffTableSize) * 2;

        ptr = (char*)MOS_AllocAndZeroMemory(size);
        CODECHAL_ENCODE_CHK_NULL_RETURN(ptr);

        m_avsParams.piYCoefsX  = (int32_t*)ptr;

        ptr += ycoeffTableSize;
        m_avsParams.piUVCoefsX = (int32_t*)ptr;

        ptr += uvcoeffTableSize;
        m_avsParams.piYCoefsY  = (int32_t*)ptr;

        ptr += ycoeffTableSize;
        m_avsParams.piUVCoefsY = (int32_t*)ptr;
    }

    return eStatus;
}

MOS_STATUS CodecHalEncodeSfcBase::FreeResources()
{
    MOS_STATUS                  eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_ENCODE_FUNCTION_ENTER;

    CODECHAL_ENCODE_CHK_NULL_RETURN(m_osInterface);

    // Free AVS Line Buffer
    m_osInterface->pfnFreeResource(m_osInterface, &m_resAvsLineBuffer);

    // Free resLaceOrAceOrRgbHistogram
    m_osInterface->pfnFreeResource(m_osInterface, &m_resLaceOrAceOrRgbHistogram);

    // Free resStatisticsOutput
    m_osInterface->pfnFreeResource(m_osInterface, &m_resStatisticsOutput);

    // Free buffers in AVS parameters
    MOS_FreeMemory(m_avsParams.piYCoefsX);
    m_avsParams.piYCoefsX = nullptr;

    return eStatus;
}

MOS_STATUS CodecHalEncodeSfcBase::SetVeboxStateParams(
    PMHW_VEBOX_STATE_CMD_PARAMS         params)
{
    MOS_STATUS                  eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_ENCODE_FUNCTION_ENTER;

    CODECHAL_ENCODE_CHK_NULL_RETURN(params);

    params->bNoUseVeboxHeap                         = 0;

    params->VeboxMode.ColorGamutExpansionEnable     = 0;
    params->VeboxMode.ColorGamutCompressionEnable   = 0;
    // On SKL, GlobalIECP must be enabled when the output pipe is Vebox or SFC
    params->VeboxMode.GlobalIECPEnable              = 1;
    params->VeboxMode.DNEnable                      = 0;
    params->VeboxMode.DIEnable                      = 0;
    params->VeboxMode.DNDIFirstFrame                = 0;
    params->VeboxMode.DIOutputFrames                = 0;
    params->VeboxMode.PipeSynchronizeDisable        = 0;
    params->VeboxMode.DemosaicEnable                = 0;
    params->VeboxMode.VignetteEnable                = 0;
    params->VeboxMode.AlphaPlaneEnable              = 0;
    params->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
    params->VeboxMode.SingleSliceVeboxEnable        = 1;
    params->VeboxMode.LACECorrectionEnable          = 0;
    params->VeboxMode.DisableEncoderStatistics      = 1;
    params->VeboxMode.DisableTemporalDenoiseFilter  = 1;
    params->VeboxMode.SinglePipeIECPEnable          = 0;
    params->VeboxMode.SFCParallelWriteEnable        = 0;
    params->VeboxMode.ScalarMode                    = 0;
    params->VeboxMode.ForwardGammaCorrectionEnable  = 0;

    return eStatus;
}

MOS_STATUS CodecHalEncodeSfcBase::SetVeboxSurfaceStateParams(
    PMHW_VEBOX_SURFACE_STATE_CMD_PARAMS         params)
{
    MOS_STATUS                  eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_ENCODE_FUNCTION_ENTER;

    CODECHAL_ENCODE_CHK_NULL_RETURN(params);

    // Initialize SurfInput
    params->SurfInput.bActive         = true;
    params->SurfInput.Format          = m_inputSurface->Format;
    params->SurfInput.dwWidth         = m_inputSurface->dwWidth;
    params->SurfInput.dwHeight        = m_inputSurface->dwHeight;
    params->SurfInput.dwPitch         = m_inputSurface->dwPitch;
    params->SurfInput.TileType        = m_inputSurface->TileType;
    params->SurfInput.TileModeGMM     = m_inputSurface->TileModeGMM;
    params->SurfInput.bGMMTileEnabled = m_inputSurface->bGMMTileEnabled;
    params->SurfInput.dwYoffset       = m_inputSurface->YPlaneOffset.iYOffset;
    params->SurfInput.pOsResource     = &m_inputSurface->OsResource;

    auto& rcMaxSrc  = params->SurfInput.rcMaxSrc;
    rcMaxSrc.left   = m_inputSurfaceRegion.X;
    rcMaxSrc.top    = m_inputSurfaceRegion.Y;
    rcMaxSrc.right  = m_inputSurfaceRegion.X + m_inputSurfaceRegion.Width;
    rcMaxSrc.bottom = m_inputSurfaceRegion.Y + m_inputSurfaceRegion.Height;
    CODECHAL_ENCODE_CHK_COND_RETURN(rcMaxSrc.right < MHW_VEBOX_MIN_WIDTH || rcMaxSrc.bottom < MHW_VEBOX_MIN_HEIGHT,
        "Invalid VEBOX input surface description. Minimal W x H: %d x %d; Requested W x H: %d x %d", MHW_VEBOX_MIN_WIDTH, MHW_VEBOX_MIN_HEIGHT, rcMaxSrc.right, rcMaxSrc.bottom);

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

    params->bDIEnable                        = false;
    params->bOutputValid                     = (m_veboxOutputSurface != nullptr) ? true : false;

    return eStatus;
}

MOS_STATUS CodecHalEncodeSfcBase::SetVeboxDiIecpParams(
    PMHW_VEBOX_DI_IECP_CMD_PARAMS         params)
{
    MOS_ALLOC_GFXRES_PARAMS allocParamsForBufferLinear;
    int32_t                 size;
    MOS_STATUS              eStatus = MOS_STATUS_SUCCESS;
    MOS_MEMCOMP_STATE       mmcMode = MOS_MEMCOMP_DISABLED;

    CODECHAL_ENCODE_FUNCTION_ENTER;
    CODECHAL_ENCODE_CHK_NULL_RETURN(params);

    params->dwStartingX             = 0;
    params->dwEndingX               = m_inputSurfaceRegion.Width - 1; // Must be taken from SPS
    params->dwCurrInputSurfOffset   = m_inputSurface->dwOffset;
    params->pOsResCurrInput         = &m_inputSurface->OsResource;
    params->CurrInputSurfCtrl.Value = 0;  //Keep it here untill VPHAL moving to new CMD definition and remove this parameter definition.
    CODECHAL_ENCODE_CHK_COND_RETURN(m_inputSurfaceRegion.Width < MHW_VEBOX_MIN_WIDTH,
        "Invalid VEBOX_DI_IECP.EndingX value. It must be greater or equal to %d", MHW_VEBOX_MIN_WIDTH);

    CodecHalGetResourceInfo(
        m_osInterface,
        m_inputSurface);

    m_osInterface->pfnGetMemoryCompressionMode(m_osInterface, &m_inputSurface->OsResource, &mmcMode);
    if (mmcMode &&
        (m_inputSurface->TileType == MOS_TILE_Y ||
         m_inputSurface->TileType == MOS_TILE_YS))
    {
        m_inputSurface->bCompressible = true;
        m_inputSurface->CompressionMode = (MOS_RESOURCE_MMC_MODE)mmcMode;
    }
    else
    {
        m_inputSurface->CompressionMode = MOS_MMC_DISABLED;
    }

    params->CurInputSurfMMCState = (MOS_MEMCOMP_STATE)(m_inputSurface->CompressionMode);

    // Allocate Resource to avoid Page Fault issue since HW will access it
    if (Mos_ResourceIsNull(&m_resLaceOrAceOrRgbHistogram))
    {
        size = GetResLaceOrAceOrRgbHistogramBufferSize();
        CODECHAL_ENCODE_CHK_COND_RETURN(size == 0, "Invalid buffer size");
        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";

        m_osInterface->pfnAllocateResource(
            m_osInterface,
            &allocParamsForBufferLinear,
            &m_resLaceOrAceOrRgbHistogram);
    }

    params->pOsResLaceOrAceOrRgbHistogram = &m_resLaceOrAceOrRgbHistogram;

    // Allocate Resource to avoid Page Fault issue since HW will access it
    if (Mos_ResourceIsNull(&m_resStatisticsOutput))
    {
        size = GetStatisticsOutputBufferSize();
        CODECHAL_ENCODE_CHK_COND_RETURN(size == 0, "Invalid buffer size");
        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 = "ResStatisticsOutput";

        m_osInterface->pfnAllocateResource(
            m_osInterface,
            &allocParamsForBufferLinear,
            &m_resStatisticsOutput);
    }

    params->pOsResStatisticsOutput = &m_resStatisticsOutput;

    return eStatus;
}

MOS_STATUS CodecHalEncodeSfcBase::VeboxSetIecpParams(
    PMHW_VEBOX_IECP_PARAMS         mhwVeboxIecpParams)
{
    // Calculate matrix if not done so before. CSC is expensive!
    if ((m_cscInputCspace != m_inputSurfaceColorSpace) ||
        (m_cscOutputCspace != m_outputSurfaceColorSpace))
    {
        float       fTemp[3];

        // Get the matrix to use for conversion
        GetCscMatrix(
            m_inputSurfaceColorSpace,
            m_outputSurfaceColorSpace,
            m_cscCoeff,
            m_cscInOffset,
            m_cscOutOffset);

        // Vebox CSC converts RGB input to YUV for SFC
        // Vebox only supports A8B8G8R8 input, swap the 1st and 3rd
        // columns of the transfer matrix for A8R8G8B8 and X8R8G8B8
        // This only happens when SFC output is used
        if ((m_inputSurface->Format == Format_A8R8G8B8) ||
            (m_inputSurface->Format == Format_X8R8G8B8))
        {
            fTemp[0] = m_cscCoeff[0];
            fTemp[1] = m_cscCoeff[3];
            fTemp[2] = m_cscCoeff[6];

            m_cscCoeff[0] = m_cscCoeff[2];
            m_cscCoeff[3] = m_cscCoeff[5];
            m_cscCoeff[6] = m_cscCoeff[8];

            m_cscCoeff[2] = fTemp[0];
            m_cscCoeff[5] = fTemp[1];
            m_cscCoeff[8] = fTemp[2];
        }
    }
    // Store it for next BLT
    m_cscInputCspace   = m_inputSurfaceColorSpace;
    m_cscOutputCspace  = m_outputSurfaceColorSpace;
    CODECHAL_ENCODE_VERBOSEMESSAGE("Input color space: %d, output color space: %d",
                                        m_cscInputCspace, m_cscOutputCspace);

    // copy into MHW parameters

    mhwVeboxIecpParams->ColorSpace          = m_inputSurfaceColorSpace;
    mhwVeboxIecpParams->dstFormat           = m_sfcOutputSurface->Format;
    mhwVeboxIecpParams->srcFormat           = m_inputSurface->Format;
    mhwVeboxIecpParams->bCSCEnable          = m_veboxCsc;
    mhwVeboxIecpParams->pfCscCoeff          = m_cscCoeff;
    mhwVeboxIecpParams->pfCscInOffset       = m_cscInOffset;
    mhwVeboxIecpParams->pfCscOutOffset      = m_cscOutOffset;
    //mhwVeboxIecpParams->bAlphaEnable      = m_alphaEnable;    ??
    //mhwVeboxIecpParams->wAlphaValue       = m_alphaValue;

    return MOS_STATUS_SUCCESS;
}

#if (_DEBUG || _RELEASE_INTERNAL)
MOS_STATUS CodecHalEncodeSfcBase::DumpBuffers(CodechalDebugInterface* debugInterface)
{
    CODECHAL_ENCODE_CHK_STATUS_RETURN(debugInterface->DumpBuffer(
        &m_resAvsLineBuffer,
        CodechalDbgAttr::attrSfcBuffers,
        "AvsLine",
        GetAvsLineBufferSize()));

    CODECHAL_ENCODE_CHK_STATUS_RETURN(debugInterface->DumpBuffer(
        &m_resLaceOrAceOrRgbHistogram,
        CodechalDbgAttr::attrSfcBuffers,
        "LaceOrAceOrRgbHistogram",
        GetResLaceOrAceOrRgbHistogramBufferSize()));

    CODECHAL_ENCODE_CHK_STATUS_RETURN(debugInterface->DumpBuffer(
        &m_resStatisticsOutput,
        CodechalDbgAttr::attrSfcBuffers,
        "StatisticsOutput",
        GetStatisticsOutputBufferSize()));

    return MOS_STATUS_SUCCESS;
}
#endif

MOS_STATUS CodecHalEncodeSfcBase::SetSfcStateParams(
    PMHW_SFC_INTERFACE             sfcInterface,
    PMHW_SFC_STATE_PARAMS          params,
    PMHW_SFC_OUT_SURFACE_PARAMS    outSurfaceParams)
{
    uint16_t                    widthAlignUnit;
    uint16_t                    heightAlignUnit;
    MOS_STATUS                  eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_ENCODE_FUNCTION_ENTER;

    CODECHAL_ENCODE_CHK_NULL_RETURN(sfcInterface);
    CODECHAL_ENCODE_CHK_NULL_RETURN(m_inputSurface);
    CODECHAL_ENCODE_CHK_NULL_RETURN(m_sfcOutputSurface);
    CODECHAL_ENCODE_CHK_NULL_RETURN(params);

    params->sfcPipeMode                    = MEDIASTATE_SFC_PIPE_VE_TO_SFC;
    params->dwAVSFilterMode                = MEDIASTATE_SFC_AVS_FILTER_8x8;
    params->dwVDVEInputOrderingMode        = MEDIASTATE_SFC_INPUT_ORDERING_VE_4x8;

    params->dwInputChromaSubSampling = MEDIASTATE_SFC_CHROMA_SUBSAMPLING_444;

    // Adjust SFC input surface alignment.
    // As VEBOX doesn't do scaling, input size equals to output size
    // For the VEBOX output to SFC, width is multiple of 16 and height is multiple of 4
    widthAlignUnit                         = sfcInterface->m_veWidthAlignment;
    heightAlignUnit                        = sfcInterface->m_veHeightAlignment;

    // Width and height must be set using SPS params instead of real surface size
    if (m_inputSurface->dwWidth < m_inputSurfaceRegion.Width ||
        m_inputSurface->dwHeight < m_inputSurfaceRegion.Height)
    {
        CODECHAL_ENCODE_ASSERTMESSAGE("width and height must be less than or equal to surface size");
        return MOS_STATUS_INVALID_PARAMETER;
    }
    params->dwInputFrameWidth              = MOS_ALIGN_CEIL(m_inputSurfaceRegion.Width, widthAlignUnit);
    params->dwInputFrameHeight             = MOS_ALIGN_CEIL(m_inputSurfaceRegion.Height, heightAlignUnit);

    params->dwChromaDownSamplingMode       = MEDIASTATE_SFC_CHROMA_DOWNSAMPLING_444TO420;
    params->bAVSChromaUpsamplingEnable     = m_scaling;

    if ((params->fAVSXScalingRatio > 1.0F) || (params->fAVSYScalingRatio > 1.0F))
    {
        params->bBypassXAdaptiveFilter     = false;
        params->bBypassYAdaptiveFilter     = false;
    }
    else
    {
        params->bBypassXAdaptiveFilter     = true;
        params->bBypassYAdaptiveFilter     = true;
    }

    params->fChromaSubSamplingXSiteOffset  = 0.0F;
    params->fChromaSubSamplingYSiteOffset  = 0.0F;

    widthAlignUnit  = 1;
    heightAlignUnit = 1;

    switch(m_sfcOutputSurface->Format)
    {
        case Format_NV12:
        case Format_P010:
            widthAlignUnit     = 2;
            heightAlignUnit    = 2;
            break;
        case Format_YUY2:
        case Format_UYVY:
            widthAlignUnit     = 2;
            break;
        default:
            break;
    }

    // Default to Horizontal Left, Vertical Top
    params->dwChromaDownSamplingHorizontalCoef = (m_chromaSiting & MHW_CHROMA_SITING_HORZ_CENTER) ?
                                                  MEDIASTATE_SFC_CHROMA_DOWNSAMPLING_COEF_4_OVER_8 :
                                                  ((m_chromaSiting & MHW_CHROMA_SITING_HORZ_RIGHT) ?
                                                  MEDIASTATE_SFC_CHROMA_DOWNSAMPLING_COEF_8_OVER_8 :
                                                  MEDIASTATE_SFC_CHROMA_DOWNSAMPLING_COEF_0_OVER_8);

    params->dwChromaDownSamplingVerticalCoef =   (m_chromaSiting  & MHW_CHROMA_SITING_VERT_CENTER) ?
                                                  MEDIASTATE_SFC_CHROMA_DOWNSAMPLING_COEF_4_OVER_8 :
                                                  ((m_chromaSiting & MHW_CHROMA_SITING_VERT_BOTTOM) ?
                                                  MEDIASTATE_SFC_CHROMA_DOWNSAMPLING_COEF_8_OVER_8 :
                                                  MEDIASTATE_SFC_CHROMA_DOWNSAMPLING_COEF_0_OVER_8);

    outSurfaceParams->dwWidth              = m_sfcOutputSurface->dwWidth;
    outSurfaceParams->dwHeight             = m_sfcOutputSurface->dwHeight;
    outSurfaceParams->dwPitch              = m_sfcOutputSurface->dwPitch;
    outSurfaceParams->TileType             = m_sfcOutputSurface->TileType;
    outSurfaceParams->TileModeGMM          = m_sfcOutputSurface->TileModeGMM;
    outSurfaceParams->bGMMTileEnabled      = m_sfcOutputSurface->bGMMTileEnabled;
    outSurfaceParams->ChromaSiting         = m_chromaSiting;
    outSurfaceParams->dwUYoffset           = m_sfcOutputSurface->UPlaneOffset.iYOffset;

    params->dwOutputFrameWidth             = MOS_ALIGN_CEIL(m_sfcOutputSurface->dwWidth, widthAlignUnit);
    params->dwOutputFrameHeight            = MOS_ALIGN_CEIL(m_sfcOutputSurface->dwHeight, heightAlignUnit);
    params->OutputFrameFormat              = m_sfcOutputSurface->Format;
    params->dwOutputSurfaceOffset          = m_sfcOutputSurface->dwOffset;
    params->pOsResOutputSurface            = &m_sfcOutputSurface->OsResource;
    params->pOsResAVSLineBuffer            = &m_resAvsLineBuffer;

    params->dwSourceRegionHeight           = MOS_ALIGN_FLOOR(m_inputSurfaceRegion.Height, heightAlignUnit);
    params->dwSourceRegionWidth            = MOS_ALIGN_FLOOR(m_inputSurfaceRegion.Width, widthAlignUnit);
    params->dwSourceRegionVerticalOffset   = MOS_ALIGN_CEIL(m_inputSurfaceRegion.Y, heightAlignUnit);
    params->dwSourceRegionHorizontalOffset = MOS_ALIGN_CEIL(m_inputSurfaceRegion.X, widthAlignUnit);
    params->dwScaledRegionHeight           = MOS_UF_ROUND(m_scaleY * params->dwSourceRegionHeight);
    params->dwScaledRegionWidth            = MOS_UF_ROUND(m_scaleX * params->dwSourceRegionWidth);
    params->dwScaledRegionVerticalOffset   = MOS_ALIGN_FLOOR(m_outputSurfaceRegion.Y, heightAlignUnit);
    params->dwScaledRegionHorizontalOffset = MOS_ALIGN_FLOOR(m_outputSurfaceRegion.X, widthAlignUnit);
    params->fAVSXScalingRatio              = m_scaleX;
    params->fAVSYScalingRatio              = m_scaleY;

    params->fAlphaPixel                    = 1.0F;
    params->bColorFillEnable               = m_colorFill;
    params->bCSCEnable                     = m_CSC;

    // ARGB8,ABGR10,A16B16G16R16,VYUY and YVYU output format need to enable swap
    if (m_sfcOutputSurface->Format == Format_X8R8G8B8     ||
        m_sfcOutputSurface->Format == Format_A8R8G8B8     ||
        m_sfcOutputSurface->Format == Format_R10G10B10A2  ||
        m_sfcOutputSurface->Format == Format_A16B16G16R16 ||
        m_sfcOutputSurface->Format == Format_VYUY         ||
        m_sfcOutputSurface->Format == Format_YVYU)
    {
        params->bRGBASwapEnable = true;
    }
    else
    {
        params->bRGBASwapEnable = false;
    }


    // CodecHal does not support SFC rotation
    params->RotationMode                   = MHW_ROTATION_IDENTITY;

    // For downsampling, expect output surface to be MMC disabled
    // For Jpeg, the only usage is CSC and the output surface format is RGB8, so also disable MMC
    params->bMMCEnable                     = false;
    params->MMCMode                        = MOS_MMC_DISABLED;

    return eStatus;
}

MOS_STATUS CodecHalEncodeSfcBase::SetSfcAvsStateParams(
    PMHW_SFC_INTERFACE             sfcInterface)
{
    MOS_STATUS                  eStatus = MOS_STATUS_SUCCESS;
    PMHW_SFC_AVS_STATE          mhwSfcAvsState;

    CODECHAL_ENCODE_FUNCTION_ENTER;

    CODECHAL_ENCODE_CHK_NULL_RETURN(sfcInterface);
    CODECHAL_ENCODE_CHK_NULL_RETURN(m_inputSurface);

    mhwSfcAvsState = &m_avsState;

    if (m_chromaSiting == MHW_CHROMA_SITING_NONE)
    {
        m_chromaSiting = MHW_CHROMA_SITING_HORZ_LEFT | MHW_CHROMA_SITING_VERT_CENTER;
    }

    mhwSfcAvsState->dwInputHorizontalSiting = (m_chromaSiting & MHW_CHROMA_SITING_HORZ_CENTER) ? SFC_AVS_INPUT_SITING_COEF_4_OVER_8 :
                                               ((m_chromaSiting & MHW_CHROMA_SITING_HORZ_RIGHT) ? SFC_AVS_INPUT_SITING_COEF_8_OVER_8 :
                                               SFC_AVS_INPUT_SITING_COEF_0_OVER_8);

    mhwSfcAvsState->dwInputVerticalSitting = (m_chromaSiting  & MHW_CHROMA_SITING_VERT_CENTER) ? SFC_AVS_INPUT_SITING_COEF_4_OVER_8 :
                                              ((m_chromaSiting & MHW_CHROMA_SITING_VERT_BOTTOM) ? SFC_AVS_INPUT_SITING_COEF_8_OVER_8 :
                                              SFC_AVS_INPUT_SITING_COEF_0_OVER_8);

    CODECHAL_ENCODE_CHK_STATUS_RETURN(sfcInterface->SetSfcSamplerTable(
                                &m_lumaTable,
                                &m_chromaTable,
                                &m_avsParams,
                                m_inputSurface->Format,
                                m_scaleX,
                                m_scaleY,
                                m_chromaSiting,
                                true,
                                0,
                                0));
    return eStatus;
}

MOS_STATUS CodecHalEncodeSfcBase::SetSfcIefStateParams(
    PMHW_SFC_IEF_STATE_PARAMS         params)
{
    MOS_STATUS                  eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_ENCODE_FUNCTION_ENTER;

    CODECHAL_ENCODE_CHK_NULL_RETURN(params);

    params->bIEFEnable     = false;
    params->bCSCEnable     = true;

    params->pfCscCoeff     = m_cscCoeff;
    params->pfCscInOffset  = m_cscInOffset;
    params->pfCscOutOffset = m_cscOutOffset;

    return eStatus;
}

MOS_STATUS CodecHalEncodeSfcBase::Initialize(
    CodechalHwInterface                *hwInterface,
    PMOS_INTERFACE                      osInterface)
{

    MOS_STATUS                eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_ENCODE_FUNCTION_ENTER;

    CODECHAL_ENCODE_CHK_NULL_RETURN(hwInterface);
    CODECHAL_ENCODE_CHK_NULL_RETURN(hwInterface->GetVeboxInterface());
    CODECHAL_ENCODE_CHK_NULL_RETURN(osInterface);

    m_hwInterface = hwInterface;
    m_osInterface = osInterface;

    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_hwInterface->GetVeboxInterface()->CreateHeap());

    // Create VEBOX Context
    MOS_GPUCTX_CREATOPTIONS createOption;
    //
    // VeboxgpuContext could be created from both VP and Codec.
    // If there is no such as a GPU context it will create a new one and set the GPU component ID.
    // If there has been a valid GPU context it will not create another one anymore and the component ID will not be updated either.
    // Therefore if a codec veboxgpu context creation happens earlier than a vp veboxgpu context creation and set its component ID to MOS_GPU_COMPONENT_ENCODE,
    // VPBLT callstack would index a GpuAppTaskEvent of MOS_GPU_COMPONENT_ENCODE.
    //
    MOS_COMPONENT originalComponent = m_osInterface->Component;
    m_osInterface->Component        = COMPONENT_VPCommon;

    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_osInterface->pfnCreateGpuContext(
        m_osInterface,
        MOS_GPU_CONTEXT_VEBOX,
        MOS_GPU_NODE_VE,
        &createOption));

    m_osInterface->Component        = originalComponent;

    // Register Vebox GPU context with the Batch Buffer completion event
    // Ignore if creation fails
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_osInterface->pfnRegisterBBCompleteNotifyEvent(
        m_osInterface,
        MOS_GPU_CONTEXT_VEBOX));

    return eStatus;
}

MOS_STATUS CodecHalEncodeSfcBase::SetParams(
    CODECHAL_ENCODE_SFC_PARAMS*         params)
{
    MOS_STATUS                eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_ENCODE_FUNCTION_ENTER;

    CODECHAL_ENCODE_CHK_NULL_RETURN(params);
    CODECHAL_ENCODE_CHK_NULL_RETURN(params->pInputSurface);
    CODECHAL_ENCODE_CHK_NULL_RETURN(params->pOutputSurface);

    m_inputSurface = params->pInputSurface;
    // Vebox o/p should not be written to memory for SFC, VeboxOutputSurface should be nullptr
    m_veboxOutputSurface = nullptr;
    m_sfcOutputSurface = params->pOutputSurface;

    // no scaling.
    m_scaling = false;
    m_colorFill = false;

    // No CSC for SFC pipe
    m_veboxCsc = true;

    m_scaleX = 1.0;
    m_scaleY = 1.0;
    m_chromaSiting = params->uiChromaSitingType;

    eStatus = MOS_SecureMemcpy(&m_inputSurfaceRegion,
        sizeof(m_inputSurfaceRegion),
        &params->rcInputSurfaceRegion,
        sizeof(params->rcInputSurfaceRegion));

    eStatus = MOS_SecureMemcpy(&m_outputSurfaceRegion,
        sizeof(m_outputSurfaceRegion),
        &params->rcOutputSurfaceRegion,
        sizeof(params->rcOutputSurfaceRegion));

    CODECHAL_ENCODE_CHK_STATUS_RETURN(AllocateResources());

    return eStatus;
}

MOS_STATUS CodecHalEncodeSfcBase::AddSfcCommands(
    PMHW_SFC_INTERFACE              sfcInterface,
    PMOS_COMMAND_BUFFER             cmdBuffer)
{
    MHW_SFC_LOCK_PARAMS            sfcLockParams;
    MHW_SFC_STATE_PARAMS           sfcStateParams;
    MHW_SFC_OUT_SURFACE_PARAMS     sfcOutSurfaceParams;
    MHW_SFC_IEF_STATE_PARAMS       sfcIefStateParams;
    MOS_STATUS                     eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_ENCODE_FUNCTION_ENTER;

    CODECHAL_ENCODE_CHK_NULL_RETURN(sfcInterface);
    CODECHAL_ENCODE_CHK_NULL_RETURN(cmdBuffer);

    MOS_ZeroMemory(&sfcLockParams, sizeof(sfcLockParams));

    sfcLockParams.sfcPipeMode     = MhwSfcInterface::SFC_PIPE_MODE_VEBOX;
    sfcLockParams.bOutputToMemory = false;

    MOS_ZeroMemory(&sfcStateParams, sizeof(sfcStateParams));
    MOS_ZeroMemory(&sfcOutSurfaceParams, sizeof(sfcOutSurfaceParams));
    CODECHAL_ENCODE_CHK_STATUS_RETURN(SetSfcStateParams(sfcInterface, &sfcStateParams, &sfcOutSurfaceParams));

    CODECHAL_ENCODE_CHK_STATUS_RETURN(sfcInterface->AddSfcLock(cmdBuffer, &sfcLockParams));
    CODECHAL_ENCODE_CHK_STATUS_RETURN(sfcInterface->AddSfcState(cmdBuffer, &sfcStateParams, &sfcOutSurfaceParams));

    if (m_scaling)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(SetSfcAvsStateParams(sfcInterface));
        CODECHAL_ENCODE_CHK_STATUS_RETURN(sfcInterface->AddSfcAvsState(cmdBuffer, &m_avsState));
        CODECHAL_ENCODE_CHK_STATUS_RETURN(sfcInterface->AddSfcAvsLumaTable(cmdBuffer, &m_lumaTable));
        CODECHAL_ENCODE_CHK_STATUS_RETURN(sfcInterface->AddSfcAvsChromaTable(cmdBuffer, &m_chromaTable));
    }

    if (m_CSC)
    {
        MOS_ZeroMemory(&sfcIefStateParams, sizeof(sfcIefStateParams));
        CODECHAL_ENCODE_CHK_STATUS_RETURN(SetSfcIefStateParams(&sfcIefStateParams));
        CODECHAL_ENCODE_CHK_STATUS_RETURN(sfcInterface->AddSfcIefState(cmdBuffer, &sfcIefStateParams));
    }

    CODECHAL_ENCODE_CHK_STATUS_RETURN(sfcInterface->AddSfcFrameStart(cmdBuffer, MhwSfcInterface::SFC_PIPE_MODE_VEBOX));

    return eStatus;
}

MOS_STATUS CodecHalEncodeSfcBase::RenderStart(
    CodechalEncoderState*       encoder)
{
    MHW_VEBOX_STATE_CMD_PARAMS          veboxStateCmdParams;
    MHW_VEBOX_SURFACE_STATE_CMD_PARAMS  veboxSurfaceStateCmdParams;
    MHW_VEBOX_DI_IECP_CMD_PARAMS        veboxDiIecpCmdParams;
    MHW_VEBOX_IECP_PARAMS               veboxIecpParams;
    MHW_VEBOX_SURFACE_CNTL_PARAMS       veboxSurfCntlParams;
    MhwVeboxInterface                   *veboxInterface;
    PMHW_SFC_INTERFACE                  sfcInterface;
    MhwMiInterface                      *miInterface;
    PMOS_CONTEXT                        pOsContext = nullptr;
    MHW_MI_MMIOREGISTERS                *mmioVeboxRegisters = nullptr;
    MOS_COMMAND_BUFFER                  cmdBuffer;
    bool                                requestFrameTracking;
    MOS_STATUS                          eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_ENCODE_FUNCTION_ENTER;

    CODECHAL_ENCODE_CHK_NULL_RETURN(m_hwInterface);
    CODECHAL_ENCODE_CHK_NULL_RETURN(m_osInterface);
    CODECHAL_ENCODE_CHK_NULL_RETURN(pOsContext = m_osInterface->pOsContext);
    CODECHAL_ENCODE_CHK_NULL_RETURN(encoder);
    CODECHAL_ENCODE_CHK_NULL_RETURN(sfcInterface = m_hwInterface->GetSfcInterface());
    CODECHAL_ENCODE_CHK_NULL_RETURN(veboxInterface = m_hwInterface->GetVeboxInterface());
    CODECHAL_ENCODE_CHK_NULL_RETURN(miInterface = m_hwInterface->GetMiInterface());
    CODECHAL_ENCODE_CHK_NULL_RETURN(mmioVeboxRegisters = miInterface->GetMmioRegisters());

    // 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_ZeroMemory(&cmdBuffer, sizeof(MOS_COMMAND_BUFFER));
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_osInterface->pfnGetCommandBuffer(m_osInterface, &cmdBuffer, 0));

    // the first task?
    requestFrameTracking = false;
    CODECHAL_ENCODE_CHK_STATUS_RETURN(encoder->SendPrologWithFrameTracking(&cmdBuffer, requestFrameTracking, mmioVeboxRegisters));

    // If m_pollingSyncEnabled is set, insert HW semaphore to wait for external
    // raw surface processing to complete, before start CSC. Once the marker in
    // raw surface is overwritten by external operation, HW semaphore will be
    // signalled and CSC will start. This is to reduce SW latency between
    // external raw surface processing and CSC, in usages like remote gaming.
    if (encoder->m_pollingSyncEnabled)
    {
        MHW_MI_SEMAPHORE_WAIT_PARAMS miSemaphoreWaitParams;
        MOS_ZeroMemory((&miSemaphoreWaitParams), sizeof(miSemaphoreWaitParams));
        miSemaphoreWaitParams.presSemaphoreMem = &m_inputSurface->OsResource;
        miSemaphoreWaitParams.dwResourceOffset = encoder->m_syncMarkerOffset;
        miSemaphoreWaitParams.bPollingWaitMode = true;
        miSemaphoreWaitParams.dwSemaphoreData  = encoder->m_syncMarkerValue;
        miSemaphoreWaitParams.CompareOperation = MHW_MI_SAD_NOT_EQUAL_SDD;
        CODECHAL_ENCODE_CHK_STATUS_RETURN(miInterface->AddMiSemaphoreWaitCmd(&cmdBuffer, &miSemaphoreWaitParams));
    }

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

    MOS_ZeroMemory(&veboxSurfaceStateCmdParams, sizeof(veboxSurfaceStateCmdParams));
    CODECHAL_ENCODE_CHK_STATUS_RETURN(SetVeboxSurfaceStateParams(&veboxSurfaceStateCmdParams));

    MOS_ZeroMemory(&veboxDiIecpCmdParams, sizeof(veboxDiIecpCmdParams));
    CODECHAL_ENCODE_CHK_STATUS_RETURN(SetVeboxDiIecpParams(&veboxDiIecpCmdParams));

    MOS_ZeroMemory(&veboxSurfCntlParams, sizeof(veboxSurfCntlParams));
    veboxSurfCntlParams.bIsCompressed   = m_inputSurface->bIsCompressed;
    veboxSurfCntlParams.CompressionMode = m_inputSurface->CompressionMode;
    CODECHAL_ENCODE_CHK_STATUS_RETURN(veboxInterface->AddVeboxSurfaceControlBits(
        &veboxSurfCntlParams,
        (uint32_t *)&(veboxDiIecpCmdParams.CurrInputSurfCtrl.Value)));

    // get csc matrix
    MOS_ZeroMemory(&veboxIecpParams, sizeof(veboxIecpParams));
    CODECHAL_ENCODE_CHK_STATUS_RETURN(VeboxSetIecpParams(&veboxIecpParams));

    // send matrix into heap
    CODECHAL_ENCODE_CHK_STATUS_RETURN(veboxInterface->AddVeboxIecpState(
        &veboxIecpParams));

    // send Vebox and SFC cmds
    CODECHAL_ENCODE_CHK_STATUS_RETURN(veboxInterface->AddVeboxState(&cmdBuffer, &veboxStateCmdParams, 0));

    CODECHAL_ENCODE_CHK_STATUS_RETURN(veboxInterface->AddVeboxSurfaces( &cmdBuffer, &veboxSurfaceStateCmdParams));

    CODECHAL_ENCODE_CHK_STATUS_RETURN(AddSfcCommands(sfcInterface, &cmdBuffer));

    HalOcaInterface::TraceMessage(cmdBuffer, (MOS_CONTEXT_HANDLE)pOsContext, __FUNCTION__, sizeof(__FUNCTION__));
    HalOcaInterface::OnDispatch(cmdBuffer, *m_osInterface, *miInterface, *mmioVeboxRegisters);

    CODECHAL_ENCODE_CHK_STATUS_RETURN(veboxInterface->AddVeboxDiIecp(&cmdBuffer, &veboxDiIecpCmdParams));

    // If m_pollingSyncEnabled is set, write the marker to source surface for next MI_SEMAPHORE_WAIT to check.
    if (encoder->m_pollingSyncEnabled)
    {
        MHW_MI_STORE_DATA_PARAMS storeDataParams;
        storeDataParams.pOsResource      = &m_inputSurface->OsResource;
        storeDataParams.dwResourceOffset = encoder->m_syncMarkerOffset;
        storeDataParams.dwValue          = encoder->m_syncMarkerValue;
        CODECHAL_ENCODE_CHK_STATUS_RETURN(miInterface->AddMiStoreDataImmCmd(&cmdBuffer, &storeDataParams));
    }

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

    CODECHAL_DEBUG_TOOL(CODECHAL_ENCODE_CHK_STATUS_RETURN(encoder->GetDebugInterface()->DumpCmdBuffer(
        &cmdBuffer,
        CODECHAL_MEDIA_STATE_CSC_DS_COPY,
        nullptr)));

    m_osInterface->pfnReturnCommandBuffer(m_osInterface, &cmdBuffer, 0);
    HalOcaInterface::On1stLevelBBEnd(cmdBuffer, *m_osInterface);

    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_osInterface->pfnSubmitCommandBuffer(
        m_osInterface,
        &cmdBuffer,
        encoder->m_videoContextUsesNullHw));
    return eStatus;
}
