/*
 * Copyright © 2014 Advanced Micro Devices, Inc.
 * All Rights Reserved.
 *
 * 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, sub license, 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 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
 * NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS, AUTHORS
 * AND/OR ITS SUPPLIERS 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.
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial portions
 * of the Software.
 */

/**
***************************************************************************************************
* @file  ciaddrlib.cpp
* @brief Contains the implementation for the CIAddrLib class.
***************************************************************************************************
*/

#include "ciaddrlib.h"

#include "si_gb_reg.h"

#include "si_ci_vi_merged_enum.h"

#if BRAHMA_BUILD
#include "amdgpu_id.h"
#else
#include "ci_id.h"
#include "kv_id.h"
#include "vi_id.h"
#endif

///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////

/**
***************************************************************************************************
*   AddrMask
*
*   @brief
*       Gets a mask of "width"
*   @return
*       Bit mask
***************************************************************************************************
*/
static UINT_64 AddrMask(
    UINT_32 width)  ///< Width of bits
{
    UINT_64 ret;

    if (width >= sizeof(UINT_64)*8)
    {
        ret = ~((UINT_64) 0);
    }
    else
    {
        return (((UINT_64) 1) << width) - 1;
    }
    return ret;
}

/**
***************************************************************************************************
*   AddrGetBits
*
*   @brief
*       Gets bits within a range of [msb, lsb]
*   @return
*       Bits of this range
***************************************************************************************************
*/
static UINT_64 AddrGetBits(
    UINT_64 bits,   ///< Source bits
    UINT_32 msb,    ///< Most signicant bit
    UINT_32 lsb)    ///< Least signicant bit
{
    UINT_64 ret = 0;

    if (msb >= lsb)
    {
        ret = (bits >> lsb) & (AddrMask(1 + msb - lsb));
    }
    return ret;
}

/**
***************************************************************************************************
*   AddrRemoveBits
*
*   @brief
*       Removes bits within the range of [msb, lsb]
*   @return
*       Modified bits
***************************************************************************************************
*/
static UINT_64 AddrRemoveBits(
    UINT_64 bits,   ///< Source bits
    UINT_32 msb,    ///< Most signicant bit
    UINT_32 lsb)    ///< Least signicant bit
{
    UINT_64 ret = bits;

    if (msb >= lsb)
    {
        ret = AddrGetBits(bits, lsb - 1, 0) // low bits
            | (AddrGetBits(bits, 8 * sizeof(bits) - 1, msb + 1) << lsb); //high bits
    }
    return ret;
}

/**
***************************************************************************************************
*   AddrInsertBits
*
*   @brief
*       Inserts new bits into the range of [msb, lsb]
*   @return
*       Modified bits
***************************************************************************************************
*/
static UINT_64 AddrInsertBits(
    UINT_64 bits,       ///< Source bits
    UINT_64 newBits,    ///< New bits to be inserted
    UINT_32 msb,        ///< Most signicant bit
    UINT_32 lsb)        ///< Least signicant bit
{
    UINT_64 ret = bits;

    if (msb >= lsb)
    {
        ret = AddrGetBits(bits, lsb - 1, 0) // old low bitss
             | (AddrGetBits(newBits, msb - lsb, 0) << lsb) //new bits
             | (AddrGetBits(bits, 8 * sizeof(bits) - 1, lsb) << (msb + 1)); //old high bits
    }
    return ret;
}


/**
***************************************************************************************************
*   AddrCIHwlInit
*
*   @brief
*       Creates an CIAddrLib object.
*
*   @return
*       Returns an CIAddrLib object pointer.
***************************************************************************************************
*/
AddrLib* AddrCIHwlInit(const AddrClient* pClient)
{
    return CIAddrLib::CreateObj(pClient);
}

/**
***************************************************************************************************
*   CIAddrLib::CIAddrLib
*
*   @brief
*       Constructor
*
***************************************************************************************************
*/
CIAddrLib::CIAddrLib(const AddrClient* pClient) :
    SIAddrLib(pClient),
    m_noOfMacroEntries(0),
    m_allowNonDispThickModes(FALSE)
{
    m_class = CI_ADDRLIB;
    memset(&m_settings, 0, sizeof(m_settings));
}

/**
***************************************************************************************************
*   CIAddrLib::~CIAddrLib
*
*   @brief
*       Destructor
***************************************************************************************************
*/
CIAddrLib::~CIAddrLib()
{
}

/**
***************************************************************************************************
*   CIAddrLib::HwlComputeDccInfo
*
*   @brief
*       Compute DCC key size, base alignment
*   @return
*       ADDR_E_RETURNCODE
***************************************************************************************************
*/
ADDR_E_RETURNCODE CIAddrLib::HwlComputeDccInfo(
    const ADDR_COMPUTE_DCCINFO_INPUT*  pIn,
    ADDR_COMPUTE_DCCINFO_OUTPUT*       pOut) const
{
    ADDR_E_RETURNCODE returnCode = ADDR_OK;

    if (m_settings.isVolcanicIslands && IsMacroTiled(pIn->tileMode))
    {
        UINT_64 dccFastClearSize = pIn->colorSurfSize >> 8;

        ADDR_ASSERT(0 == (pIn->colorSurfSize & 0xff));

        if (pIn->numSamples > 1)
        {
            UINT_32 tileSizePerSample = BITS_TO_BYTES(pIn->bpp * MicroTileWidth * MicroTileHeight);
            UINT_32 samplesPerSplit  = pIn->tileInfo.tileSplitBytes / tileSizePerSample;

            if (samplesPerSplit < pIn->numSamples)
            {
                UINT_32 numSplits = pIn->numSamples / samplesPerSplit;
                UINT_32 fastClearBaseAlign = HwlGetPipes(&pIn->tileInfo) * m_pipeInterleaveBytes;

                ADDR_ASSERT(IsPow2(fastClearBaseAlign));

                dccFastClearSize /= numSplits;

                if (0 != (dccFastClearSize & (fastClearBaseAlign - 1)))
                {
                    // Disable dcc fast clear
                    // if key size of fisrt sample split is not pipe*interleave aligned
                    dccFastClearSize = 0;
                }
            }
        }

        pOut->dccRamSize          = pIn->colorSurfSize >> 8;
        pOut->dccRamBaseAlign     = pIn->tileInfo.banks *
                                    HwlGetPipes(&pIn->tileInfo) *
                                    m_pipeInterleaveBytes;
        pOut->dccFastClearSize    = dccFastClearSize;

        ADDR_ASSERT(IsPow2(pOut->dccRamBaseAlign));

        if (0 == (pOut->dccRamSize & (pOut->dccRamBaseAlign - 1)))
        {
            pOut->subLvlCompressible = TRUE;
        }
        else
        {
            UINT_64 dccRamSizeAlign = HwlGetPipes(&pIn->tileInfo) * m_pipeInterleaveBytes;

            if (pOut->dccRamSize == pOut->dccFastClearSize)
            {
                pOut->dccFastClearSize = PowTwoAlign(pOut->dccRamSize, dccRamSizeAlign);
            }
            pOut->dccRamSize          = PowTwoAlign(pOut->dccRamSize, dccRamSizeAlign);
            pOut->subLvlCompressible  = FALSE;
        }
    }
    else
    {
        returnCode = ADDR_NOTSUPPORTED;
    }

    return returnCode;
}

