/*
* Copyright (c) 2014-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     mhw_sfc.cpp
//! \brief    MHW interface for constructing commands for the SFC
//! \details  Impelements the functionalities common across all platforms for MHW_SFC
//!

#include "mhw_sfc.h"
#include "mhw_utilities.h"

MhwSfcInterface::MhwSfcInterface(PMOS_INTERFACE pOsInterface)
{
    MHW_FUNCTION_ENTER;

    MOS_ZeroMemory(&m_outputSurfCtrl, sizeof(m_outputSurfCtrl));
    MOS_ZeroMemory(&m_avsLineBufferCtrl, sizeof(m_avsLineBufferCtrl));
    MOS_ZeroMemory(&m_iefLineBufferCtrl, sizeof(m_iefLineBufferCtrl));
    MOS_ZeroMemory(&m_sfdLineBufferCtrl, sizeof(m_sfdLineBufferCtrl));
    pfnAddResourceToCmd = nullptr;
    m_scalingMode       = MHW_SCALING_AVS;

    if (pOsInterface == nullptr)
    {
        MHW_ASSERTMESSAGE("Invalid input pointers provided");
        return;
    }
    if (!pOsInterface->bUsesGfxAddress && !pOsInterface->bUsesPatchList)
    {
        MHW_ASSERTMESSAGE("No valid addressing mode indicated");
        return;
    }

    m_osInterface = pOsInterface;

    if (m_osInterface->bUsesGfxAddress)
    {
        pfnAddResourceToCmd = Mhw_AddResourceToCmd_GfxAddress;
    }
    else  //PatchList
    {
        pfnAddResourceToCmd = Mhw_AddResourceToCmd_PatchList;
    }
}

void MhwSfcInterface::SetSfcAVSChromaTable(
    PSFC_AVS_CHROMA_FILTER_COEFF        pUVCoeffTable,
    int32_t                             *piUVCoefsX,
    int32_t                             *piUVCoefsY)
{
    int32_t i;

    MHW_CHK_NULL_NO_STATUS_RETURN(pUVCoeffTable);
    MHW_CHK_NULL_NO_STATUS_RETURN(piUVCoefsX);
    MHW_CHK_NULL_NO_STATUS_RETURN(piUVCoefsY);

    for (i = 0; i < NUM_HW_POLYPHASE_TABLES; i++, pUVCoeffTable++)
    {
        pUVCoeffTable->DW0.Table1XFilterCoefficient2 = *(piUVCoefsX++);
        pUVCoeffTable->DW0.Table1XFilterCoefficient3 = *(piUVCoefsX++);
        pUVCoeffTable->DW1.Table1XFilterCoefficient4 = *(piUVCoefsX++);
        pUVCoeffTable->DW1.Table1XFilterCoefficient5 = *(piUVCoefsX++);

        pUVCoeffTable->DW0.Table1YFilterCoefficient2 = *(piUVCoefsY++);
        pUVCoeffTable->DW0.Table1YFilterCoefficient3 = *(piUVCoefsY++);
        pUVCoeffTable->DW1.Table1YFilterCoefficient4 = *(piUVCoefsY++);
        pUVCoeffTable->DW1.Table1YFilterCoefficient5 = *(piUVCoefsY++);
    }
}

void MhwSfcInterface::SetSfcAVSLumaTable(
    MOS_FORMAT                      SrcFormat,
    PSFC_AVS_LUMA_FILTER_COEFF      pCoeffTable,
    int32_t                         *piYCoefsX,
    int32_t                         *piYCoefsY,
    bool                            bUse8x8Filter)
{
    int32_t i;

    MHW_CHK_NULL_NO_STATUS_RETURN(pCoeffTable);
    MHW_CHK_NULL_NO_STATUS_RETURN(piYCoefsX);
    MHW_CHK_NULL_NO_STATUS_RETURN(piYCoefsY);

    for (i = 0; i < NUM_HW_POLYPHASE_TABLES; i++, pCoeffTable++)
    {
        // 4-tap filtering for G-channel, update only center 4 coeffs.
        if (IS_RGB32_FORMAT(SrcFormat) && (!bUse8x8Filter))
        {
            pCoeffTable->DW0.Table0XFilterCoefficient0 = 0;
            pCoeffTable->DW0.Table0XFilterCoefficient1 = 0;
            pCoeffTable->DW1.Table0XFilterCoefficient2 = *(piYCoefsX++);
            pCoeffTable->DW1.Table0XFilterCoefficient3 = *(piYCoefsX++);
            pCoeffTable->DW2.Table0XFilterCoefficient4 = *(piYCoefsX++);
            pCoeffTable->DW2.Table0XFilterCoefficient5 = *(piYCoefsX++);
            pCoeffTable->DW3.Table0XFilterCoefficient6 = 0;
            pCoeffTable->DW3.Table0XFilterCoefficient7 = 0;

            pCoeffTable->DW0.Table0YFilterCoefficient0 = 0;
            pCoeffTable->DW0.Table0YFilterCoefficient1 = 0;
            pCoeffTable->DW1.Table0YFilterCoefficient2 = *(piYCoefsY++);
            pCoeffTable->DW1.Table0YFilterCoefficient3 = *(piYCoefsY++);
            pCoeffTable->DW2.Table0YFilterCoefficient4 = *(piYCoefsY++);
            pCoeffTable->DW2.Table0YFilterCoefficient5 = *(piYCoefsY++);
            pCoeffTable->DW3.Table0YFilterCoefficient6 = 0;
            pCoeffTable->DW3.Table0YFilterCoefficient7 = 0;
        }
        else
        {
            pCoeffTable->DW0.Table0XFilterCoefficient0 = *(piYCoefsX++);
            pCoeffTable->DW0.Table0XFilterCoefficient1 = *(piYCoefsX++);
            pCoeffTable->DW1.Table0XFilterCoefficient2 = *(piYCoefsX++);
            pCoeffTable->DW1.Table0XFilterCoefficient3 = *(piYCoefsX++);
            pCoeffTable->DW2.Table0XFilterCoefficient4 = *(piYCoefsX++);
            pCoeffTable->DW2.Table0XFilterCoefficient5 = *(piYCoefsX++);
            pCoeffTable->DW3.Table0XFilterCoefficient6 = *(piYCoefsX++);
            pCoeffTable->DW3.Table0XFilterCoefficient7 = *(piYCoefsX++);

            pCoeffTable->DW0.Table0YFilterCoefficient0 = *(piYCoefsY++);
            pCoeffTable->DW0.Table0YFilterCoefficient1 = *(piYCoefsY++);
            pCoeffTable->DW1.Table0YFilterCoefficient2 = *(piYCoefsY++);
            pCoeffTable->DW1.Table0YFilterCoefficient3 = *(piYCoefsY++);
            pCoeffTable->DW2.Table0YFilterCoefficient4 = *(piYCoefsY++);
            pCoeffTable->DW2.Table0YFilterCoefficient5 = *(piYCoefsY++);
            pCoeffTable->DW3.Table0YFilterCoefficient6 = *(piYCoefsY++);
            pCoeffTable->DW3.Table0YFilterCoefficient7 = *(piYCoefsY++);
        }
    }
}

MOS_STATUS MhwSfcInterface::SetSfcSamplerTable(
    PMHW_SFC_AVS_LUMA_TABLE         pLumaTable,
    PMHW_SFC_AVS_CHROMA_TABLE       pChromaTable,
    PMHW_AVS_PARAMS                 pAvsParams,
    MOS_FORMAT                      SrcFormat,
    float                           fScaleX,
    float                           fScaleY,
    uint32_t                        dwChromaSiting,
    bool                            bUse8x8Filter,
    float                           fHPStrength,
    float                           fLanczosT)
{
    int32_t                             iPhaseOffset;

    int32_t     *piYCoefsX, *piYCoefsY;
    int32_t     *piUVCoefsX, *piUVCoefsY;
    MHW_PLANE   Plane;

    MHW_CHK_NULL_RETURN(pLumaTable);
    MHW_CHK_NULL_RETURN(pChromaTable);
    MHW_CHK_NULL_RETURN(pAvsParams);

    piYCoefsX   = pAvsParams->piYCoefsX;
    piYCoefsY   = pAvsParams->piYCoefsY;
    piUVCoefsX  = pAvsParams->piUVCoefsX;
    piUVCoefsY  = pAvsParams->piUVCoefsY;

    //  Skip calculation if no changes to AVS parameters
    if (SrcFormat == pAvsParams->Format  &&
        fScaleX   == pAvsParams->fScaleX &&
        fScaleY   == pAvsParams->fScaleY)
    {
        return MOS_STATUS_SUCCESS;
    }

    // AVS Coefficients don't change for Scaling Factors > 1.0x
    // Hence recalculation is avoided
    if (fScaleX > 1.0F && pAvsParams->fScaleX > 1.0F)
    {
        pAvsParams->fScaleX = fScaleX;
    }

    // AVS Coefficients don't change for Scaling Factors > 1.0x
    // Hence recalculation is avoided
    if (fScaleY > 1.0F && pAvsParams->fScaleY > 1.0F)
    {
        pAvsParams->fScaleY = fScaleY;
    }

    // Recalculate Horizontal scaling table
    if (SrcFormat != pAvsParams->Format || fScaleX != pAvsParams->fScaleX)
    {
        MOS_ZeroMemory(
            piYCoefsX,
            8 * 32 * sizeof(int32_t));

        MOS_ZeroMemory(
            piUVCoefsX,
            4 * 32 * sizeof(int32_t));

        // 4-tap filtering for RGB format G-channel.
        Plane = IS_RGB32_FORMAT(SrcFormat) ? MHW_U_PLANE : MHW_Y_PLANE;

        pAvsParams->fScaleX = fScaleX;

        // Nearest, overwrite the coefficients.
        if (m_scalingMode == MHW_SCALING_NEAREST)
        {
            MHW_CHK_STATUS_RETURN(Mhw_SetNearestModeTable(
                piYCoefsX,
                Plane,
                true));

            MHW_CHK_STATUS_RETURN(Mhw_SetNearestModeTable(
                piUVCoefsX,
                MHW_U_PLANE,
                true));
        }
        else
        {
            // By default AVS coefficients
            // For 1x scaling in horizontal direction and not force polyphase coefs, use special coefficients for filtering
            if (fScaleX == 1.0F && !pAvsParams->bForcePolyPhaseCoefs)
            {
                MHW_CHK_STATUS_RETURN(Mhw_SetNearestModeTable(
                    piYCoefsX,
                    Plane,
                    true));

                MHW_CHK_STATUS_RETURN(Mhw_SetNearestModeTable(
                    piUVCoefsX,
                    MHW_U_PLANE,
                    true));
            }
            else
            {
                // Clamp the Scaling Factor if > 1.0x
                fScaleX = MOS_MIN(1.0F, fScaleX);

                MHW_CHK_STATUS_RETURN(Mhw_CalcPolyphaseTablesY(
                    piYCoefsX,
                    fScaleX,
                    Plane,
                    SrcFormat,
                    fHPStrength,
                    bUse8x8Filter,
                    NUM_HW_POLYPHASE_TABLES,
                    fLanczosT));
            }

            // If Chroma Siting info is present
            if (dwChromaSiting & MHW_CHROMA_SITING_HORZ_LEFT)
            {
                // No Chroma Siting
                MHW_CHK_STATUS_RETURN(Mhw_CalcPolyphaseTablesUV(
                    piUVCoefsX,
                    2.0F,
                    fScaleX));
            }
            else
            {
                // Chroma siting offset needs to be added
                if (dwChromaSiting & MHW_CHROMA_SITING_HORZ_CENTER)
                {
                    iPhaseOffset = MOS_UF_ROUND(0.5F * 16.0F);   // U0.4
                }
                else //if (ChromaSiting & MHW_CHROMA_SITING_HORZ_RIGHT)
                {
                    iPhaseOffset = MOS_UF_ROUND(1.0F * 16.0F);   // U0.4
                }

                MHW_CHK_STATUS_RETURN(Mhw_CalcPolyphaseTablesUVOffset(
                    piUVCoefsX,
                    3.0F,
                    fScaleX,
                    iPhaseOffset));
            }
        }
    }

    // Recalculate Vertical scaling table
    if (SrcFormat != pAvsParams->Format || fScaleY != pAvsParams->fScaleY)
    {
        MOS_ZeroMemory(piYCoefsY, 8 * 32 * sizeof(int32_t));

        MOS_ZeroMemory(piUVCoefsY, 4 * 32 * sizeof(int32_t));

        // 4-tap filtering for RGB format G-channel.
        Plane = IS_RGB32_FORMAT(SrcFormat) ? MHW_U_PLANE : MHW_Y_PLANE;

        pAvsParams->fScaleY = fScaleY;

        // Nearest, overwrite the coefficients.
        if (m_scalingMode == MHW_SCALING_NEAREST)
        {
            MHW_CHK_STATUS_RETURN(Mhw_SetNearestModeTable(
                piYCoefsY,
                Plane,
                true));

            MHW_CHK_STATUS_RETURN(Mhw_SetNearestModeTable(
                piUVCoefsY,
                MHW_U_PLANE,
                true));
        }
        else
        {
            // For 1x scaling in vertical direction and not force polyphase coefs, use special coefficients for filtering
            if (fScaleY == 1.0F && !pAvsParams->bForcePolyPhaseCoefs)
            {
                MHW_CHK_STATUS_RETURN(Mhw_SetNearestModeTable(
                    piYCoefsY,
                    Plane,
                    true));

                MHW_CHK_STATUS_RETURN(Mhw_SetNearestModeTable(
                    piUVCoefsY,
                    MHW_U_PLANE,
                    true));
            }
            else
            {
                // Clamp the Scaling Factor if > 1.0x
                fScaleY = MOS_MIN(1.0F, fScaleY);

                MHW_CHK_STATUS_RETURN(Mhw_CalcPolyphaseTablesY(
                    piYCoefsY,
                    fScaleY,
                    Plane,
                    SrcFormat,
                    fHPStrength,
                    bUse8x8Filter,
                    NUM_HW_POLYPHASE_TABLES,
                    fLanczosT));
            }

            // If Chroma Siting info is present
            if (dwChromaSiting & MHW_CHROMA_SITING_VERT_TOP)
            {
                // No Chroma Siting
                MHW_CHK_STATUS_RETURN(Mhw_CalcPolyphaseTablesUV(
                    piUVCoefsY,
                    2.0F,
                    fScaleY));
            }
            else
            {
                // Chroma siting offset needs to be added
                if (dwChromaSiting & MHW_CHROMA_SITING_VERT_CENTER)
                {
                    iPhaseOffset = MOS_UF_ROUND(0.5F * 16.0F);   // U0.4
                }
                else //if (ChromaSiting & MHW_CHROMA_SITING_VERT_BOTTOM)
                {
                    iPhaseOffset = MOS_UF_ROUND(1.0F * 16.0F);   // U0.4
                }

                MHW_CHK_STATUS_RETURN(Mhw_CalcPolyphaseTablesUVOffset(
                    piUVCoefsY,
                    3.0F,
                    fScaleY,
                    iPhaseOffset));
            }
        }
    }

    // Save format used to calculate AVS parameters
    pAvsParams->Format = SrcFormat;

    SetSfcAVSLumaTable(
        SrcFormat,
        pLumaTable->LumaTable,
        piYCoefsX,
        piYCoefsY,
        bUse8x8Filter);

    SetSfcAVSChromaTable(
        pChromaTable->ChromaTable,
        piUVCoefsX,
        piUVCoefsY);

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MhwSfcInterface::GetInputFrameWidthHeightAlignUnit(uint32_t &widthAlignUnit, uint32_t &heightAlignUnit,
    bool bVdbox, CODECHAL_STANDARD codecStandard, CodecDecodeJpegChromaType jpegChromaType)
{
    if (bVdbox)
    {
        if (CODECHAL_JPEG == codecStandard &&( jpegYUV400 == jpegChromaType ||
            jpegYUV444 == jpegChromaType || jpegYUV422H2Y == jpegChromaType) ||
            jpegBGR == jpegChromaType || jpegRGB == jpegChromaType)
        {
            widthAlignUnit = 8;
            heightAlignUnit = 8;
            return MOS_STATUS_SUCCESS;
        }
        else if (CODECHAL_HEVC == codecStandard || CODECHAL_VP9 == codecStandard)
        {
            widthAlignUnit = 8;
            heightAlignUnit = 8;
            return MOS_STATUS_SUCCESS;
        }
        else if (CODECHAL_AV1 == codecStandard)
        {
            widthAlignUnit  = 1;
            heightAlignUnit = 1;
            return MOS_STATUS_SUCCESS;
        }
        else
        {
            widthAlignUnit = 16;
            heightAlignUnit = 16;
            return MOS_STATUS_SUCCESS;
        }
    }
    else
    {
        widthAlignUnit = m_veWidthAlignment;
        heightAlignUnit = m_veHeightAlignment;
        return MOS_STATUS_SUCCESS;
    }
}