/**
***************************************************************************************************
*   CIAddrLib::HwlComputeCmaskAddrFromCoord
*
*   @brief
*       Compute tc compatible Cmask address from fmask ram address
*
*   @return
*       ADDR_E_RETURNCODE
***************************************************************************************************
*/
ADDR_E_RETURNCODE CIAddrLib::HwlComputeCmaskAddrFromCoord(
    const ADDR_COMPUTE_CMASK_ADDRFROMCOORD_INPUT*  pIn,  ///< [in] fmask addr/bpp/tile input
    ADDR_COMPUTE_CMASK_ADDRFROMCOORD_OUTPUT*       pOut  ///< [out] cmask address
    ) const
{
    ADDR_E_RETURNCODE returnCode = ADDR_NOTSUPPORTED;

    if ((m_settings.isVolcanicIslands == TRUE) &&
        (pIn->flags.tcCompatible == TRUE))
    {
        UINT_32 numOfPipes   = HwlGetPipes(pIn->pTileInfo);
        UINT_32 numOfBanks   = pIn->pTileInfo->banks;
        UINT_64 fmaskAddress = pIn->fmaskAddr;
        UINT_32 elemBits     = pIn->bpp;
        UINT_32 blockByte    = 64 * elemBits / 8;
        UINT_64 metaNibbleAddress = HwlComputeMetadataNibbleAddress(fmaskAddress,
                                                                    0,
                                                                    0,
                                                                    4,
                                                                    elemBits,
                                                                    blockByte,
                                                                    m_pipeInterleaveBytes,
                                                                    numOfPipes,
                                                                    numOfBanks,
                                                                    1);
        pOut->addr = (metaNibbleAddress >> 1);
        pOut->bitPosition = (metaNibbleAddress % 2) ? 4 : 0;
        returnCode = ADDR_OK;
    }

    return returnCode;
}
/**
***************************************************************************************************
*   CIAddrLib::HwlConvertChipFamily
*
*   @brief
*       Convert familyID defined in atiid.h to AddrChipFamily and set m_chipFamily/m_chipRevision
*   @return
*       AddrChipFamily
***************************************************************************************************
*/
AddrChipFamily CIAddrLib::HwlConvertChipFamily(
    UINT_32 uChipFamily,        ///< [in] chip family defined in atiih.h
    UINT_32 uChipRevision)      ///< [in] chip revision defined in "asic_family"_id.h
{
    AddrChipFamily family = ADDR_CHIP_FAMILY_CI;

    switch (uChipFamily)
    {
        case FAMILY_CI:
            m_settings.isSeaIsland  = 1;
            m_settings.isBonaire    = ASICREV_IS_BONAIRE_M(uChipRevision);
            m_settings.isHawaii     = ASICREV_IS_HAWAII_P(uChipRevision);
            break;
        case FAMILY_KV:
            m_settings.isKaveri     = 1;
            m_settings.isSpectre    = ASICREV_IS_SPECTRE(uChipRevision);
            m_settings.isSpooky     = ASICREV_IS_SPOOKY(uChipRevision);
            m_settings.isKalindi    = ASICREV_IS_KALINDI(uChipRevision);
            break;
        case FAMILY_VI:
            m_settings.isVolcanicIslands = 1;
            m_settings.isIceland         = ASICREV_IS_ICELAND_M(uChipRevision);
            m_settings.isTonga           = ASICREV_IS_TONGA_P(uChipRevision);
            m_settings.isFiji            = ASICREV_IS_FIJI_P(uChipRevision);
            break;
        case FAMILY_CZ:
            m_settings.isCarrizo         = 1;
            m_settings.isVolcanicIslands = 1;
            break;
        default:
            ADDR_ASSERT(!"This should be a unexpected Fusion");
            break;
    }

    return family;
}

/**
***************************************************************************************************
*   CIAddrLib::HwlInitGlobalParams
*
*   @brief
*       Initializes global parameters
*
*   @return
*       TRUE if all settings are valid
*
***************************************************************************************************
*/
BOOL_32 CIAddrLib::HwlInitGlobalParams(
    const ADDR_CREATE_INPUT* pCreateIn) ///< [in] create input
{
    BOOL_32  valid = TRUE;

    const ADDR_REGISTER_VALUE* pRegValue = &pCreateIn->regValue;

    valid = DecodeGbRegs(pRegValue);

    // The following assignments for m_pipes is only for fail-safe, InitTileSettingTable should
    // read the correct pipes from tile mode table
    if (m_settings.isHawaii)
    {
        // Hawaii has 16-pipe, see GFXIP_Config_Summary.xls
        m_pipes = 16;
    }
    else if (m_settings.isBonaire || m_settings.isSpectre)
    {
        m_pipes = 4;
    }
    else // Treat other KV asics to be 2-pipe
    {
        m_pipes = 2;
    }

    // @todo: VI
    // Move this to VI code path once created
    if (m_settings.isTonga)
    {
        m_pipes = 8;
    }
    else if (m_settings.isIceland)
    {
        m_pipes = 2;
    }
    else if (m_settings.isFiji)
    {
        m_pipes = 16;
    }

    if (valid)
    {
        valid = InitTileSettingTable(pRegValue->pTileConfig, pRegValue->noOfEntries);
    }
    if (valid)
    {
        valid = InitMacroTileCfgTable(pRegValue->pMacroTileConfig, pRegValue->noOfMacroEntries);
    }

    return valid;
}

/**
***************************************************************************************************
*   CIAddrLib::HwlPostCheckTileIndex
*
*   @brief
*       Map a tile setting to index if curIndex is invalid, otherwise check if curIndex matches
*       tile mode/type/info and change the index if needed
*   @return
*       Tile index.
***************************************************************************************************
*/
INT_32 CIAddrLib::HwlPostCheckTileIndex(
    const ADDR_TILEINFO* pInfo,     ///< [in] Tile Info
    AddrTileMode         mode,      ///< [in] Tile mode
    AddrTileType         type,      ///< [in] Tile type
    INT                  curIndex   ///< [in] Current index assigned in HwlSetupTileInfo
    ) const
{
    INT_32 index = curIndex;

    if (mode == ADDR_TM_LINEAR_GENERAL)
    {
        index = TileIndexLinearGeneral;
    }
    else
    {
        BOOL_32 macroTiled = IsMacroTiled(mode);

        // We need to find a new index if either of them is true
        // 1. curIndex is invalid
        // 2. tile mode is changed
        // 3. tile info does not match for macro tiled
        if ((index == TileIndexInvalid)         ||
            (mode != m_tileTable[index].mode)   ||
            (macroTiled && pInfo->pipeConfig != m_tileTable[index].info.pipeConfig))
        {
            for (index = 0; index < static_cast<INT_32>(m_noOfEntries); index++)
            {
                if (macroTiled)
                {
                    // macro tile modes need all to match
                    if ((pInfo->pipeConfig == m_tileTable[index].info.pipeConfig) &&
                        (mode == m_tileTable[index].mode) &&
                        (type == m_tileTable[index].type))
                    {
                        // tileSplitBytes stored in m_tileTable is only valid for depth entries
                        if (type == ADDR_DEPTH_SAMPLE_ORDER)
                        {
                            if (pInfo->tileSplitBytes == m_tileTable[index].info.tileSplitBytes)
                            {
                                break;
                            }
                        }
                        else // other entries are determined by other 3 fields
                        {
                            break;
                        }
                    }
                }
                else if (mode == ADDR_TM_LINEAR_ALIGNED)
                {
                    // linear mode only needs tile mode to match
                    if (mode == m_tileTable[index].mode)
                    {
                        break;
                    }
                }
                else
                {
                    // micro tile modes only need tile mode and tile type to match
                    if (mode == m_tileTable[index].mode &&
                        type == m_tileTable[index].type)
                    {
                        break;
                    }
                }
            }
        }
    }

    ADDR_ASSERT(index < static_cast<INT_32>(m_noOfEntries));

    if (index >= static_cast<INT_32>(m_noOfEntries))
    {
        index = TileIndexInvalid;
    }

    return index;
}

/**
***************************************************************************************************
*   CIAddrLib::HwlSetupTileCfg
*
*   @brief
*       Map tile index to tile setting.
*   @return
*       ADDR_E_RETURNCODE
***************************************************************************************************
*/
ADDR_E_RETURNCODE CIAddrLib::HwlSetupTileCfg(
    INT_32          index,          ///< [in] Tile index
    INT_32          macroModeIndex, ///< [in] Index in macro tile mode table(CI)
    ADDR_TILEINFO*  pInfo,          ///< [out] Tile Info
    AddrTileMode*   pMode,          ///< [out] Tile mode
    AddrTileType*   pType           ///< [out] Tile type
    ) const
{
    ADDR_E_RETURNCODE returnCode = ADDR_OK;

    // Global flag to control usage of tileIndex
    if (UseTileIndex(index))
    {
        if (static_cast<UINT_32>(index) >= m_noOfEntries)
        {
            returnCode = ADDR_INVALIDPARAMS;
        }
        else
        {
            const ADDR_TILECONFIG* pCfgTable = GetTileSetting(index);

            if (pInfo != NULL)
            {
                if (IsMacroTiled(pCfgTable->mode))
                {
                    ADDR_ASSERT(((macroModeIndex != TileIndexInvalid)
                        && (macroModeIndex != TileIndexNoMacroIndex)));
                    // Here we used tile_bytes to replace of tile_split
                    // According info as below:
                    // "tile_split_c = MIN(ROW_SIZE, tile_split)
                    // "tile_bytes = MIN(tile_split_c, num_samples * tile_bytes_1x)
                    // when using tile_bytes replacing of tile_split, the result of
                    // alignment and others(such as slicesPerTile) are unaffected -
                    // since if tile_split_c is larger, split won't happen, otherwise
                    // (num_samples * tile_bytes_1x is larger), a correct tile_split is
                    // returned.
                    *pInfo = m_macroTileTable[macroModeIndex];

                    if (pCfgTable->type == ADDR_DEPTH_SAMPLE_ORDER)
                    {
                        pInfo->tileSplitBytes = pCfgTable->info.tileSplitBytes;
                    }
                    pInfo->pipeConfig = pCfgTable->info.pipeConfig;
                }
                else // 1D and linear modes, we return default value stored in table
                {
                    *pInfo = pCfgTable->info;
                }
            }

            if (pMode != NULL)
            {
                *pMode = pCfgTable->mode;
            }

            if (pType != NULL)
            {
                *pType = pCfgTable->type;
            }
        }
    }

    return returnCode;
}

/**
***************************************************************************************************
*   CIAddrLib::HwlComputeSurfaceInfo
*
*   @brief
*       Entry of ci's ComputeSurfaceInfo
*   @return
*       ADDR_E_RETURNCODE
***************************************************************************************************
*/
ADDR_E_RETURNCODE CIAddrLib::HwlComputeSurfaceInfo(
    const ADDR_COMPUTE_SURFACE_INFO_INPUT*  pIn,    ///< [in] input structure
    ADDR_COMPUTE_SURFACE_INFO_OUTPUT*       pOut    ///< [out] output structure
    ) const
{
    // If tileIndex is invalid, force macroModeIndex to be invalid, too
    if (pIn->tileIndex == TileIndexInvalid)
    {
        pOut->macroModeIndex = TileIndexInvalid;
    }

    ADDR_E_RETURNCODE retCode = SIAddrLib::HwlComputeSurfaceInfo(pIn,pOut);

    if (pOut->macroModeIndex == TileIndexNoMacroIndex)
    {
        pOut->macroModeIndex = TileIndexInvalid;
    }

    return retCode;
}

/**
***************************************************************************************************
*   CIAddrLib::HwlFmaskSurfaceInfo
*   @brief
*       Entry of r800's ComputeFmaskInfo
*   @return
*       ADDR_E_RETURNCODE
***************************************************************************************************
*/
ADDR_E_RETURNCODE CIAddrLib::HwlComputeFmaskInfo(
    const ADDR_COMPUTE_FMASK_INFO_INPUT*    pIn,   ///< [in] input structure
    ADDR_COMPUTE_FMASK_INFO_OUTPUT*         pOut   ///< [out] output structure
    )
{
    ADDR_E_RETURNCODE retCode = ADDR_OK;

    ADDR_TILEINFO tileInfo = {0};
    ADDR_COMPUTE_FMASK_INFO_INPUT fmaskIn;
    fmaskIn = *pIn;

    AddrTileMode tileMode = pIn->tileMode;

    // Use internal tile info if pOut does not have a valid pTileInfo
    if (pOut->pTileInfo == NULL)
    {
        pOut->pTileInfo = &tileInfo;
    }

    ADDR_ASSERT(tileMode == ADDR_TM_2D_TILED_THIN1     ||
                tileMode == ADDR_TM_3D_TILED_THIN1     ||
                tileMode == ADDR_TM_PRT_TILED_THIN1    ||
                tileMode == ADDR_TM_PRT_2D_TILED_THIN1 ||
                tileMode == ADDR_TM_PRT_3D_TILED_THIN1);

    ADDR_ASSERT(m_tileTable[14].mode == ADDR_TM_2D_TILED_THIN1);
    ADDR_ASSERT(m_tileTable[15].mode == ADDR_TM_3D_TILED_THIN1);

    // The only valid tile modes for fmask are 2D_THIN1 and 3D_THIN1 plus non-displayable
    INT_32 tileIndex = tileMode == ADDR_TM_2D_TILED_THIN1 ? 14 : 15;
    ADDR_SURFACE_FLAGS flags = {{0}};
    flags.fmask = 1;

    INT_32 macroModeIndex = TileIndexInvalid;

    UINT_32 numSamples = pIn->numSamples;
    UINT_32 numFrags = pIn->numFrags == 0 ? numSamples : pIn->numFrags;

    UINT_32 bpp = QLog2(numFrags);

    // EQAA needs one more bit
    if (numSamples > numFrags)
    {
        bpp++;
    }

    if (bpp == 3)
    {
        bpp = 4;
    }

    bpp = Max(8u, bpp * numSamples);

    macroModeIndex = HwlComputeMacroModeIndex(tileIndex, flags, bpp, numSamples, pOut->pTileInfo);

    fmaskIn.tileIndex = tileIndex;
    fmaskIn.pTileInfo = pOut->pTileInfo;
    pOut->macroModeIndex = macroModeIndex;
    pOut->tileIndex = tileIndex;

    retCode = DispatchComputeFmaskInfo(&fmaskIn, pOut);

    if (retCode == ADDR_OK)
    {
        pOut->tileIndex =
            HwlPostCheckTileIndex(pOut->pTileInfo, pIn->tileMode, ADDR_NON_DISPLAYABLE,
                                  pOut->tileIndex);
    }

    // Resets pTileInfo to NULL if the internal tile info is used
    if (pOut->pTileInfo == &tileInfo)
    {
        pOut->pTileInfo = NULL;
    }

    return retCode;
}

/**
***************************************************************************************************
*   CIAddrLib::HwlFmaskPreThunkSurfInfo
*
*   @brief
*       Some preparation before thunking a ComputeSurfaceInfo call for Fmask
*   @return
*       ADDR_E_RETURNCODE
***************************************************************************************************
*/
VOID CIAddrLib::HwlFmaskPreThunkSurfInfo(
    const ADDR_COMPUTE_FMASK_INFO_INPUT*    pFmaskIn,   ///< [in] Input of fmask info
    const ADDR_COMPUTE_FMASK_INFO_OUTPUT*   pFmaskOut,  ///< [in] Output of fmask info
    ADDR_COMPUTE_SURFACE_INFO_INPUT*        pSurfIn,    ///< [out] Input of thunked surface info
    ADDR_COMPUTE_SURFACE_INFO_OUTPUT*       pSurfOut    ///< [out] Output of thunked surface info
    ) const
{
    pSurfIn->tileIndex = pFmaskIn->tileIndex;
    pSurfOut->macroModeIndex  = pFmaskOut->macroModeIndex;
}

/**
***************************************************************************************************
*   CIAddrLib::HwlFmaskPostThunkSurfInfo
*
*   @brief
*       Copy hwl extra field after calling thunked ComputeSurfaceInfo
*   @return
*       ADDR_E_RETURNCODE
***************************************************************************************************
*/
VOID CIAddrLib::HwlFmaskPostThunkSurfInfo(
    const ADDR_COMPUTE_SURFACE_INFO_OUTPUT* pSurfOut,   ///< [in] Output of surface info
    ADDR_COMPUTE_FMASK_INFO_OUTPUT* pFmaskOut           ///< [out] Output of fmask info
    ) const
{
    pFmaskOut->tileIndex = pSurfOut->tileIndex;
    pFmaskOut->macroModeIndex = pSurfOut->macroModeIndex;
}

/**
***************************************************************************************************
*   CIAddrLib::HwlDegradeThickTileMode
*
*   @brief
*       Degrades valid tile mode for thick modes if needed
*
*   @return
*       Suitable tile mode
***************************************************************************************************
*/
AddrTileMode CIAddrLib::HwlDegradeThickTileMode(
    AddrTileMode        baseTileMode,   ///< [in] base tile mode
    UINT_32             numSlices,      ///< [in] current number of slices
    UINT_32*            pBytesPerTile   ///< [in/out] pointer to bytes per slice
    ) const
{
    return baseTileMode;
}

/**
***************************************************************************************************
*   CIAddrLib::HwlOverrideTileMode
*
*   @brief
*       Override THICK to THIN, for specific formats on CI
*
*   @return
*       Suitable tile mode
*
***************************************************************************************************
*/
BOOL_32 CIAddrLib::HwlOverrideTileMode(
    const ADDR_COMPUTE_SURFACE_INFO_INPUT*  pIn,       ///< [in] input structure
    AddrTileMode*                           pTileMode, ///< [in/out] pointer to the tile mode
    AddrTileType*                           pTileType  ///< [in/out] pointer to the tile type
    ) const
{
    BOOL_32 bOverrided = FALSE;
    AddrTileMode tileMode = *pTileMode;

    // currently, all CI/VI family do not
    // support ADDR_TM_PRT_2D_TILED_THICK,ADDR_TM_PRT_3D_TILED_THICK and
    // ADDR_TM_PRT_2D_TILED_THIN1, ADDR_TM_PRT_3D_TILED_THIN1
    switch (tileMode)
    {
        case ADDR_TM_PRT_2D_TILED_THICK:
        case ADDR_TM_PRT_3D_TILED_THICK:
            tileMode = ADDR_TM_PRT_TILED_THICK;
            break;
        case ADDR_TM_PRT_2D_TILED_THIN1:
        case ADDR_TM_PRT_3D_TILED_THIN1:
            tileMode = ADDR_TM_PRT_TILED_THIN1;
            break;
        default:
            break;
    }

    // UBTS#404321, we do not need such overriding, as THICK+THICK entries removed from the tile-mode table
    if (!m_settings.isBonaire)
    {
        UINT_32 thickness = ComputeSurfaceThickness(tileMode);

        // tile_thickness = (array_mode == XTHICK) ? 8 : ((array_mode == THICK) ? 4 : 1)
        if (thickness > 1)
        {
            switch (pIn->format)
            {
                // see //gfxip/gcB/devel/cds/src/verif/tc/models/csim/tcp.cpp
                // tcpError("Thick micro tiling is not supported for format...
                case ADDR_FMT_X24_8_32_FLOAT:
                case ADDR_FMT_32_AS_8:
                case ADDR_FMT_32_AS_8_8:
                case ADDR_FMT_32_AS_32_32_32_32:

                // packed formats
                case ADDR_FMT_GB_GR:
                case ADDR_FMT_BG_RG:
                case ADDR_FMT_1_REVERSED:
                case ADDR_FMT_1:
                case ADDR_FMT_BC1:
                case ADDR_FMT_BC2:
                case ADDR_FMT_BC3:
                case ADDR_FMT_BC4:
                case ADDR_FMT_BC5:
                case ADDR_FMT_BC6:
                case ADDR_FMT_BC7:
                    switch (tileMode)
                    {
                        case ADDR_TM_1D_TILED_THICK:
                            tileMode    = ADDR_TM_1D_TILED_THIN1;
                            break;

                        case ADDR_TM_2D_TILED_XTHICK:
                        case ADDR_TM_2D_TILED_THICK:
                            tileMode    = ADDR_TM_2D_TILED_THIN1;
                            break;

                        case ADDR_TM_3D_TILED_XTHICK:
                        case ADDR_TM_3D_TILED_THICK:
                            tileMode    = ADDR_TM_3D_TILED_THIN1;
                            break;

                        case ADDR_TM_PRT_TILED_THICK:
                            tileMode    = ADDR_TM_PRT_TILED_THIN1;
                            break;

                        case ADDR_TM_PRT_2D_TILED_THICK:
                            tileMode    = ADDR_TM_PRT_2D_TILED_THIN1;
                            break;

                        case ADDR_TM_PRT_3D_TILED_THICK:
                            tileMode    = ADDR_TM_PRT_3D_TILED_THIN1;
                            break;

                        default:
                            break;

                    }

                    // Switch tile type from thick to thin
                    if (tileMode != *pTileMode)
                    {
                        // see tileIndex: 13-18
                        *pTileType = ADDR_NON_DISPLAYABLE;
                    }

                    break;
                default:
                    break;
            }
        }
    }

    if (tileMode != *pTileMode)
    {
        *pTileMode = tileMode;
        bOverrided = TRUE;
    }

    return bOverrided;
}

/**
***************************************************************************************************
*   CiAddrLib::GetPrtSwitchP4Threshold
*
*   @brief
*       Return the threshold of switching to P4_* instead of P16_* for PRT resources
***************************************************************************************************
*/
UINT_32 CIAddrLib::GetPrtSwitchP4Threshold() const
{
    UINT_32 threshold;

    switch (m_pipes)
    {
        case 8:
            threshold = 32;
            break;
        case 16:
            if (m_settings.isFiji)
            {
                threshold = 16;
            }
            else if (m_settings.isHawaii)
            {
                threshold = 8;
            }
            else
            {
                ///@todo add for possible new ASICs.
                ADDR_ASSERT_ALWAYS();
                threshold = 16;
            }
            break;
        default:
            ///@todo add for possible new ASICs.
            ADDR_ASSERT_ALWAYS();
            threshold = 32;
            break;
    }

    return threshold;
}

/**
***************************************************************************************************
*   CIAddrLib::HwlSetupTileInfo
*
*   @brief
*       Setup default value of tile info for SI
***************************************************************************************************
*/
VOID CIAddrLib::HwlSetupTileInfo(
    AddrTileMode                        tileMode,       ///< [in] Tile mode
    ADDR_SURFACE_FLAGS                  flags,          ///< [in] Surface type flags
    UINT_32                             bpp,            ///< [in] Bits per pixel
    UINT_32                             pitch,          ///< [in] Pitch in pixels
    UINT_32                             height,         ///< [in] Height in pixels
    UINT_32                             numSamples,     ///< [in] Number of samples
    ADDR_TILEINFO*                      pTileInfoIn,    ///< [in] Tile info input: NULL for default
    ADDR_TILEINFO*                      pTileInfoOut,   ///< [out] Tile info output
    AddrTileType                        inTileType,     ///< [in] Tile type
    ADDR_COMPUTE_SURFACE_INFO_OUTPUT*   pOut            ///< [out] Output
    ) const
{
    UINT_32 thickness = ComputeSurfaceThickness(tileMode);
    ADDR_TILEINFO* pTileInfo = pTileInfoOut;
    INT index = TileIndexInvalid;
    INT macroModeIndex = TileIndexInvalid;

    // Fail-safe code
    if (!IsLinear(tileMode))
    {
        // Thick tile modes must use thick micro tile mode but Bonaire does not support due to
        // old derived netlists (UBTS 404321)
        if (thickness > 1)
        {
            if (m_settings.isBonaire)
            {
                inTileType = ADDR_NON_DISPLAYABLE;
            }
            else if ((m_allowNonDispThickModes == FALSE) || (inTileType != ADDR_NON_DISPLAYABLE))
            {
                inTileType = ADDR_THICK;
            }
        }
        // 128 bpp tiling must be non-displayable.
        // Fmask reuse color buffer's entry but bank-height field can be from another entry
        // To simplify the logic, fmask entry should be picked from non-displayable ones
        else if (bpp == 128 || flags.fmask)
        {
            inTileType = ADDR_NON_DISPLAYABLE;
        }
        // These two modes only have non-disp entries though they can be other micro tile modes
        else if (tileMode == ADDR_TM_3D_TILED_THIN1 || tileMode == ADDR_TM_PRT_3D_TILED_THIN1)
        {
            inTileType = ADDR_NON_DISPLAYABLE;
        }

        if (flags.depth || flags.stencil)
        {
            inTileType = ADDR_DEPTH_SAMPLE_ORDER;
        }
    }

    if (IsTileInfoAllZero(pTileInfo))
    {
        // See table entries 0-4
        if (flags.depth || flags.stencil)
        {
            if (flags.depth && flags.tcCompatible)
            {
                // tileSize = bpp * numSamples * 8 * 8 / 8
                UINT_32 tileSize = bpp * numSamples * 8;

                // Texure readable depth surface should not be split
                switch (tileSize)
                {
                    case 128:
                        index = 1;
                        break;
                    case 256:
                        index = 2;
                        break;
                    case 512:
                        index = 3;
                        break;
                    default:
                        index = 4;
                        break;
                }
            }
            else
            {
                // Depth and stencil need to use the same index, thus the pre-defined tile_split
                // can meet the requirement to choose the same macro mode index
                // uncompressed depth/stencil are not supported for now
                switch (numSamples)
                {
                    case 1:
                        index = 0;
                        break;
                    case 2:
                    case 4:
                        index = 1;
                        break;
                    case 8:
                        index = 2;
                        break;
                    default:
                        break;
                }
            }
        }

        // See table entries 5-6
        if (inTileType == ADDR_DEPTH_SAMPLE_ORDER)
        {
            switch (tileMode)
            {
                case ADDR_TM_1D_TILED_THIN1:
                    index = 5;
                    break;
                case ADDR_TM_PRT_TILED_THIN1:
                    index = 6;
                    break;
                default:
                    break;
            }
        }

        // See table entries 8-12
        if (inTileType == ADDR_DISPLAYABLE)
        {
            switch (tileMode)
            {
                case ADDR_TM_1D_TILED_THIN1:
                    index = 9;
                    break;
                case ADDR_TM_2D_TILED_THIN1:
                    index = 10;
                    break;
                case ADDR_TM_PRT_TILED_THIN1:
                    index = 11;
                    break;
                default:
                    break;
            }
        }

        // See table entries 13-18
        if (inTileType == ADDR_NON_DISPLAYABLE)
        {
            switch (tileMode)
            {
                case ADDR_TM_1D_TILED_THIN1:
                    index = 13;
                    break;
                case ADDR_TM_2D_TILED_THIN1:
                    index = 14;
                    break;
                case ADDR_TM_3D_TILED_THIN1:
                    index = 15;
                    break;
                case ADDR_TM_PRT_TILED_THIN1:
                    index = 16;
                    break;
                default:
                    break;
            }
        }

        // See table entries 19-26
        if (thickness > 1)
        {
            switch (tileMode)
            {
            case ADDR_TM_1D_TILED_THICK:
                    //special check for bonaire, for the compatablity between old KMD and new UMD for bonaire
                    index = ((inTileType == ADDR_THICK) || m_settings.isBonaire) ? 19 : 18;
                    break;
            case ADDR_TM_2D_TILED_THICK:
                    // special check for bonaire, for the compatablity between old KMD and new UMD for bonaire
                    index = ((inTileType == ADDR_THICK) || m_settings.isBonaire) ? 20 : 24;
                    break;
                case ADDR_TM_3D_TILED_THICK:
                    index = 21;
                    break;
                case ADDR_TM_PRT_TILED_THICK:
                    index = 22;
                    break;
                case ADDR_TM_2D_TILED_XTHICK:
                    index = 25;
                    break;
                case ADDR_TM_3D_TILED_XTHICK:
                    index = 26;
                    break;
                default:
                    break;
            }
        }

        // See table entries 27-30
        if (inTileType == ADDR_ROTATED)
        {
            switch (tileMode)
            {
                case ADDR_TM_1D_TILED_THIN1:
                    index = 27;
                    break;
                case ADDR_TM_2D_TILED_THIN1:
                    index = 28;
                    break;
                case ADDR_TM_PRT_TILED_THIN1:
                    index = 29;
                    break;
                case ADDR_TM_PRT_2D_TILED_THIN1:
                    index = 30;
                    break;
                default:
                    break;
            }
        }

        if (m_pipes >= 8)
        {
            ADDR_ASSERT((index + 1) < static_cast<INT_32>(m_noOfEntries));
            // Only do this when tile mode table is updated.
            if (((tileMode == ADDR_TM_PRT_TILED_THIN1) || (tileMode == ADDR_TM_PRT_TILED_THICK)) &&
                (m_tileTable[index+1].mode == tileMode))
            {
                UINT_32 bytesXSamples = bpp * numSamples / 8;
                UINT_32 bytesXThickness = bpp * thickness / 8;
                UINT_32 switchP4Threshold = GetPrtSwitchP4Threshold();

                if ((bytesXSamples > switchP4Threshold) || (bytesXThickness > switchP4Threshold))
                {
                    // Pick next 4 pipe entry
                    index += 1;
                }
            }
        }
    }
    else
    {
        // A pre-filled tile info is ready
        index = pOut->tileIndex;
        macroModeIndex = pOut->macroModeIndex;

        // pass tile type back for post tile index compute
        pOut->tileType = inTileType;
    }

    // We only need to set up tile info if there is a valid index but macroModeIndex is invalid
    if (index != TileIndexInvalid && macroModeIndex == TileIndexInvalid)
    {
        macroModeIndex = HwlComputeMacroModeIndex(index, flags, bpp, numSamples, pTileInfo);

        /// Copy to pOut->tileType/tileIndex/macroModeIndex
        pOut->tileIndex = index;
        pOut->tileType = m_tileTable[index].type; // Or inTileType, the samea
        pOut->macroModeIndex = macroModeIndex;
    }
    else if (tileMode == ADDR_TM_LINEAR_GENERAL)
    {
        pOut->tileIndex = TileIndexLinearGeneral;

        // Copy linear-aligned entry??
        *pTileInfo = m_tileTable[8].info;
    }
    else if (tileMode == ADDR_TM_LINEAR_ALIGNED)
    {
        pOut->tileIndex = 8;
        *pTileInfo = m_tileTable[8].info;
    }
}

/**
***************************************************************************************************
*   CIAddrLib::ReadGbTileMode
*
*   @brief
*       Convert GB_TILE_MODE HW value to ADDR_TILE_CONFIG.
*   @return
*       NA.
***************************************************************************************************
*/
VOID CIAddrLib::ReadGbTileMode(
    UINT_32             regValue,   ///< [in] GB_TILE_MODE register
    ADDR_TILECONFIG*    pCfg        ///< [out] output structure
    ) const
{
    GB_TILE_MODE gbTileMode;
    gbTileMode.val = regValue;

    pCfg->type = static_cast<AddrTileType>(gbTileMode.f.micro_tile_mode_new);
    pCfg->info.pipeConfig = static_cast<AddrPipeCfg>(gbTileMode.f.pipe_config + 1);

    if (pCfg->type == ADDR_DEPTH_SAMPLE_ORDER)
    {
        pCfg->info.tileSplitBytes = 64 << gbTileMode.f.tile_split;
    }
    else
    {
        pCfg->info.tileSplitBytes = 1 << gbTileMode.f.sample_split;
    }

    UINT_32 regArrayMode = gbTileMode.f.array_mode;

    pCfg->mode = static_cast<AddrTileMode>(regArrayMode);

    switch (regArrayMode)
    {
        case 5:
            pCfg->mode = ADDR_TM_PRT_TILED_THIN1;
            break;
        case 6:
            pCfg->mode = ADDR_TM_PRT_2D_TILED_THIN1;
            break;
        case 8:
            pCfg->mode = ADDR_TM_2D_TILED_XTHICK;
            break;
        case 9:
            pCfg->mode = ADDR_TM_PRT_TILED_THICK;
            break;
        case 0xa:
            pCfg->mode = ADDR_TM_PRT_2D_TILED_THICK;
            break;
        case 0xb:
            pCfg->mode = ADDR_TM_PRT_3D_TILED_THIN1;
            break;
        case 0xe:
            pCfg->mode = ADDR_TM_3D_TILED_XTHICK;
            break;
        case 0xf:
            pCfg->mode = ADDR_TM_PRT_3D_TILED_THICK;
            break;
        default:
            break;
    }

    // Fail-safe code for these always convert tile info, as the non-macro modes
    // return the entry of tile mode table directly without looking up macro mode table
    if (!IsMacroTiled(pCfg->mode))
    {
        pCfg->info.banks = 2;
        pCfg->info.bankWidth = 1;
        pCfg->info.bankHeight = 1;
        pCfg->info.macroAspectRatio = 1;
        pCfg->info.tileSplitBytes = 64;
    }
}

/**
***************************************************************************************************
*   CIAddrLib::InitTileSettingTable
*
*   @brief
*       Initialize the ADDR_TILE_CONFIG table.
*   @return
*       TRUE if tile table is correctly initialized
***************************************************************************************************
*/
BOOL_32 CIAddrLib::InitTileSettingTable(
    const UINT_32*  pCfg,           ///< [in] Pointer to table of tile configs
    UINT_32         noOfEntries     ///< [in] Numbe of entries in the table above
    )
{
    BOOL_32 initOk = TRUE;

    ADDR_ASSERT(noOfEntries <= TileTableSize);

    memset(m_tileTable, 0, sizeof(m_tileTable));

    if (noOfEntries != 0)
    {
        m_noOfEntries = noOfEntries;
    }
    else
    {
        m_noOfEntries = TileTableSize;
    }

    if (pCfg) // From Client
    {
        for (UINT_32 i = 0; i < m_noOfEntries; i++)
        {
            ReadGbTileMode(*(pCfg + i), &m_tileTable[i]);
        }
    }
    else
    {
        ADDR_ASSERT_ALWAYS();
        initOk = FALSE;
    }

    if (initOk)
    {
        ADDR_ASSERT(m_tileTable[TILEINDEX_LINEAR_ALIGNED].mode == ADDR_TM_LINEAR_ALIGNED);

        if (m_settings.isBonaire == FALSE)
        {
            // Check if entry 18 is "thick+thin" combination
            if ((m_tileTable[18].mode == ADDR_TM_1D_TILED_THICK) &&
                (m_tileTable[18].type == ADDR_NON_DISPLAYABLE))
            {
                m_allowNonDispThickModes = TRUE;
                ADDR_ASSERT(m_tileTable[24].mode == ADDR_TM_2D_TILED_THICK);
            }
        }
        else
        {
            m_allowNonDispThickModes = TRUE;
        }

        // Assume the first entry is always programmed with full pipes
        m_pipes = HwlGetPipes(&m_tileTable[0].info);
    }

    return initOk;
}

/**
***************************************************************************************************
*   CIAddrLib::ReadGbMacroTileCfg
*
*   @brief
*       Convert GB_MACRO_TILE_CFG HW value to ADDR_TILE_CONFIG.
*   @return
*       NA.
***************************************************************************************************
*/
VOID CIAddrLib::ReadGbMacroTileCfg(
    UINT_32             regValue,   ///< [in] GB_MACRO_TILE_MODE register
    ADDR_TILEINFO*      pCfg        ///< [out] output structure
    ) const
{
    GB_MACROTILE_MODE gbTileMode;
    gbTileMode.val = regValue;

    pCfg->bankHeight = 1 << gbTileMode.f.bank_height;
    pCfg->bankWidth = 1 << gbTileMode.f.bank_width;
    pCfg->banks = 1 << (gbTileMode.f.num_banks + 1);
    pCfg->macroAspectRatio = 1 << gbTileMode.f.macro_tile_aspect;
}

/**
***************************************************************************************************
*   CIAddrLib::InitMacroTileCfgTable
*
*   @brief
*       Initialize the ADDR_MACRO_TILE_CONFIG table.
*   @return
*       TRUE if macro tile table is correctly initialized
***************************************************************************************************
*/
BOOL_32 CIAddrLib::InitMacroTileCfgTable(
    const UINT_32*  pCfg,           ///< [in] Pointer to table of tile configs
    UINT_32         noOfMacroEntries     ///< [in] Numbe of entries in the table above
    )
{
    BOOL_32 initOk = TRUE;

    ADDR_ASSERT(noOfMacroEntries <= MacroTileTableSize);

    memset(m_macroTileTable, 0, sizeof(m_macroTileTable));

    if (noOfMacroEntries != 0)
    {
        m_noOfMacroEntries = noOfMacroEntries;
    }
    else
    {
        m_noOfMacroEntries = MacroTileTableSize;
    }

    if (pCfg) // From Client
    {
        for (UINT_32 i = 0; i < m_noOfMacroEntries; i++)
        {
            ReadGbMacroTileCfg(*(pCfg + i), &m_macroTileTable[i]);

            m_macroTileTable[i].tileSplitBytes = 64 << (i % 8);
        }
    }
    else
    {
        ADDR_ASSERT_ALWAYS();
        initOk = FALSE;
    }
    return initOk;
}

/**
***************************************************************************************************
*   CIAddrLib::HwlComputeMacroModeIndex
*
*   @brief
*       Computes macro tile mode index
*   @return
*       TRUE if macro tile table is correctly initialized
***************************************************************************************************
*/
INT_32 CIAddrLib::HwlComputeMacroModeIndex(
    INT_32              tileIndex,      ///< [in] Tile mode index
    ADDR_SURFACE_FLAGS  flags,          ///< [in] Surface flags
    UINT_32             bpp,            ///< [in] Bit per pixel
    UINT_32             numSamples,     ///< [in] Number of samples
    ADDR_TILEINFO*      pTileInfo,      ///< [out] Pointer to ADDR_TILEINFO
    AddrTileMode*       pTileMode,      ///< [out] Pointer to AddrTileMode
    AddrTileType*       pTileType       ///< [out] Pointer to AddrTileType
    ) const
{
    INT_32 macroModeIndex = TileIndexInvalid;

    if (flags.tcCompatible && flags.stencil)
    {
        // Don't compute macroModeIndex for tc compatible stencil surface
        macroModeIndex = TileIndexNoMacroIndex;
    }
    else
    {
        AddrTileMode tileMode = m_tileTable[tileIndex].mode;
        AddrTileType tileType = m_tileTable[tileIndex].type;
        UINT_32 thickness = ComputeSurfaceThickness(tileMode);

        if (!IsMacroTiled(tileMode))
        {
            *pTileInfo = m_tileTable[tileIndex].info;
            macroModeIndex = TileIndexNoMacroIndex;
        }
        else
        {
            UINT_32 tileBytes1x = BITS_TO_BYTES(bpp * MicroTilePixels * thickness);
            UINT_32 tileSplit;

            if (m_tileTable[tileIndex].type == ADDR_DEPTH_SAMPLE_ORDER)
            {
                // Depth entries store real tileSplitBytes
                tileSplit = m_tileTable[tileIndex].info.tileSplitBytes;
            }
            else
            {
                // Non-depth entries store a split factor
                UINT_32 sampleSplit = m_tileTable[tileIndex].info.tileSplitBytes;
                UINT_32 colorTileSplit = Max(256u, sampleSplit * tileBytes1x);

                tileSplit = colorTileSplit;
            }

            UINT_32 tileSplitC = Min(m_rowSize, tileSplit);
            UINT_32 tileBytes;

            if (flags.fmask)
            {
                tileBytes = Min(tileSplitC, tileBytes1x);
            }
            else
            {
                tileBytes = Min(tileSplitC, numSamples * tileBytes1x);
            }

            if (tileBytes < 64)
            {
                tileBytes = 64;
            }

            macroModeIndex = Log2(tileBytes / 64);

            if (flags.prt || IsPrtTileMode(tileMode))
            {
                // Unknown - assume it is 1/2 of table size
                const UINT_32 PrtMacroModeOffset = MacroTileTableSize / 2;

                macroModeIndex += PrtMacroModeOffset;
                *pTileInfo = m_macroTileTable[macroModeIndex];
            }
            else
            {
                *pTileInfo = m_macroTileTable[macroModeIndex];
            }

            pTileInfo->pipeConfig = m_tileTable[tileIndex].info.pipeConfig;

            if (m_tileTable[tileIndex].type != ADDR_DEPTH_SAMPLE_ORDER)
            {
                pTileInfo->tileSplitBytes = tileSplitC;
            }
            else
            {
                pTileInfo->tileSplitBytes = m_tileTable[tileIndex].info.tileSplitBytes;
            }
        }

        if (NULL != pTileMode)
        {
            *pTileMode = tileMode;
        }

        if (NULL != pTileType)
        {
            *pTileType = tileType;
        }
    }

    return macroModeIndex;
}

/**
***************************************************************************************************
*   CIAddrLib::HwlComputeTileDataWidthAndHeightLinear
*
*   @brief
*       Compute the squared cache shape for per-tile data (CMASK and HTILE) for linear layout
*
*   @return
*       N/A
*
*   @note
*       MacroWidth and macroHeight are measured in pixels
***************************************************************************************************
*/
VOID CIAddrLib::HwlComputeTileDataWidthAndHeightLinear(
    UINT_32*        pMacroWidth,     ///< [out] macro tile width
    UINT_32*        pMacroHeight,    ///< [out] macro tile height
    UINT_32         bpp,             ///< [in] bits per pixel
    ADDR_TILEINFO*  pTileInfo        ///< [in] tile info
    ) const
{
    ADDR_ASSERT(pTileInfo != NULL);

    UINT_32 numTiles;

    switch (pTileInfo->pipeConfig)
    {
        case ADDR_PIPECFG_P16_32x32_8x16:
        case ADDR_PIPECFG_P16_32x32_16x16:
        case ADDR_PIPECFG_P8_32x64_32x32:
        case ADDR_PIPECFG_P8_32x32_16x32:
        case ADDR_PIPECFG_P8_32x32_16x16:
        case ADDR_PIPECFG_P8_32x32_8x16:
        case ADDR_PIPECFG_P4_32x32:
            numTiles = 8;
            break;
        default:
            numTiles = 4;
            break;
    }

    *pMacroWidth    = numTiles * MicroTileWidth;
    *pMacroHeight   = numTiles * MicroTileHeight;
}

/**
***************************************************************************************************
*   CIAddrLib::HwlStereoCheckRightOffsetPadding
*
*   @brief
*       check if the height needs extra padding for stereo right eye offset, to avoid swizzling
*
*   @return
*       TRUE is the extra padding is needed
*
*   @note
*       Kalindi (Kabini) is the only one that needs this padding as there is a uncertain
*       possible HW issue where the right eye displays incorrectly with some type of swizzles, if
*       the right eye offset is not 64KB aligned - EPR#366461
*       Other Kaveri APUs also need the padding according to DXX team's report otherwise
*       corruption observed. - EPR#374788
***************************************************************************************************
*/
BOOL_32 CIAddrLib::HwlStereoCheckRightOffsetPadding() const
{
    BOOL_32 bNeedPadding = FALSE;

    if (m_settings.isKaveri)
    {
        bNeedPadding = TRUE;
    }

    return bNeedPadding;
}

/**
***************************************************************************************************
*   CIAddrLib::HwlComputeMetadataNibbleAddress
*
*   @brief
*        calculate meta data address based on input information
*
*   &parameter
*        uncompressedDataByteAddress - address of a pixel in color surface
*        dataBaseByteAddress         - base address of color surface
*        metadataBaseByteAddress     - base address of meta ram
*        metadataBitSize             - meta key size, 8 for DCC, 4 for cmask
*        elementBitSize              - element size of color surface
*        blockByteSize               - compression block size, 256 for DCC
*        pipeInterleaveBytes         - pipe interleave size
*        numOfPipes                  - number of pipes
*        numOfBanks                  - number of banks
*        numOfSamplesPerSplit        - number of samples per tile split
*   @return
*        meta data nibble address (nibble address is used to support DCC compatible cmask)
*
***************************************************************************************************
*/
UINT_64 CIAddrLib::HwlComputeMetadataNibbleAddress(
    UINT_64 uncompressedDataByteAddress,
    UINT_64 dataBaseByteAddress,
    UINT_64 metadataBaseByteAddress,
    UINT_32 metadataBitSize,
    UINT_32 elementBitSize,
    UINT_32 blockByteSize,
    UINT_32 pipeInterleaveBytes,
    UINT_32 numOfPipes,
    UINT_32 numOfBanks,
    UINT_32 numOfSamplesPerSplit) const
{
    ///--------------------------------------------------------------------------------------------
    /// Get pipe interleave, bank and pipe bits
    ///--------------------------------------------------------------------------------------------
    UINT_32 pipeInterleaveBits  = Log2(pipeInterleaveBytes);
    UINT_32 pipeBits            = Log2(numOfPipes);
    UINT_32 bankBits            = Log2(numOfBanks);

    ///--------------------------------------------------------------------------------------------
    /// Clear pipe and bank swizzles
    ///--------------------------------------------------------------------------------------------
    UINT_32 dataMacrotileBits        = pipeInterleaveBits + pipeBits + bankBits;
    UINT_32 metadataMacrotileBits    = pipeInterleaveBits + pipeBits + bankBits;

    UINT_64 dataMacrotileClearMask     = ~((1L << dataMacrotileBits) - 1);
    UINT_64 metadataMacrotileClearMask = ~((1L << metadataMacrotileBits) - 1);

    UINT_64 dataBaseByteAddressNoSwizzle = dataBaseByteAddress & dataMacrotileClearMask;
    UINT_64 metadataBaseByteAddressNoSwizzle = metadataBaseByteAddress & metadataMacrotileClearMask;

    ///--------------------------------------------------------------------------------------------
    /// Modify metadata base before adding in so that when final address is divided by data ratio,
    /// the base address returns to where it should be
    ///--------------------------------------------------------------------------------------------
    ADDR_ASSERT((0 != metadataBitSize));
    UINT_64 metadataBaseShifted = metadataBaseByteAddressNoSwizzle * blockByteSize * 8 /
                                  metadataBitSize;
    UINT_64 offset = uncompressedDataByteAddress -
                     dataBaseByteAddressNoSwizzle +
                     metadataBaseShifted;

    ///--------------------------------------------------------------------------------------------
    /// Save bank data bits
    ///--------------------------------------------------------------------------------------------
    UINT_32 lsb = pipeBits + pipeInterleaveBits;
    UINT_32 msb = bankBits - 1 + lsb;

    UINT_64 bankDataBits = AddrGetBits(offset, msb, lsb);

    ///--------------------------------------------------------------------------------------------
    /// Save pipe data bits
    ///--------------------------------------------------------------------------------------------
    lsb = pipeInterleaveBits;
    msb = pipeBits - 1 + lsb;

    UINT_64 pipeDataBits = AddrGetBits(offset, msb, lsb);

    ///--------------------------------------------------------------------------------------------
    /// Remove pipe and bank bits
    ///--------------------------------------------------------------------------------------------
    lsb = pipeInterleaveBits;
    msb = dataMacrotileBits - 1;

    UINT_64 offsetWithoutPipeBankBits = AddrRemoveBits(offset, msb, lsb);

    ADDR_ASSERT((0 != blockByteSize));
    UINT_64 blockInBankpipe = offsetWithoutPipeBankBits / blockByteSize;

    UINT_32 tileSize = 8 * 8 * elementBitSize/8 * numOfSamplesPerSplit;
    UINT_32 blocksInTile = tileSize / blockByteSize;

    if (0 == blocksInTile)
    {
        lsb = 0;
    }
    else
    {
        lsb = Log2(blocksInTile);
    }
    msb = bankBits - 1 + lsb;

    UINT_64 blockInBankpipeWithBankBits = AddrInsertBits(blockInBankpipe, bankDataBits, msb, lsb);

    /// NOTE *2 because we are converting to Nibble address in this step
    UINT_64 metaAddressInPipe = blockInBankpipeWithBankBits * 2 * metadataBitSize / 8;


    ///--------------------------------------------------------------------------------------------
    /// Reinsert pipe bits back into the final address
    ///--------------------------------------------------------------------------------------------
    lsb = pipeInterleaveBits + 1; ///<+1 due to Nibble address now gives interleave bits extra lsb.
    msb = pipeBits - 1 + lsb;
    UINT_64 metadataAddress = AddrInsertBits(metaAddressInPipe, pipeDataBits, msb, lsb);

    return metadataAddress;
}

/**
***************************************************************************************************
*   CIAddrLib::HwlPadDimensions
*
*   @brief
*       Helper function to pad dimensions
*
*   @return
*       N/A
*
***************************************************************************************************
*/
VOID CIAddrLib::HwlPadDimensions(
    AddrTileMode        tileMode,    ///< [in] tile mode
    UINT_32             bpp,         ///< [in] bits per pixel
    ADDR_SURFACE_FLAGS  flags,       ///< [in] surface flags
    UINT_32             numSamples,  ///< [in] number of samples
    ADDR_TILEINFO*      pTileInfo,   ///< [in/out] bank structure.
    UINT_32             padDims,     ///< [in] Dimensions to pad valid value 1,2,3
    UINT_32             mipLevel,    ///< [in] MipLevel
    UINT_32*            pPitch,      ///< [in/out] pitch in pixels
    UINT_32             pitchAlign,  ///< [in] pitch alignment
    UINT_32*            pHeight,     ///< [in/out] height in pixels
    UINT_32             heightAlign, ///< [in] height alignment
    UINT_32*            pSlices,     ///< [in/out] number of slices
    UINT_32             sliceAlign   ///< [in] number of slice alignment
    ) const
{
    if (m_settings.isVolcanicIslands &&
        flags.dccCompatible &&
        (numSamples > 1) &&
        (mipLevel == 0) &&
        IsMacroTiled(tileMode))
    {
        UINT_32 tileSizePerSample = BITS_TO_BYTES(bpp * MicroTileWidth * MicroTileHeight);
        UINT_32 samplesPerSplit  = pTileInfo->tileSplitBytes / tileSizePerSample;

        if (samplesPerSplit < numSamples)
        {
            UINT_32 dccFastClearByteAlign = HwlGetPipes(pTileInfo) * m_pipeInterleaveBytes * 256;
            UINT_32 bytesPerSplit = BITS_TO_BYTES((*pPitch) * (*pHeight) * bpp * samplesPerSplit);

            ADDR_ASSERT(IsPow2(dccFastClearByteAlign));

            if (0 != (bytesPerSplit & (dccFastClearByteAlign - 1)))
            {
                UINT_32 dccFastClearPixelAlign = dccFastClearByteAlign /
                                                BITS_TO_BYTES(bpp) /
                                                samplesPerSplit;
                UINT_32 macroTilePixelAlign = pitchAlign * heightAlign;

                if ((dccFastClearPixelAlign >= macroTilePixelAlign) &&
                    ((dccFastClearPixelAlign % macroTilePixelAlign) == 0))
                {
                    UINT_32 dccFastClearPitchAlignInMacroTile =
                        dccFastClearPixelAlign / macroTilePixelAlign;
                    UINT_32 heightInMacroTile = *pHeight / heightAlign;
                    UINT_32 dccFastClearPitchAlignInPixels;

                    while ((heightInMacroTile > 1) &&
                           ((heightInMacroTile % 2) == 0) &&
                           (dccFastClearPitchAlignInMacroTile > 1) &&
                           ((dccFastClearPitchAlignInMacroTile % 2) == 0))
                    {
                        heightInMacroTile >>= 1;
                        dccFastClearPitchAlignInMacroTile >>= 1;
                    }

                    dccFastClearPitchAlignInPixels = pitchAlign * dccFastClearPitchAlignInMacroTile;

                    if (IsPow2(dccFastClearPitchAlignInPixels))
                    {
                        *pPitch = PowTwoAlign((*pPitch), dccFastClearPitchAlignInPixels);
                    }
                    else
                    {
                        *pPitch += (dccFastClearPitchAlignInPixels - 1);
                        *pPitch /= dccFastClearPitchAlignInPixels;
                        *pPitch *= dccFastClearPitchAlignInPixels;
                    }
                }
            }
        }
    }
}

