/*
 * 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  siaddrlib.cpp
* @brief Contains the implementation for the SIAddrLib class.
***************************************************************************************************
*/

#include "siaddrlib.h"

#include "si_gb_reg.h"

#include "si_ci_vi_merged_enum.h"

#if BRAHMA_BUILD
#include "amdgpu_id.h"
#else
#include "si_id.h"
#endif

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

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

/**
***************************************************************************************************
*   SIAddrLib::SIAddrLib
*
*   @brief
*       Constructor
*
***************************************************************************************************
*/
SIAddrLib::SIAddrLib(const AddrClient* pClient) :
    EgBasedAddrLib(pClient),
    m_noOfEntries(0)
{
    m_class = SI_ADDRLIB;
    memset(&m_settings, 0, sizeof(m_settings));
}

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

/**
***************************************************************************************************
*   SIAddrLib::HwlGetPipes
*
*   @brief
*       Get number pipes
*   @return
*       num pipes
***************************************************************************************************
*/
UINT_32 SIAddrLib::HwlGetPipes(
    const ADDR_TILEINFO* pTileInfo    ///< [in] Tile info
    ) const
{
    UINT_32 numPipes;

    if (pTileInfo)
    {
        numPipes = GetPipePerSurf(pTileInfo->pipeConfig);
    }
    else
    {
        ADDR_ASSERT_ALWAYS();
        numPipes = m_pipes; // Suppose we should still have a global pipes
    }

    return numPipes;
}

/**
***************************************************************************************************
*   SIAddrLib::GetPipePerSurf
*   @brief
*       get pipe num base on inputing tileinfo->pipeconfig
*   @return
*       pipe number
***************************************************************************************************
*/
UINT_32 SIAddrLib::GetPipePerSurf(
    AddrPipeCfg pipeConfig   ///< [in] pipe config
    ) const
{
    UINT_32 numPipes = 0;

    switch (pipeConfig)
    {
        case ADDR_PIPECFG_P2:
            numPipes = 2;
            break;
        case ADDR_PIPECFG_P4_8x16:
        case ADDR_PIPECFG_P4_16x16:
        case ADDR_PIPECFG_P4_16x32:
        case ADDR_PIPECFG_P4_32x32:
            numPipes = 4;
            break;
        case ADDR_PIPECFG_P8_16x16_8x16:
        case ADDR_PIPECFG_P8_16x32_8x16:
        case ADDR_PIPECFG_P8_32x32_8x16:
        case ADDR_PIPECFG_P8_16x32_16x16:
        case ADDR_PIPECFG_P8_32x32_16x16:
        case ADDR_PIPECFG_P8_32x32_16x32:
        case ADDR_PIPECFG_P8_32x64_32x32:
            numPipes = 8;
            break;
        case ADDR_PIPECFG_P16_32x32_8x16:
        case ADDR_PIPECFG_P16_32x32_16x16:
            numPipes = 16;
            break;
        default:
            ADDR_ASSERT(!"Invalid pipe config");
            numPipes = m_pipes;
    }
    return numPipes;
}

/**
***************************************************************************************************
*   SIAddrLib::ComputePipeFromCoord
*
*   @brief
*       Compute pipe number from coordinates
*   @return
*       Pipe number
***************************************************************************************************
*/
UINT_32 SIAddrLib::ComputePipeFromCoord(
    UINT_32         x,              ///< [in] x coordinate
    UINT_32         y,              ///< [in] y coordinate
    UINT_32         slice,          ///< [in] slice index
    AddrTileMode    tileMode,       ///< [in] tile mode
    UINT_32         pipeSwizzle,    ///< [in] pipe swizzle
    BOOL_32         ignoreSE,       ///< [in] TRUE if shader engines are ignored
    ADDR_TILEINFO*  pTileInfo       ///< [in] Tile info
    ) const
{
    UINT_32 pipe;
    UINT_32 pipeBit0 = 0;
    UINT_32 pipeBit1 = 0;
    UINT_32 pipeBit2 = 0;
    UINT_32 pipeBit3 = 0;
    UINT_32 sliceRotation;
    UINT_32 numPipes = 0;

    UINT_32 tx = x / MicroTileWidth;
    UINT_32 ty = y / MicroTileHeight;
    UINT_32 x3 = _BIT(tx,0);
    UINT_32 x4 = _BIT(tx,1);
    UINT_32 x5 = _BIT(tx,2);
    UINT_32 x6 = _BIT(tx,3);
    UINT_32 y3 = _BIT(ty,0);
    UINT_32 y4 = _BIT(ty,1);
    UINT_32 y5 = _BIT(ty,2);
    UINT_32 y6 = _BIT(ty,3);

    switch (pTileInfo->pipeConfig)
    {
        case ADDR_PIPECFG_P2:
            pipeBit0 = x3 ^ y3;
            numPipes = 2;
            break;
        case ADDR_PIPECFG_P4_8x16:
            pipeBit0 = x4 ^ y3;
            pipeBit1 = x3 ^ y4;
            numPipes = 4;
            break;
        case ADDR_PIPECFG_P4_16x16:
            pipeBit0 = x3 ^ y3 ^ x4;
            pipeBit1 = x4 ^ y4;
            numPipes = 4;
            break;
        case ADDR_PIPECFG_P4_16x32:
            pipeBit0 = x3 ^ y3 ^ x4;
            pipeBit1 = x4 ^ y5;
            numPipes = 4;
            break;
        case ADDR_PIPECFG_P4_32x32:
            pipeBit0 = x3 ^ y3 ^ x5;
            pipeBit1 = x5 ^ y5;
            numPipes = 4;
            break;
        case ADDR_PIPECFG_P8_16x16_8x16:
            pipeBit0 = x4 ^ y3 ^ x5;
            pipeBit1 = x3 ^ y5;
            numPipes = 8;
            break;
        case ADDR_PIPECFG_P8_16x32_8x16:
            pipeBit0 = x4 ^ y3 ^ x5;
            pipeBit1 = x3 ^ y4;
            pipeBit2 = x4 ^ y5;
            numPipes = 8;
            break;
        case ADDR_PIPECFG_P8_16x32_16x16:
            pipeBit0 = x3 ^ y3 ^ x4;
            pipeBit1 = x5 ^ y4;
            pipeBit2 = x4 ^ y5;
            numPipes = 8;
            break;
        case ADDR_PIPECFG_P8_32x32_8x16:
            pipeBit0 = x4 ^ y3 ^ x5;
            pipeBit1 = x3 ^ y4;
            pipeBit2 = x5 ^ y5;
            numPipes = 8;
            break;
        case ADDR_PIPECFG_P8_32x32_16x16:
            pipeBit0 = x3 ^ y3 ^ x4;
            pipeBit1 = x4 ^ y4;
            pipeBit2 = x5 ^ y5;
            numPipes = 8;
            break;
        case ADDR_PIPECFG_P8_32x32_16x32:
            pipeBit0 = x3 ^ y3 ^ x4;
            pipeBit1 = x4 ^ y6;
            pipeBit2 = x5 ^ y5;
            numPipes = 8;
            break;
        case ADDR_PIPECFG_P8_32x64_32x32:
            pipeBit0 = x3 ^ y3 ^ x5;
            pipeBit1 = x6 ^ y5;
            pipeBit2 = x5 ^ y6;
            numPipes = 8;
            break;
        case ADDR_PIPECFG_P16_32x32_8x16:
            pipeBit0 = x4 ^ y3;
            pipeBit1 = x3 ^ y4;
            pipeBit2 = x5 ^ y6;
            pipeBit3 = x6 ^ y5;
            numPipes = 16;
            break;
        case ADDR_PIPECFG_P16_32x32_16x16:
            pipeBit0 = x3 ^ y3 ^ x4;
            pipeBit1 = x4 ^ y4;
            pipeBit2 = x5 ^ y6;
            pipeBit3 = x6 ^ y5;
            numPipes = 16;
            break;
        default:
            ADDR_UNHANDLED_CASE();
            break;
    }
    pipe = pipeBit0 | (pipeBit1 << 1) | (pipeBit2 << 2) | (pipeBit3 << 3);

    UINT_32 microTileThickness = ComputeSurfaceThickness(tileMode);

    //
    // Apply pipe rotation for the slice.
    //
    switch (tileMode)
    {
        case ADDR_TM_3D_TILED_THIN1:    //fall through thin
        case ADDR_TM_3D_TILED_THICK:    //fall through thick
        case ADDR_TM_3D_TILED_XTHICK:
            sliceRotation =
                Max(1, static_cast<INT_32>(numPipes / 2) - 1) * (slice / microTileThickness);
            break;
        default:
            sliceRotation = 0;
            break;
    }
    pipeSwizzle += sliceRotation;
    pipeSwizzle &= (numPipes - 1);

    pipe = pipe ^ pipeSwizzle;

    return pipe;
}

/**
***************************************************************************************************
*   SIAddrLib::ComputeTileCoordFromPipeAndElemIdx
*
*   @brief
*       Compute (x,y) of a tile within a macro tile from address
*   @return
*       Pipe number
***************************************************************************************************
*/
VOID SIAddrLib::ComputeTileCoordFromPipeAndElemIdx(
    UINT_32         elemIdx,          ///< [in] per pipe element index within a macro tile
    UINT_32         pipe,             ///< [in] pipe index
    AddrPipeCfg     pipeCfg,          ///< [in] pipe config
    UINT_32         pitchInMacroTile, ///< [in] surface pitch in macro tile
    UINT_32         x,                ///< [in] x coordinate of the (0,0) tile in a macro tile
    UINT_32         y,                ///< [in] y coordinate of the (0,0) tile in a macro tile
    UINT_32*        pX,               ///< [out] x coordinate
    UINT_32*        pY                ///< [out] y coordinate
    ) const
{
    UINT_32 pipebit0 = _BIT(pipe,0);
    UINT_32 pipebit1 = _BIT(pipe,1);
    UINT_32 pipebit2 = _BIT(pipe,2);
    UINT_32 pipebit3 = _BIT(pipe,3);
    UINT_32 elemIdx0 = _BIT(elemIdx,0);
    UINT_32 elemIdx1 = _BIT(elemIdx,1);
    UINT_32 elemIdx2 = _BIT(elemIdx,2);
    UINT_32 x3 = 0;
    UINT_32 x4 = 0;
    UINT_32 x5 = 0;
    UINT_32 x6 = 0;
    UINT_32 y3 = 0;
    UINT_32 y4 = 0;
    UINT_32 y5 = 0;
    UINT_32 y6 = 0;

    switch(pipeCfg)
    {
        case ADDR_PIPECFG_P2:
            x4 = elemIdx2;
            y4 = elemIdx1 ^ x4;
            y3 = elemIdx0 ^ x4;
            x3 = pipebit0 ^ y3;
            *pY = Bits2Number(2, y4, y3);
            *pX = Bits2Number(2, x4, x3);
            break;
        case ADDR_PIPECFG_P4_8x16:
            x4 = elemIdx1;
            y4 = elemIdx0 ^ x4;
            x3 = pipebit1 ^ y4;
            y3 = pipebit0 ^ x4;
            *pY = Bits2Number(2, y4, y3);
            *pX = Bits2Number(2, x4, x3);
            break;
        case ADDR_PIPECFG_P4_16x16:
            x4 = elemIdx1;
            y3 = elemIdx0 ^ x4;
            y4 = pipebit1 ^ x4;
            x3 = pipebit0 ^ y3 ^ x4;
            *pY = Bits2Number(2, y4, y3);
            *pX = Bits2Number(2, x4, x3);
            break;
        case ADDR_PIPECFG_P4_16x32:
            x3 = elemIdx0 ^ pipebit0;
            y5 = _BIT(y,5);
            x4 = pipebit1 ^ y5;
            y3 = pipebit0 ^ x3 ^ x4;
            y4 = elemIdx1 ^ x4;
            *pY = Bits2Number(2, y4, y3);
            *pX = Bits2Number(2, x4, x3);
            break;
        case ADDR_PIPECFG_P4_32x32:
            x4 = elemIdx2;
            y3 = elemIdx0 ^ x4;
            y4 = elemIdx1 ^ x4;
            if((pitchInMacroTile % 2) == 0)
            {   //even
                y5 = _BIT(y,5);
                x5 = pipebit1 ^ y5;
                x3 = pipebit0 ^ y3 ^ x5;
                *pY = Bits2Number(2, y4, y3);
                *pX = Bits2Number(3, x5, x4, x3);
            }
            else
            {   //odd
                x5 = _BIT(x,5);
                x3 = pipebit0 ^ y3 ^ x5;
                *pY = Bits2Number(2, y4, y3);
                *pX = Bits2Number(2, x4, x3);
            }
            break;
        case ADDR_PIPECFG_P8_16x16_8x16:
            x4 = elemIdx0;
            y5 = _BIT(y,5);
            x5 = _BIT(x,5);
            x3 = pipebit1 ^ y5;
            y4 = pipebit2 ^ x4;
            y3 = pipebit0 ^ x5 ^ x4;
            *pY = Bits2Number(2, y4, y3);
            *pX = Bits2Number(2, x4, x3);
            break;
        case ADDR_PIPECFG_P8_16x32_8x16:
            x3 = elemIdx0;
            y4 = pipebit1 ^ x3;
            y5 = _BIT(y,5);
            x5 = _BIT(x,5);
            x4 = pipebit2 ^ y5;
            y3 = pipebit0 ^ x4 ^ x5;
            *pY = Bits2Number(2, y4, y3);
            *pX = Bits2Number(2, x4, x3);
            break;
        case ADDR_PIPECFG_P8_32x32_8x16:
            x4 = elemIdx1;
            y4 = elemIdx0 ^ x4;
            x3 = pipebit1 ^ y4;
            if((pitchInMacroTile % 2) == 0)
            {  //even
                y5 = _BIT(y,5);
                x5 = _BIT(x,5);
                x5 = pipebit2 ^ y5;
                y3 = pipebit0 ^ x4 ^ x5;
                *pY = Bits2Number(2, y4, y3);
                *pX = Bits2Number(3, x5, x4, x3);
            }
            else
            {  //odd
                x5 = _BIT(x,5);
                y3 = pipebit0 ^ x4 ^ x5;
                *pY = Bits2Number(2, y4, y3);
                *pX = Bits2Number(2, x4, x3);
            }
            break;
        case ADDR_PIPECFG_P8_16x32_16x16:
            x3 = elemIdx0;
            x5 = _BIT(x,5);
            y5 = _BIT(y,5);
            x4 = pipebit2 ^ y5;
            y4 = pipebit1 ^ x5;
            y3 = pipebit0 ^ x3 ^ x4;
            *pY = Bits2Number(2, y4, y3);
            *pX = Bits2Number(2, x4, x3);
            break;
        case ADDR_PIPECFG_P8_32x32_16x16:
            x4 = elemIdx1;
            y3 = elemIdx0 ^ x4;
            x3 = y3^x4^pipebit0;
            y4 = pipebit1 ^ x4;
            if((pitchInMacroTile % 2) == 0)
            {   //even
                y5 = _BIT(y,5);
                x5 = pipebit2 ^ y5;
                *pY = Bits2Number(2, y4, y3);
                *pX = Bits2Number(3, x5, x4, x3);
            }
            else
            {   //odd
                *pY = Bits2Number(2, y4, y3);
                *pX = Bits2Number(2, x4, x3);
            }
            break;
        case ADDR_PIPECFG_P8_32x32_16x32:
            if((pitchInMacroTile % 2) == 0)
            {   //even
                y5 = _BIT(y,5);
                y6 = _BIT(y,6);
                x4 = pipebit1 ^ y6;
                y3 = elemIdx0 ^ x4;
                y4 = elemIdx1 ^ x4;
                x3 = pipebit0 ^ y3 ^ x4;
                x5 = pipebit2 ^ y5;
                *pY = Bits2Number(2, y4, y3);
                *pX = Bits2Number(3, x5, x4, x3);
            }
            else
            {   //odd
                y6 = _BIT(y,6);
                x4 = pipebit1 ^ y6;
                y3 = elemIdx0 ^ x4;
                y4 = elemIdx1 ^ x4;
                x3 = pipebit0 ^ y3 ^ x4;
                *pY = Bits2Number(2, y4, y3);
                *pX = Bits2Number(2, x4, x3);
            }
            break;
        case ADDR_PIPECFG_P8_32x64_32x32:
            x4 = elemIdx2;
            y3 = elemIdx0 ^ x4;
            y4 = elemIdx1 ^ x4;
            if((pitchInMacroTile % 4) == 0)
            {   //multiple of 4
                y5 = _BIT(y,5);
                y6 = _BIT(y,6);
                x5 = pipebit2 ^ y6;
                x6 = pipebit1 ^ y5;
                x3 = pipebit0 ^ y3 ^ x5;
                *pY = Bits2Number(2, y4, y3);
                *pX = Bits2Number(4, x6, x5, x4, x3);
            }
            else
            {
                y6 = _BIT(y,6);
                x5 = pipebit2 ^ y6;
                x3 = pipebit0 ^ y3 ^ x5;
                *pY = Bits2Number(2, y4, y3);
                *pX = Bits2Number(3, x5, x4, x3);
            }
            break;
        case ADDR_PIPECFG_P16_32x32_8x16:
            x4 = elemIdx1;
            y4 = elemIdx0 ^ x4;
            y3 = pipebit0 ^ x4;
            x3 = pipebit1 ^ y4;
            if((pitchInMacroTile % 4) == 0)
            {   //multiple of 4
                y5 = _BIT(y,5);
                y6 = _BIT(y,6);
                x5 = pipebit2 ^ y6;
                x6 = pipebit3 ^ y5;
                *pY = Bits2Number(2, y4, y3);
                *pX = Bits2Number(4, x6, x5,x4, x3);
            }
            else
            {
                y6 = _BIT(y,6);
                x5 = pipebit2 ^ y6;
                *pY = Bits2Number(2, y4, y3);
                *pX = Bits2Number(3, x5, x4, x3);
            }
            break;
        case ADDR_PIPECFG_P16_32x32_16x16:
            x4 = elemIdx1;
            y3 = elemIdx0 ^ x4;
            y4 = pipebit1 ^ x4;
            x3 = pipebit0 ^ y3 ^ x4;
            if((pitchInMacroTile % 4) == 0)
            {   //multiple of 4
                y5 = _BIT(y,5);
                y6 = _BIT(y,6);
                x5 = pipebit2 ^ y6;
                x6 = pipebit3 ^ y5;
                *pY = Bits2Number(2, y4, y3);
                *pX = Bits2Number(4, x6, x5, x4, x3);
            }
            else
            {
                y6 = _BIT(y,6);
                x5 = pipebit2 ^ y6;
                *pY = Bits2Number(2, y4, y3);
                *pX = Bits2Number(3, x5, x4, x3);
            }
            break;
        default:
            ADDR_UNHANDLED_CASE();
    }
}

/**
***************************************************************************************************
*   SIAddrLib::TileCoordToMaskElementIndex
*
*   @brief
*       Compute element index from coordinates in tiles
*   @return
*       Element index
***************************************************************************************************
*/
UINT_32 SIAddrLib::TileCoordToMaskElementIndex(
    UINT_32         tx,                 ///< [in] x coord, in Tiles
    UINT_32         ty,                 ///< [in] y coord, in Tiles
    AddrPipeCfg     pipeConfig,         ///< [in] pipe config
    UINT_32*        macroShift,         ///< [out] macro shift
    UINT_32*        elemIdxBits         ///< [out] tile offset bits
    ) const
{
    UINT_32 elemIdx = 0;
    UINT_32 elemIdx0, elemIdx1, elemIdx2;
    UINT_32 tx0, tx1;
    UINT_32 ty0, ty1;

    tx0 = _BIT(tx,0);
    tx1 = _BIT(tx,1);
    ty0 = _BIT(ty,0);
    ty1 = _BIT(ty,1);

    switch(pipeConfig)
    {
        case ADDR_PIPECFG_P2:
            *macroShift = 3;
            *elemIdxBits =3;
            elemIdx2 = tx1;
            elemIdx1 = tx1 ^ ty1;
            elemIdx0 = tx1 ^ ty0;
            elemIdx = Bits2Number(3,elemIdx2,elemIdx1,elemIdx0);
            break;
        case ADDR_PIPECFG_P4_8x16:
            *macroShift = 2;
            *elemIdxBits =2;
            elemIdx1 = tx1;
            elemIdx0 = tx1 ^ ty1;
            elemIdx = Bits2Number(2,elemIdx1,elemIdx0);
            break;
        case ADDR_PIPECFG_P4_16x16:
            *macroShift = 2;
            *elemIdxBits =2;
            elemIdx0 = tx1^ty0;
            elemIdx1 = tx1;
            elemIdx = Bits2Number(2, elemIdx1, elemIdx0);
            break;
        case ADDR_PIPECFG_P4_16x32:
            *macroShift = 2;
            *elemIdxBits =2;
            elemIdx0 = tx1^ty0;
            elemIdx1 = tx1^ty1;
            elemIdx = Bits2Number(2, elemIdx1, elemIdx0);
            break;
        case ADDR_PIPECFG_P4_32x32:
            *macroShift = 2;
            *elemIdxBits =3;
            elemIdx0 = tx1^ty0;
            elemIdx1 = tx1^ty1;
            elemIdx2 = tx1;
            elemIdx = Bits2Number(3, elemIdx2, elemIdx1, elemIdx0);
            break;
        case ADDR_PIPECFG_P8_16x16_8x16:
            *macroShift = 1;
            *elemIdxBits =1;
            elemIdx0 = tx1;
            elemIdx = elemIdx0;
            break;
        case ADDR_PIPECFG_P8_16x32_8x16:
            *macroShift = 1;
            *elemIdxBits =1;
            elemIdx0 = tx0;
            elemIdx = elemIdx0;
            break;
        case ADDR_PIPECFG_P8_32x32_8x16:
            *macroShift = 1;
            *elemIdxBits =2;
            elemIdx1 = tx1;
            elemIdx0 = tx1^ty1;
            elemIdx = Bits2Number(2, elemIdx1, elemIdx0);
            break;
        case ADDR_PIPECFG_P8_16x32_16x16:
            *macroShift = 1;
            *elemIdxBits =1;
            elemIdx0 = tx0;
            elemIdx = elemIdx0;
            break;
        case ADDR_PIPECFG_P8_32x32_16x16:
            *macroShift = 1;
            *elemIdxBits =2;
            elemIdx0 = tx1^ty0;
            elemIdx1 = tx1;
            elemIdx = Bits2Number(2, elemIdx1, elemIdx0);
            break;
        case ADDR_PIPECFG_P8_32x32_16x32:
            *macroShift = 1;
            *elemIdxBits =2;
            elemIdx0 =  tx1^ty0;
            elemIdx1 = tx1^ty1;
            elemIdx = Bits2Number(2, elemIdx1, elemIdx0);
            break;
        case ADDR_PIPECFG_P8_32x64_32x32:
            *macroShift = 1;
            *elemIdxBits =3;
            elemIdx0 = tx1^ty0;
            elemIdx1 = tx1^ty1;
            elemIdx2 = tx1;
            elemIdx = Bits2Number(3, elemIdx2, elemIdx1, elemIdx0);
            break;
        case ADDR_PIPECFG_P16_32x32_8x16:
            *macroShift = 0;
            *elemIdxBits =2;
            elemIdx0 = tx1^ty1;
            elemIdx1 = tx1;
            elemIdx = Bits2Number(2, elemIdx1, elemIdx0);
            break;
        case ADDR_PIPECFG_P16_32x32_16x16:
            *macroShift = 0;
            *elemIdxBits =2;
            elemIdx0 = tx1^ty0;
            elemIdx1 = tx1;
            elemIdx = Bits2Number(2, elemIdx1, elemIdx0);
            break;
        default:
            ADDR_UNHANDLED_CASE();
            break;
    }

    return elemIdx;
}

/**
***************************************************************************************************
*   SIAddrLib::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 SIAddrLib::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 macroWidth;
    UINT_32 macroHeight;

    /// In linear mode, the htile or cmask buffer must be padded out to 4 tiles
    /// but for P8_32x64_32x32, it must be padded out to 8 tiles
    /// Actually there are more pipe configs which need 8-tile padding but SI family
    /// has a bug which is fixed in CI family
    if ((pTileInfo->pipeConfig == ADDR_PIPECFG_P8_32x64_32x32) ||
        (pTileInfo->pipeConfig == ADDR_PIPECFG_P16_32x32_8x16) ||
        (pTileInfo->pipeConfig == ADDR_PIPECFG_P8_32x32_16x16))
    {
        macroWidth  = 8*MicroTileWidth;
        macroHeight = 8*MicroTileHeight;
    }
    else
    {
        macroWidth  = 4*MicroTileWidth;
        macroHeight = 4*MicroTileHeight;
    }

    *pMacroWidth    = macroWidth;
    *pMacroHeight   = macroHeight;
}

/**
***************************************************************************************************
*   SIAddrLib::HwlComputeHtileBytes
*
*   @brief
*       Compute htile size in bytes
*
*   @return
*       Htile size in bytes
***************************************************************************************************
*/
UINT_64 SIAddrLib::HwlComputeHtileBytes(
    UINT_32     pitch,          ///< [in] pitch
    UINT_32     height,         ///< [in] height
    UINT_32     bpp,            ///< [in] bits per pixel
    BOOL_32     isLinear,       ///< [in] if it is linear mode
    UINT_32     numSlices,      ///< [in] number of slices
    UINT_64*    pSliceBytes,    ///< [out] bytes per slice
    UINT_32     baseAlign       ///< [in] base alignments
    ) const
{
    return ComputeHtileBytes(pitch, height, bpp, isLinear, numSlices, pSliceBytes, baseAlign);
}

/**
***************************************************************************************************
*   SIAddrLib::HwlComputeXmaskAddrFromCoord
*
*   @brief
*       Compute address from coordinates for htile/cmask
*   @return
*       Byte address
***************************************************************************************************
*/
UINT_64 SIAddrLib::HwlComputeXmaskAddrFromCoord(
    UINT_32        pitch,          ///< [in] pitch
    UINT_32        height,         ///< [in] height
    UINT_32        x,              ///< [in] x coord
    UINT_32        y,              ///< [in] y coord
    UINT_32        slice,          ///< [in] slice/depth index
    UINT_32        numSlices,      ///< [in] number of slices
    UINT_32        factor,         ///< [in] factor that indicates cmask(2) or htile(1)
    BOOL_32        isLinear,       ///< [in] linear or tiled HTILE layout
    BOOL_32        isWidth8,       ///< [in] TRUE if width is 8, FALSE means 4. It's register value
    BOOL_32        isHeight8,      ///< [in] TRUE if width is 8, FALSE means 4. It's register value
    ADDR_TILEINFO* pTileInfo,      ///< [in] Tile info
    UINT_32*       pBitPosition    ///< [out] bit position inside a byte
    ) const
{
    UINT_32 tx = x / MicroTileWidth;
    UINT_32 ty = y / MicroTileHeight;
    UINT_32 newPitch;
    UINT_32 newHeight;
    UINT_64 totalBytes;
    UINT_32 macroWidth;
    UINT_32 macroHeight;
    UINT_64 pSliceBytes;
    UINT_32 pBaseAlign;
    UINT_32 tileNumPerPipe;
    UINT_32 elemBits;

    if (factor == 2) //CMASK
    {
        ADDR_CMASK_FLAGS flags = {{0}};

        tileNumPerPipe = 256;

        ComputeCmaskInfo(flags,
                         pitch,
                         height,
                         numSlices,
                         isLinear,
                         pTileInfo,
                         &newPitch,
                         &newHeight,
                         &totalBytes,
                         &macroWidth,
                         &macroHeight);
        elemBits = CmaskElemBits;
    }
    else //HTile
    {
        ADDR_HTILE_FLAGS flags = {{0}};

        tileNumPerPipe = 512;

        ComputeHtileInfo(flags,
                         pitch,
                         height,
                         numSlices,
                         isLinear,
                         TRUE,
                         TRUE,
                         pTileInfo,
                         &newPitch,
                         &newHeight,
                         &totalBytes,
                         &macroWidth,
                         &macroHeight,
                         &pSliceBytes,
                         &pBaseAlign);
        elemBits = 32;
    }

    const UINT_32 pitchInTile = newPitch / MicroTileWidth;
    const UINT_32 heightInTile = newHeight / MicroTileWidth;
    UINT_64 macroOffset; // Per pipe starting offset of the macro tile in which this tile lies.
    UINT_64 microNumber; // Per pipe starting offset of the macro tile in which this tile lies.
    UINT_32 microX;
    UINT_32 microY;
    UINT_64 microOffset;
    UINT_32 microShift;
    UINT_64 totalOffset;
    UINT_32 elemIdxBits;
    UINT_32 elemIdx =
        TileCoordToMaskElementIndex(tx, ty, pTileInfo->pipeConfig, &microShift, &elemIdxBits);

    UINT_32 numPipes = HwlGetPipes(pTileInfo);

    if (isLinear)
    {   //linear addressing
        // Linear addressing is extremelly wasting memory if slice > 1, since each pipe has the full
        // slice memory foot print instead of divided by numPipes.
        microX = tx / 4; // Macro Tile is 4x4
        microY = ty / 4 ;
        microNumber = static_cast<UINT_64>(microX + microY * (pitchInTile / 4)) << microShift;

        UINT_32 sliceBits = pitchInTile * heightInTile;

        // do htile single slice alignment if the flag is true
        if (m_configFlags.useHtileSliceAlign && (factor == 1))  //Htile
        {
            sliceBits = PowTwoAlign(sliceBits, BITS_TO_BYTES(HtileCacheBits) * numPipes / elemBits);
        }
        macroOffset = slice * (sliceBits / numPipes) * elemBits ;
    }
    else
    {   //tiled addressing
        const UINT_32 macroWidthInTile = macroWidth / MicroTileWidth; // Now in unit of Tiles
        const UINT_32 macroHeightInTile = macroHeight / MicroTileHeight;
        const UINT_32 pitchInCL = pitchInTile / macroWidthInTile;
        const UINT_32 heightInCL = heightInTile / macroHeightInTile;

        const UINT_32 macroX = x / macroWidth;
        const UINT_32 macroY = y / macroHeight;
        const UINT_32 macroNumber = macroX + macroY * pitchInCL + slice * pitchInCL * heightInCL;

        // Per pipe starting offset of the cache line in which this tile lies.
        microX = (x % macroWidth) / MicroTileWidth / 4; // Macro Tile is 4x4
        microY = (y % macroHeight) / MicroTileHeight / 4 ;
        microNumber = static_cast<UINT_64>(microX + microY * (macroWidth / MicroTileWidth / 4)) << microShift;

        macroOffset = macroNumber * tileNumPerPipe * elemBits;
    }

    if(elemIdxBits == microShift)
    {
        microNumber += elemIdx;
    }
    else
    {
        microNumber >>= elemIdxBits;
        microNumber <<= elemIdxBits;
        microNumber += elemIdx;
    }

    microOffset = elemBits * microNumber;
    totalOffset = microOffset + macroOffset;

    UINT_32 pipe = ComputePipeFromCoord(x, y, 0, ADDR_TM_2D_TILED_THIN1, 0, FALSE, pTileInfo);
    UINT_64 addrInBits = totalOffset % (m_pipeInterleaveBytes * 8) +
                   pipe * (m_pipeInterleaveBytes * 8) +
                   totalOffset / (m_pipeInterleaveBytes * 8) * (m_pipeInterleaveBytes * 8) * numPipes;
    *pBitPosition = static_cast<UINT_32>(addrInBits) % 8;
    UINT_64 addr = addrInBits / 8;

    return addr;
}

/**
***************************************************************************************************
*   SIAddrLib::HwlComputeXmaskCoordFromAddr
*
*   @brief
*       Compute the coord from an address of a cmask/htile
*
*   @return
*       N/A
*
*   @note
*       This method is reused by htile, so rename to Xmask
***************************************************************************************************
*/
VOID SIAddrLib::HwlComputeXmaskCoordFromAddr(
    UINT_64         addr,           ///< [in] address
    UINT_32         bitPosition,    ///< [in] bitPosition in a byte
    UINT_32         pitch,          ///< [in] pitch
    UINT_32         height,         ///< [in] height
    UINT_32         numSlices,      ///< [in] number of slices
    UINT_32         factor,         ///< [in] factor that indicates cmask or htile
    BOOL_32         isLinear,       ///< [in] linear or tiled HTILE layout
    BOOL_32         isWidth8,       ///< [in] Not used by SI
    BOOL_32         isHeight8,      ///< [in] Not used by SI
    ADDR_TILEINFO*  pTileInfo,      ///< [in] Tile info
    UINT_32*        pX,             ///< [out] x coord
    UINT_32*        pY,             ///< [out] y coord
    UINT_32*        pSlice          ///< [out] slice index
    ) const
{
    UINT_32 newPitch;
    UINT_32 newHeight;
    UINT_64 totalBytes;
    UINT_32 clWidth;
    UINT_32 clHeight;
    UINT_32 tileNumPerPipe;
    UINT_64 sliceBytes;

    *pX = 0;
    *pY = 0;
    *pSlice = 0;

    if (factor == 2) //CMASK
    {
        ADDR_CMASK_FLAGS flags = {{0}};

        tileNumPerPipe = 256;

        ComputeCmaskInfo(flags,
                         pitch,
                         height,
                         numSlices,
                         isLinear,
                         pTileInfo,
                         &newPitch,
                         &newHeight,
                         &totalBytes,
                         &clWidth,
                         &clHeight);
    }
    else //HTile
    {
        ADDR_HTILE_FLAGS flags = {{0}};

        tileNumPerPipe = 512;

        ComputeHtileInfo(flags,
                         pitch,
                         height,
                         numSlices,
                         isLinear,
                         TRUE,
                         TRUE,
                         pTileInfo,
                         &newPitch,
                         &newHeight,
                         &totalBytes,
                         &clWidth,
                         &clHeight,
                         &sliceBytes);
    }

    const UINT_32 pitchInTile = newPitch / MicroTileWidth;
    const UINT_32 heightInTile = newHeight / MicroTileWidth;
    const UINT_32 pitchInMacroTile = pitchInTile / 4;
    UINT_32 macroShift;
    UINT_32 elemIdxBits;
    // get macroShift and elemIdxBits
    TileCoordToMaskElementIndex(0, 0, pTileInfo->pipeConfig, &macroShift, &elemIdxBits);

    const UINT_32 numPipes = HwlGetPipes(pTileInfo);
    const UINT_32 pipe = (UINT_32)((addr / m_pipeInterleaveBytes) % numPipes);
    // per pipe
    UINT_64 localOffset = (addr % m_pipeInterleaveBytes) +
        (addr / m_pipeInterleaveBytes / numPipes)* m_pipeInterleaveBytes;

    UINT_32 tileIndex;
    if (factor == 2) //CMASK
    {
        tileIndex = (UINT_32)(localOffset * 2 + (bitPosition != 0));
    }
    else
    {
        tileIndex = (UINT_32)(localOffset / 4);
    }

    UINT_32 macroOffset;
    if (isLinear)
    {
        UINT_32 sliceSizeInTile = pitchInTile * heightInTile;

        // do htile single slice alignment if the flag is true
        if (m_configFlags.useHtileSliceAlign && (factor == 1))  //Htile
        {
            sliceSizeInTile = PowTwoAlign(sliceSizeInTile, static_cast<UINT_32>(sliceBytes) / 64);
        }
        *pSlice = tileIndex / (sliceSizeInTile / numPipes);
        macroOffset = tileIndex % (sliceSizeInTile / numPipes);
    }
    else
    {
        const UINT_32 clWidthInTile = clWidth / MicroTileWidth; // Now in unit of Tiles
        const UINT_32 clHeightInTile = clHeight / MicroTileHeight;
        const UINT_32 pitchInCL = pitchInTile / clWidthInTile;
        const UINT_32 heightInCL = heightInTile / clHeightInTile;
        const UINT_32 clIndex = tileIndex / tileNumPerPipe;

        UINT_32 clX = clIndex % pitchInCL;
        UINT_32 clY = (clIndex % (heightInCL * pitchInCL)) / pitchInCL;

        *pX = clX * clWidthInTile * MicroTileWidth;
        *pY = clY * clHeightInTile * MicroTileHeight;
        *pSlice = clIndex / (heightInCL * pitchInCL);

        macroOffset = tileIndex % tileNumPerPipe;
    }

    UINT_32 elemIdx = macroOffset & 7;
    macroOffset >>= elemIdxBits;

    if (elemIdxBits != macroShift)
    {
        macroOffset <<= (elemIdxBits - macroShift);

        UINT_32 pipebit1 = _BIT(pipe,1);
        UINT_32 pipebit2 = _BIT(pipe,2);
        UINT_32 pipebit3 = _BIT(pipe,3);
        if (pitchInMacroTile % 2)
        {   //odd
            switch (pTileInfo->pipeConfig)
            {
                case ADDR_PIPECFG_P4_32x32:
                    macroOffset |= pipebit1;
                    break;
                case ADDR_PIPECFG_P8_32x32_8x16:
                case ADDR_PIPECFG_P8_32x32_16x16:
                case ADDR_PIPECFG_P8_32x32_16x32:
                    macroOffset |= pipebit2;
                    break;
                default:
                    break;
            }

        }

        if (pitchInMacroTile % 4)
        {
            if (pTileInfo->pipeConfig == ADDR_PIPECFG_P8_32x64_32x32)
            {
                macroOffset |= (pipebit1<<1);
            }
            if((pTileInfo->pipeConfig == ADDR_PIPECFG_P16_32x32_8x16) ||
               (pTileInfo->pipeConfig == ADDR_PIPECFG_P16_32x32_16x16))
            {
                macroOffset |= (pipebit3<<1);
            }
        }
    }

    UINT_32 macroX;
    UINT_32 macroY;

    if (isLinear)
    {
        macroX = macroOffset % pitchInMacroTile;
        macroY = macroOffset / pitchInMacroTile;
    }
    else
    {
        const UINT_32 clWidthInMacroTile = clWidth / (MicroTileWidth * 4);
        macroX = macroOffset % clWidthInMacroTile;
        macroY = macroOffset / clWidthInMacroTile;
    }

    *pX += macroX * 4 * MicroTileWidth;
    *pY += macroY * 4 * MicroTileHeight;

    UINT_32 microX;
    UINT_32 microY;
    ComputeTileCoordFromPipeAndElemIdx(elemIdx, pipe, pTileInfo->pipeConfig, pitchInMacroTile,
                                       *pX, *pY, &microX, &microY);

    *pX += microX * MicroTileWidth;
    *pY += microY * MicroTileWidth;
}

/**
***************************************************************************************************
*   SIAddrLib::HwlGetPitchAlignmentLinear
*   @brief
*       Get pitch alignment
*   @return
*       pitch alignment
***************************************************************************************************
*/
UINT_32 SIAddrLib::HwlGetPitchAlignmentLinear(
    UINT_32             bpp,    ///< [in] bits per pixel
    ADDR_SURFACE_FLAGS  flags   ///< [in] surface flags
    ) const
{
    UINT_32 pitchAlign;

    // Interleaved access requires a 256B aligned pitch, so fall back to pre-SI alignment
    if (flags.interleaved)
    {
        pitchAlign = Max(64u, m_pipeInterleaveBytes / BITS_TO_BYTES(bpp));

    }
    else
    {
        pitchAlign = Max(8u, 64 / BITS_TO_BYTES(bpp));
    }

    return pitchAlign;
}

/**
***************************************************************************************************
*   SIAddrLib::HwlGetSizeAdjustmentLinear
*
*   @brief
*       Adjust linear surface pitch and slice size
*
*   @return
*       Logical slice size in bytes
***************************************************************************************************
*/
UINT_64 SIAddrLib::HwlGetSizeAdjustmentLinear(
    AddrTileMode        tileMode,       ///< [in] tile mode
    UINT_32             bpp,            ///< [in] bits per pixel
    UINT_32             numSamples,     ///< [in] number of samples
    UINT_32             baseAlign,      ///< [in] base alignment
    UINT_32             pitchAlign,     ///< [in] pitch alignment
    UINT_32*            pPitch,         ///< [in/out] pointer to pitch
    UINT_32*            pHeight,        ///< [in/out] pointer to height
    UINT_32*            pHeightAlign    ///< [in/out] pointer to height align
    ) const
{
    UINT_64 sliceSize;
    if (tileMode == ADDR_TM_LINEAR_GENERAL)
    {
        sliceSize = BITS_TO_BYTES(static_cast<UINT_64>(*pPitch) * (*pHeight) * bpp * numSamples);
    }
    else
    {
        UINT_32 pitch   = *pPitch;
        UINT_32 height  = *pHeight;

        UINT_32 pixelsPerPipeInterleave = m_pipeInterleaveBytes / BITS_TO_BYTES(bpp);
        UINT_32 sliceAlignInPixel = pixelsPerPipeInterleave < 64 ? 64 : pixelsPerPipeInterleave;

        // numSamples should be 1 in real cases (no MSAA for linear but TGL may pass non 1 value)
        UINT_64 pixelPerSlice = static_cast<UINT_64>(pitch) * height * numSamples;

        while (pixelPerSlice % sliceAlignInPixel)
        {
            pitch += pitchAlign;
            pixelPerSlice = static_cast<UINT_64>(pitch) * height * numSamples;
        }

        *pPitch = pitch;

        UINT_32 heightAlign = 1;

        while ((pitch * heightAlign) % sliceAlignInPixel)
        {
            heightAlign++;
        }

        *pHeightAlign = heightAlign;

        sliceSize = BITS_TO_BYTES(pixelPerSlice * bpp);
    }

    return sliceSize;
}

/**
***************************************************************************************************
*   SIAddrLib::HwlPreHandleBaseLvl3xPitch
*
*   @brief
*       Pre-handler of 3x pitch (96 bit) adjustment
*
*   @return
*       Expected pitch
***************************************************************************************************
*/
UINT_32 SIAddrLib::HwlPreHandleBaseLvl3xPitch(
    const ADDR_COMPUTE_SURFACE_INFO_INPUT*  pIn,        ///< [in] input
    UINT_32                                 expPitch    ///< [in] pitch
    ) const
{
    ADDR_ASSERT(pIn->width == expPitch);

    // From SI, if pow2Pad is 1 the pitch is expanded 3x first, then padded to pow2, so nothing to
    // do here
    if (!pIn->flags.pow2Pad)
    {
        AddrLib::HwlPreHandleBaseLvl3xPitch(pIn, expPitch);
    }
    else
    {
        ADDR_ASSERT(IsPow2(expPitch));
    }

    return expPitch;
}

/**
***************************************************************************************************
*   SIAddrLib::HwlPostHandleBaseLvl3xPitch
*
*   @brief
*       Post-handler of 3x pitch adjustment
*
*   @return
*       Expected pitch
***************************************************************************************************
*/
UINT_32 SIAddrLib::HwlPostHandleBaseLvl3xPitch(
    const ADDR_COMPUTE_SURFACE_INFO_INPUT*  pIn,        ///< [in] input
    UINT_32                                 expPitch    ///< [in] pitch
    ) const
{
    /**
     * @note The pitch will be divided by 3 in the end so the value will look odd but h/w should
     *  be able to compute a correct pitch from it as h/w address library is doing the job.
     */
    // From SI, the pitch is expanded 3x first, then padded to pow2, so no special handler here
    if (!pIn->flags.pow2Pad)
    {
        AddrLib::HwlPostHandleBaseLvl3xPitch(pIn, expPitch);
    }

    return expPitch;
}

/**
***************************************************************************************************
*   SIAddrLib::HwlGetPitchAlignmentMicroTiled
*
*   @brief
*       Compute 1D tiled surface pitch alignment
*
*   @return
*       pitch alignment
***************************************************************************************************
*/
UINT_32 SIAddrLib::HwlGetPitchAlignmentMicroTiled(
    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
    ) const
{
    UINT_32 pitchAlign;

    if (flags.qbStereo)
    {
        pitchAlign = EgBasedAddrLib::HwlGetPitchAlignmentMicroTiled(tileMode,bpp,flags,numSamples);
    }
    else
    {
        pitchAlign = 8;
    }

    return pitchAlign;
}

/**
***************************************************************************************************
*   SIAddrLib::HwlGetSizeAdjustmentMicroTiled
*
*   @brief
*       Adjust 1D tiled surface pitch and slice size
*
*   @return
*       Logical slice size in bytes
***************************************************************************************************
*/
UINT_64 SIAddrLib::HwlGetSizeAdjustmentMicroTiled(
    UINT_32             thickness,      ///< [in] thickness
    UINT_32             bpp,            ///< [in] bits per pixel
    ADDR_SURFACE_FLAGS  flags,          ///< [in] surface flags
    UINT_32             numSamples,     ///< [in] number of samples
    UINT_32             baseAlign,      ///< [in] base alignment
    UINT_32             pitchAlign,     ///< [in] pitch alignment
    UINT_32*            pPitch,         ///< [in/out] pointer to pitch
    UINT_32*            pHeight         ///< [in/out] pointer to height
    ) const
{
    UINT_64 logicalSliceSize;
    UINT_64 physicalSliceSize;

    UINT_32 pitch   = *pPitch;
    UINT_32 height  = *pHeight;

    // Logical slice: pitch * height * bpp * numSamples (no 1D MSAA so actually numSamples == 1)
    logicalSliceSize = BITS_TO_BYTES(static_cast<UINT_64>(pitch) * height * bpp * numSamples);

    // Physical slice: multiplied by thickness
    physicalSliceSize =  logicalSliceSize * thickness;

    // Pitch alignment is always 8, so if slice size is not padded to base alignment
    // (pipe_interleave_size), we need to increase pitch
    while ((physicalSliceSize % baseAlign) != 0)
    {
        pitch += pitchAlign;

        logicalSliceSize = BITS_TO_BYTES(static_cast<UINT_64>(pitch) * height * bpp * numSamples);

        physicalSliceSize =  logicalSliceSize * thickness;
    }

#if !ALT_TEST
    //
    // Special workaround for depth/stencil buffer, use 8 bpp to align depth buffer again since
    // the stencil plane may have larger pitch if the slice size is smaller than base alignment.
    //
    // Note: this actually does not work for mipmap but mipmap depth texture is not really
    // sampled with mipmap.
    //
    if (flags.depth && !flags.noStencil)
    {
        ADDR_ASSERT(numSamples == 1);

        UINT_64 logicalSiceSizeStencil = static_cast<UINT_64>(pitch) * height; // 1 byte stencil

        while ((logicalSiceSizeStencil % baseAlign) != 0)
        {
            pitch += pitchAlign; // Stencil plane's pitch alignment is the same as depth plane's

            logicalSiceSizeStencil = static_cast<UINT_64>(pitch) * height;
        }

        if (pitch != *pPitch)
        {
            // If this is a mipmap, this padded one cannot be sampled as a whole mipmap!
            logicalSliceSize = logicalSiceSizeStencil * BITS_TO_BYTES(bpp);
        }
    }
#endif
    *pPitch = pitch;

    // No adjust for pHeight

    return logicalSliceSize;
}

/**
***************************************************************************************************
*   SIAddrLib::HwlConvertChipFamily
*
*   @brief
*       Convert familyID defined in atiid.h to AddrChipFamily and set m_chipFamily/m_chipRevision
*   @return
*       AddrChipFamily
***************************************************************************************************
*/
AddrChipFamily SIAddrLib::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_SI;

    switch (uChipFamily)
    {
        case FAMILY_SI:
            m_settings.isSouthernIsland = 1;
            m_settings.isTahiti     = ASICREV_IS_TAHITI_P(uChipRevision);
            m_settings.isPitCairn   = ASICREV_IS_PITCAIRN_PM(uChipRevision);
            m_settings.isCapeVerde  = ASICREV_IS_CAPEVERDE_M(uChipRevision);
            m_settings.isOland      = ASICREV_IS_OLAND_M(uChipRevision);
            m_settings.isHainan     = ASICREV_IS_HAINAN_V(uChipRevision);
            break;
        default:
            ADDR_ASSERT(!"This should be a Fusion");
            break;
    }

    return family;
}

/**
***************************************************************************************************
*   SIAddrLib::HwlSetupTileInfo
*
*   @brief
*       Setup default value of tile info for SI
***************************************************************************************************
*/
VOID SIAddrLib::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;

    // Fail-safe code
    if (!IsLinear(tileMode))
    {
        // 128 bpp/thick 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
        if (bpp == 128 || thickness > 1 || flags.fmask || flags.prt)
        {
            inTileType = ADDR_NON_DISPLAYABLE;
        }

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

    // Partial valid fields are not allowed for SI.
    if (IsTileInfoAllZero(pTileInfo))
    {
        if (IsMacroTiled(tileMode))
        {
            if (flags.prt)
            {
                if (numSamples == 1)
                {
                    if (flags.depth)
                    {
                        switch (bpp)
                        {
                            case 16:
                                index = 3;
                                break;
                            case 32:
                                index = 6;
                                break;
                            default:
                                ADDR_ASSERT_ALWAYS();
                                break;
                        }
                    }
                    else
                    {
                        switch (bpp)
                        {
                            case 8:
                                index = 21;
                                break;
                            case 16:
                                index = 22;
                                break;
                            case 32:
                                index = 23;
                                break;
                            case 64:
                                index = 24;
                                break;
                            case 128:
                                index = 25;
                                break;
                            default:
                                break;
                        }

                        if (thickness > 1)
                        {
                            ADDR_ASSERT(bpp != 128);
                            index += 5;
                        }
                    }
                }
                else
                {
                    ADDR_ASSERT(numSamples == 4);

                    if (flags.depth)
                    {
                        switch (bpp)
                        {
                            case 16:
                                index = 5;
                                break;
                            case 32:
                                index = 7;
                                break;
                            default:
                                ADDR_ASSERT_ALWAYS();
                                break;
                        }
                    }
                    else
                    {
                        switch (bpp)
                        {
                            case 8:
                                index = 23;
                                break;
                            case 16:
                                index = 24;
                                break;
                            case 32:
                                index = 25;
                                break;
                            case 64:
                                index = 30;
                                break;
                            default:
                                ADDR_ASSERT_ALWAYS();
                                break;
                        }
                    }
                }
            }//end of PRT part
            // See table entries 0-7
            else if (flags.depth || flags.stencil)
            {
                if (flags.compressZ)
                {
                    if (flags.stencil)
                    {
                        index = 0;
                    }
                    else
                    {
                        // optimal tile index for compressed depth/stencil.
                        switch (numSamples)
                        {
                            case 1:
                                index = 0;
                                break;
                            case 2:
                            case 4:
                                index = 1;
                                break;
                            case 8:
                                index = 2;
                                break;
                            default:
                                break;
                        }
                    }
                }
                else // unCompressZ
                {
                    index = 3;
                }
            }
            else //non PRT & non Depth & non Stencil
            {
                // See table entries 9-12
                if (inTileType == ADDR_DISPLAYABLE)
                {
                    switch (bpp)
                    {
                        case 8:
                            index = 10;
                            break;
                        case 16:
                            index = 11;
                            break;
                        case 32:
                            index = 12;
                            break;
                        case 64:
                            index = 12;
                            break;
                        default:
                            break;
                    }
                }
                else
                {
                    // See table entries 13-17
                    if (thickness == 1)
                    {
                        if (flags.fmask)
                        {
                            UINT_32 fmaskPixelSize = bpp * numSamples;

                            switch (fmaskPixelSize)
                            {
                                case 8:
                                    index = 14;
                                    break;
                                case 16:
                                    index = 15;
                                    break;
                                case 32:
                                    index = 16;
                                    break;
                                case 64:
                                    index = 17;
                                    break;
                                default:
                                    ADDR_ASSERT_ALWAYS();
                            }
                        }
                        else
                        {
                            switch (bpp)
                            {
                                case 8:
                                    index = 14;
                                    break;
                                case 16:
                                    index = 15;
                                    break;
                                case 32:
                                    index = 16;
                                    break;
                                case 64:
                                    index = 17;
                                    break;
                                case 128:
                                    index = 17;
                                    break;
                                default:
                                    break;
                            }
                        }
                    }
                    else // thick tiling - entries 18-20
                    {
                        switch (thickness)
                        {
                            case 4:
                                index = 20;
                                break;
                            case 8:
                                index = 19;
                                break;
                            default:
                                break;
                        }
                    }
                }
            }
        }
        else
        {
            if (tileMode == ADDR_TM_LINEAR_ALIGNED)
            {
                index = 8;
            }
            else if (tileMode == ADDR_TM_LINEAR_GENERAL)
            {
                index = TileIndexLinearGeneral;
            }
            else
            {
                if (flags.depth || flags.stencil)
                {
                    index = 4;
                }
                else if (inTileType == ADDR_DISPLAYABLE)
                {
                    index = 9;
                }
                else if (thickness == 1)
                {
                    index = 13;
                }
                else
                {
                    index = 18;
                }
            }
        }

        if (index >= 0 && index <= 31)
        {
            *pTileInfo      = m_tileTable[index].info;
            pOut->tileType  = m_tileTable[index].type;
        }

        if (index == TileIndexLinearGeneral)
        {
            *pTileInfo      = m_tileTable[8].info;
            pOut->tileType  = m_tileTable[8].type;
        }
    }
    else
    {
        if (pTileInfoIn)
        {
            if (flags.stencil && pTileInfoIn->tileSplitBytes == 0)
            {
                // Stencil always uses index 0
                *pTileInfo = m_tileTable[0].info;
            }
        }
        // Pass through tile type
        pOut->tileType = inTileType;
    }

    pOut->tileIndex = index;
}

/**
***************************************************************************************************
*   SIAddrLib::DecodeGbRegs
*
*   @brief
*       Decodes GB_ADDR_CONFIG and noOfBanks/noOfRanks
*
*   @return
*       TRUE if all settings are valid
*
***************************************************************************************************
*/
BOOL_32 SIAddrLib::DecodeGbRegs(
    const ADDR_REGISTER_VALUE* pRegValue) ///< [in] create input
{
    GB_ADDR_CONFIG  reg;
    BOOL_32         valid = TRUE;

    reg.val = pRegValue->gbAddrConfig;

    switch (reg.f.pipe_interleave_size)
    {
        case ADDR_CONFIG_PIPE_INTERLEAVE_256B:
            m_pipeInterleaveBytes = ADDR_PIPEINTERLEAVE_256B;
            break;
        case ADDR_CONFIG_PIPE_INTERLEAVE_512B:
            m_pipeInterleaveBytes = ADDR_PIPEINTERLEAVE_512B;
            break;
        default:
            valid = FALSE;
            ADDR_UNHANDLED_CASE();
            break;
    }

    switch (reg.f.row_size)
    {
        case ADDR_CONFIG_1KB_ROW:
            m_rowSize = ADDR_ROWSIZE_1KB;
            break;
        case ADDR_CONFIG_2KB_ROW:
            m_rowSize = ADDR_ROWSIZE_2KB;
            break;
        case ADDR_CONFIG_4KB_ROW:
            m_rowSize = ADDR_ROWSIZE_4KB;
            break;
        default:
            valid = FALSE;
            ADDR_UNHANDLED_CASE();
            break;
    }

    switch (pRegValue->noOfBanks)
    {
        case 0:
            m_banks = 4;
            break;
        case 1:
            m_banks = 8;
            break;
        case 2:
            m_banks = 16;
            break;
        default:
            valid = FALSE;
            ADDR_UNHANDLED_CASE();
            break;
    }

    switch (pRegValue->noOfRanks)
    {
        case 0:
            m_ranks = 1;
            break;
        case 1:
            m_ranks = 2;
            break;
        default:
            valid = FALSE;
            ADDR_UNHANDLED_CASE();
            break;
    }

    m_logicalBanks = m_banks * m_ranks;

    ADDR_ASSERT(m_logicalBanks <= 16);

    return valid;
}

/**
***************************************************************************************************
*   SIAddrLib::HwlInitGlobalParams
*
*   @brief
*       Initializes global parameters
*
*   @return
*       TRUE if all settings are valid
*
***************************************************************************************************
*/
BOOL_32 SIAddrLib::HwlInitGlobalParams(
    const ADDR_CREATE_INPUT* pCreateIn) ///< [in] create input
{
    BOOL_32 valid = TRUE;
    const ADDR_REGISTER_VALUE* pRegValue = &pCreateIn->regValue;

    valid = DecodeGbRegs(pRegValue);

    if (valid)
    {
        if (m_settings.isTahiti || m_settings.isPitCairn)
        {
            m_pipes = 8;
        }
        else if (m_settings.isCapeVerde || m_settings.isOland)
        {
            m_pipes = 4;
        }
        else
        {
            // Hainan is 2-pipe (m_settings.isHainan == 1)
            m_pipes = 2;
        }

        valid = InitTileSettingTable(pRegValue->pTileConfig, pRegValue->noOfEntries);

        m_maxSamples = 16;
    }

    return valid;
}

/**
***************************************************************************************************
*   SIAddrLib::HwlConvertTileInfoToHW
*   @brief
*       Entry of si's ConvertTileInfoToHW
*   @return
*       ADDR_E_RETURNCODE
***************************************************************************************************
*/
ADDR_E_RETURNCODE SIAddrLib::HwlConvertTileInfoToHW(
    const ADDR_CONVERT_TILEINFOTOHW_INPUT* pIn, ///< [in] input structure
    ADDR_CONVERT_TILEINFOTOHW_OUTPUT* pOut      ///< [out] output structure
    ) const
{
    ADDR_E_RETURNCODE retCode   = ADDR_OK;

    retCode = EgBasedAddrLib::HwlConvertTileInfoToHW(pIn, pOut);

    if (retCode == ADDR_OK)
    {
        if (pIn->reverse == FALSE)
        {
            if (pIn->pTileInfo->pipeConfig == ADDR_PIPECFG_INVALID)
            {
                retCode = ADDR_INVALIDPARAMS;
            }
            else
            {
                pOut->pTileInfo->pipeConfig =
                    static_cast<AddrPipeCfg>(pIn->pTileInfo->pipeConfig - 1);
            }
        }
        else
        {
            pOut->pTileInfo->pipeConfig =
                static_cast<AddrPipeCfg>(pIn->pTileInfo->pipeConfig + 1);
        }
    }

    return retCode;
}

/**
***************************************************************************************************
*   SIAddrLib::HwlComputeXmaskCoordYFrom8Pipe
*
*   @brief
*       Compute the Y coord which will be added to Xmask Y
*       coord.
*   @return
*       Y coord
***************************************************************************************************
*/
UINT_32 SIAddrLib::HwlComputeXmaskCoordYFrom8Pipe(
    UINT_32         pipe,       ///< [in] pipe id
    UINT_32         x           ///< [in] tile coord x, which is original x coord / 8
    ) const
{
    // This function should never be called since it is 6xx/8xx specfic.
    // Keep this empty implementation to avoid any mis-use.
    ADDR_ASSERT_ALWAYS();

    return 0;
}

/**
***************************************************************************************************
*   SIAddrLib::HwlComputeSurfaceCoord2DFromBankPipe
*
*   @brief
*       Compute surface x,y coordinates from bank/pipe info
*   @return
*       N/A
***************************************************************************************************
*/
VOID SIAddrLib::HwlComputeSurfaceCoord2DFromBankPipe(
    AddrTileMode        tileMode,   ///< [in] tile mode
    UINT_32*            pX,         ///< [in/out] x coordinate
    UINT_32*            pY,         ///< [in/out] y coordinate
    UINT_32             slice,      ///< [in] slice index
    UINT_32             bank,       ///< [in] bank number
    UINT_32             pipe,       ///< [in] pipe number
    UINT_32             bankSwizzle,///< [in] bank swizzle
    UINT_32             pipeSwizzle,///< [in] pipe swizzle
    UINT_32             tileSlices, ///< [in] slices in a micro tile
    BOOL_32             ignoreSE,   ///< [in] TRUE if shader engines are ignored
    ADDR_TILEINFO*      pTileInfo   ///< [in] bank structure. **All fields to be valid on entry**
    ) const
{
    UINT_32 xBit;
    UINT_32 yBit;
    UINT_32 yBit3 = 0;
    UINT_32 yBit4 = 0;
    UINT_32 yBit5 = 0;
    UINT_32 yBit6 = 0;

    UINT_32 xBit3 = 0;
    UINT_32 xBit4 = 0;
    UINT_32 xBit5 = 0;

    UINT_32 numPipes = GetPipePerSurf(pTileInfo->pipeConfig);

    CoordFromBankPipe xyBits = {0};
    ComputeSurfaceCoord2DFromBankPipe(tileMode, *pX, *pY, slice, bank, pipe,
                                      bankSwizzle, pipeSwizzle, tileSlices, pTileInfo,
                                      &xyBits);
    yBit3 = xyBits.yBit3;
    yBit4 = xyBits.yBit4;
    yBit5 = xyBits.yBit5;
    yBit6 = xyBits.yBit6;

    xBit3 = xyBits.xBit3;
    xBit4 = xyBits.xBit4;
    xBit5 = xyBits.xBit5;

    yBit = xyBits.yBits;

    UINT_32 yBitTemp = 0;

    if ((pTileInfo->pipeConfig == ADDR_PIPECFG_P4_32x32) ||
        (pTileInfo->pipeConfig == ADDR_PIPECFG_P8_32x64_32x32))
    {
        ADDR_ASSERT(pTileInfo->bankWidth == 1 && pTileInfo->macroAspectRatio > 1);
        UINT_32 yBitToCheck = QLog2(pTileInfo->banks) - 1;

        ADDR_ASSERT(yBitToCheck <= 3);

        yBitTemp = _BIT(yBit, yBitToCheck);

        xBit3 = 0;
    }

    yBit = Bits2Number(4, yBit6, yBit5, yBit4, yBit3);
    xBit = Bits2Number(3, xBit5, xBit4, xBit3);

    *pY += yBit * pTileInfo->bankHeight * MicroTileHeight;
    *pX += xBit * numPipes * pTileInfo->bankWidth * MicroTileWidth;

    //calculate the bank and pipe bits in x, y
    UINT_32 xTile; //x in micro tile
    UINT_32 x3 = 0;
    UINT_32 x4 = 0;
    UINT_32 x5 = 0;
    UINT_32 x6 = 0;
    UINT_32 y = *pY;

    UINT_32 pipeBit0 = _BIT(pipe,0);
    UINT_32 pipeBit1 = _BIT(pipe,1);
    UINT_32 pipeBit2 = _BIT(pipe,2);

    UINT_32 y3 = _BIT(y, 3);
    UINT_32 y4 = _BIT(y, 4);
    UINT_32 y5 = _BIT(y, 5);
    UINT_32 y6 = _BIT(y, 6);

    // bankbit0 after ^x4^x5
    UINT_32 bankBit00 = _BIT(bank,0);
    UINT_32 bankBit0 = 0;

    switch (pTileInfo->pipeConfig)
    {
        case ADDR_PIPECFG_P2:
            x3 = pipeBit0 ^ y3;
            break;
        case ADDR_PIPECFG_P4_8x16:
            x4 = pipeBit0 ^ y3;
            x3 = pipeBit0 ^ y4;
            break;
        case ADDR_PIPECFG_P4_16x16:
            x4 = pipeBit1 ^ y4;
            x3 = pipeBit0 ^ y3 ^ x4;
            break;
        case ADDR_PIPECFG_P4_16x32:
            x4 = pipeBit1 ^ y4;
            x3 = pipeBit0 ^ y3 ^ x4;
            break;
        case ADDR_PIPECFG_P4_32x32:
            x5 = pipeBit1 ^ y5;
            x3 = pipeBit0 ^ y3 ^ x5;
            bankBit0 = yBitTemp ^ x5;
            x4 = bankBit00 ^ x5 ^ bankBit0;
            *pX += x5 * 4 * 1 * 8; // x5 * num_pipes * bank_width * 8;
            break;
        case ADDR_PIPECFG_P8_16x16_8x16:
            x3 = pipeBit1 ^ y5;
            x4 = pipeBit2 ^ y4;
            x5 = pipeBit0 ^ y3 ^ x4;
            break;
        case ADDR_PIPECFG_P8_16x32_8x16:
            x3 = pipeBit1 ^ y4;
            x4 = pipeBit2 ^ y5;
            x5 = pipeBit0 ^ y3 ^ x4;
            break;
        case ADDR_PIPECFG_P8_32x32_8x16:
            x3 = pipeBit1 ^ y4;
            x5 = pipeBit2 ^ y5;
            x4 = pipeBit0 ^ y3 ^ x5;
            break;
        case ADDR_PIPECFG_P8_16x32_16x16:
            x4 = pipeBit2 ^ y5;
            x5 = pipeBit1 ^ y4;
            x3 = pipeBit0 ^ y3 ^ x4;
            break;
        case ADDR_PIPECFG_P8_32x32_16x16:
            x5 = pipeBit2 ^ y5;
            x4 = pipeBit1 ^ y4;
            x3 = pipeBit0 ^ y3 ^ x4;
            break;
        case ADDR_PIPECFG_P8_32x32_16x32:
            x5 = pipeBit2 ^ y5;
            x4 = pipeBit1 ^ y6;
            x3 = pipeBit0 ^ y3 ^ x4;
            break;
        case ADDR_PIPECFG_P8_32x64_32x32:
            x6 = pipeBit1 ^ y5;
            x5 = pipeBit2 ^ y6;
            x3 = pipeBit0 ^ y3 ^ x5;
            bankBit0 = yBitTemp ^ x6;
            x4 = bankBit00 ^ x5 ^ bankBit0;
            *pX += x6 * 8 * 1 * 8; // x6 * num_pipes * bank_width * 8;
            break;
        default:
            ADDR_ASSERT_ALWAYS();
    }

    xTile = Bits2Number(3, x5, x4, x3);

    *pX += xTile << 3;
}

/**
***************************************************************************************************
*   SIAddrLib::HwlPreAdjustBank
*
*   @brief
*       Adjust bank before calculating address acoording to bank/pipe
*   @return
*       Adjusted bank
***************************************************************************************************
*/
UINT_32 SIAddrLib::HwlPreAdjustBank(
    UINT_32         tileX,      ///< [in] x coordinate in unit of tile
    UINT_32         bank,       ///< [in] bank
    ADDR_TILEINFO*  pTileInfo   ///< [in] tile info
    ) const
{
    if (((pTileInfo->pipeConfig == ADDR_PIPECFG_P4_32x32) ||
        (pTileInfo->pipeConfig == ADDR_PIPECFG_P8_32x64_32x32)) && (pTileInfo->bankWidth == 1))
    {
        UINT_32 bankBit0 = _BIT(bank, 0);
        UINT_32 x4 = _BIT(tileX, 1);
        UINT_32 x5 = _BIT(tileX, 2);

        bankBit0 = bankBit0 ^ x4 ^ x5;
        bank |= bankBit0;

        ADDR_ASSERT(pTileInfo->macroAspectRatio > 1)
    }

    return bank;
}

/**
***************************************************************************************************
*   SIAddrLib::HwlComputeSurfaceInfo
*
*   @brief
*       Entry of si's ComputeSurfaceInfo
*   @return
*       ADDR_E_RETURNCODE
***************************************************************************************************
*/
ADDR_E_RETURNCODE SIAddrLib::HwlComputeSurfaceInfo(
    const ADDR_COMPUTE_SURFACE_INFO_INPUT*  pIn,    ///< [in] input structure
    ADDR_COMPUTE_SURFACE_INFO_OUTPUT*       pOut    ///< [out] output structure
    ) const
{
    pOut->tileIndex = pIn->tileIndex;

    return EgBasedAddrLib::HwlComputeSurfaceInfo(pIn,pOut);
}

/**
***************************************************************************************************
*   SIAddrLib::HwlComputeMipLevel
*   @brief
*       Compute MipLevel info (including level 0)
*   @return
*       TRUE if HWL's handled
***************************************************************************************************
*/
BOOL_32 SIAddrLib::HwlComputeMipLevel(
    ADDR_COMPUTE_SURFACE_INFO_INPUT* pIn ///< [in/out] Input structure
    ) const
{
    // basePitch is calculated from level 0 so we only check this for mipLevel > 0
    if (pIn->mipLevel > 0)
    {
        // Note: Don't check expand 3x formats(96 bit) as the basePitch is not pow2 even if
        // we explicity set pow2Pad flag. The 3x base pitch is padded to pow2 but after being
        // divided by expandX factor (3) - to program texture pitch, the basePitch is never pow2.
        if (!AddrElemLib::IsExpand3x(pIn->format))
        {
            // Sublevel pitches are generated from base level pitch instead of width on SI
            // If pow2Pad is 0, we don't assert - as this is not really used for a mip chain
            ADDR_ASSERT(!pIn->flags.pow2Pad || ((pIn->basePitch != 0) && IsPow2(pIn->basePitch)));
        }

        if (pIn->basePitch != 0)
        {
            pIn->width = Max(1u, pIn->basePitch >> pIn->mipLevel);
        }
    }

    // pow2Pad is done in PostComputeMipLevel

    return TRUE;
}

/**
***************************************************************************************************
*   SIAddrLib::HwlCheckLastMacroTiledLvl
*
*   @brief
*       Sets pOut->last2DLevel to TRUE if it is
*   @note
*
***************************************************************************************************
*/
VOID SIAddrLib::HwlCheckLastMacroTiledLvl(
    const ADDR_COMPUTE_SURFACE_INFO_INPUT* pIn, ///< [in] Input structure
    ADDR_COMPUTE_SURFACE_INFO_OUTPUT* pOut      ///< [in/out] Output structure (used as input, too)
    ) const
{
    // pow2Pad covers all mipmap cases
    if (pIn->flags.pow2Pad)
    {
        ADDR_ASSERT(IsMacroTiled(pIn->tileMode));

        UINT_32 nextPitch;
        UINT_32 nextHeight;
        UINT_32 nextSlices;

        AddrTileMode nextTileMode;

        if (pIn->mipLevel == 0 || pIn->basePitch == 0)
        {
            // Base level or fail-safe case (basePitch == 0)
            nextPitch = pOut->pitch >> 1;
        }
        else
        {
            // Sub levels
            nextPitch = pIn->basePitch >> (pIn->mipLevel + 1);
        }

        // nextHeight must be shifted from this level's original height rather than a pow2 padded
        // one but this requires original height stored somewhere (pOut->height)
        ADDR_ASSERT(pOut->height != 0);

        // next level's height is just current level's >> 1 in pixels
        nextHeight = pOut->height >> 1;
        // Special format such as FMT_1 and FMT_32_32_32 can be linear only so we consider block
        // compressed foramts
        if (AddrElemLib::IsBlockCompressed(pIn->format))
        {
            nextHeight = (nextHeight + 3) / 4;
        }
        nextHeight = NextPow2(nextHeight);

        // nextSlices may be 0 if this level's is 1
        if (pIn->flags.volume)
        {
            nextSlices = Max(1u, pIn->numSlices >> 1);
        }
        else
        {
            nextSlices = pIn->numSlices;
        }

        nextTileMode = ComputeSurfaceMipLevelTileMode(pIn->tileMode,
                                                      pIn->bpp,
                                                      nextPitch,
                                                      nextHeight,
                                                      nextSlices,
                                                      pIn->numSamples,
                                                      pOut->pitchAlign,
                                                      pOut->heightAlign,
                                                      pOut->pTileInfo);

        pOut->last2DLevel = IsMicroTiled(nextTileMode);
    }
}

/**
***************************************************************************************************
*   SIAddrLib::HwlDegradeThickTileMode
*
*   @brief
*       Degrades valid tile mode for thick modes if needed
*
*   @return
*       Suitable tile mode
***************************************************************************************************
*/
AddrTileMode SIAddrLib::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 EgBasedAddrLib::HwlDegradeThickTileMode(baseTileMode, numSlices, pBytesPerTile);
}

/**
***************************************************************************************************
*   SIAddrLib::HwlTileInfoEqual
*
*   @brief
*       Return TRUE if all field are equal
*   @note
*       Only takes care of current HWL's data
***************************************************************************************************
*/
BOOL_32 SIAddrLib::HwlTileInfoEqual(
    const ADDR_TILEINFO* pLeft, ///<[in] Left compare operand
    const ADDR_TILEINFO* pRight ///<[in] Right compare operand
    ) const
{
    BOOL_32 equal = FALSE;

    if (pLeft->pipeConfig == pRight->pipeConfig)
    {
        equal =  EgBasedAddrLib::HwlTileInfoEqual(pLeft, pRight);
    }

    return equal;
}

/**
***************************************************************************************************
*   SIAddrLib::GetTileSettings
*
*   @brief
*       Get tile setting infos by index.
*   @return
*       Tile setting info.
***************************************************************************************************
*/
const ADDR_TILECONFIG* SIAddrLib::GetTileSetting(
    UINT_32 index          ///< [in] Tile index
    ) const
{
    ADDR_ASSERT(index < m_noOfEntries);
    return &m_tileTable[index];
}

/**
***************************************************************************************************
*   SIAddrLib::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 SIAddrLib::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 && !HwlTileInfoEqual(pInfo, &m_tileTable[index].info))))
        {
            for (index = 0; index < static_cast<INT_32>(m_noOfEntries); index++)
            {
                if (macroTiled)
                {
                    // macro tile modes need all to match
                    if (HwlTileInfoEqual(pInfo, &m_tileTable[index].info) &&
                        (mode == m_tileTable[index].mode)                 &&
                        (type == m_tileTable[index].type))
                    {
                        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;
}

/**
***************************************************************************************************
*   SIAddrLib::HwlSetupTileCfg
*
*   @brief
*       Map tile index to tile setting.
*   @return
*       ADDR_E_RETURNCODE
***************************************************************************************************
*/
ADDR_E_RETURNCODE SIAddrLib::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 (index == TileIndexLinearGeneral)
        {
            if (pMode)
            {
                *pMode = ADDR_TM_LINEAR_GENERAL;
            }

            if (pType)
            {
                *pType = ADDR_DISPLAYABLE;
            }

            if (pInfo)
            {
                pInfo->banks = 2;
                pInfo->bankWidth = 1;
                pInfo->bankHeight = 1;
                pInfo->macroAspectRatio = 1;
                pInfo->tileSplitBytes = 64;
                pInfo->pipeConfig = ADDR_PIPECFG_P2;
            }
        }
        else if (static_cast<UINT_32>(index) >= m_noOfEntries)
        {
            returnCode = ADDR_INVALIDPARAMS;
        }
        else
        {
            const ADDR_TILECONFIG* pCfgTable = GetTileSetting(index);

            if (pInfo)
            {
                *pInfo = pCfgTable->info;
            }
            else
            {
                if (IsMacroTiled(pCfgTable->mode))
                {
                    returnCode = ADDR_INVALIDPARAMS;
                }
            }

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

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

    return returnCode;
}

/**
***************************************************************************************************
*   SIAddrLib::ReadGbTileMode
*
*   @brief
*       Convert GB_TILE_MODE HW value to ADDR_TILE_CONFIG.
*   @return
*       NA.
***************************************************************************************************
*/
VOID SIAddrLib::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);
    pCfg->info.bankHeight = 1 << gbTileMode.f.bank_height;
    pCfg->info.bankWidth = 1 << gbTileMode.f.bank_width;
    pCfg->info.banks = 1 << (gbTileMode.f.num_banks + 1);
    pCfg->info.macroAspectRatio = 1 << gbTileMode.f.macro_tile_aspect;
    pCfg->info.tileSplitBytes = 64 << gbTileMode.f.tile_split;
    pCfg->info.pipeConfig = static_cast<AddrPipeCfg>(gbTileMode.f.pipe_config + 1);

    UINT_32 regArrayMode = gbTileMode.f.array_mode;

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

    if (regArrayMode == 8) //ARRAY_2D_TILED_XTHICK
    {
        pCfg->mode = ADDR_TM_2D_TILED_XTHICK;
    }
    else if (regArrayMode >= 14) //ARRAY_3D_TILED_XTHICK
    {
        pCfg->mode = static_cast<AddrTileMode>(pCfg->mode + 3);
    }
}

/**
***************************************************************************************************
*   SIAddrLib::InitTileSettingTable
*
*   @brief
*       Initialize the ADDR_TILE_CONFIG table.
*   @return
*       TRUE if tile table is correctly initialized
***************************************************************************************************
*/
BOOL_32 SIAddrLib::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);
    }

    return initOk;
}

/**
***************************************************************************************************
*   SIAddrLib::HwlGetTileIndex
*
*   @brief
*       Return the virtual/real index for given mode/type/info
*   @return
*       ADDR_OK if successful.
***************************************************************************************************
*/
ADDR_E_RETURNCODE SIAddrLib::HwlGetTileIndex(
    const ADDR_GET_TILEINDEX_INPUT* pIn,
    ADDR_GET_TILEINDEX_OUTPUT*      pOut) const
{
    ADDR_E_RETURNCODE returnCode = ADDR_OK;

    pOut->index = HwlPostCheckTileIndex(pIn->pTileInfo, pIn->tileMode, pIn->tileType);

    return returnCode;
}

/**
***************************************************************************************************
*   SIAddrLib::HwlFmaskPreThunkSurfInfo
*
*   @brief
*       Some preparation before thunking a ComputeSurfaceInfo call for Fmask
*   @return
*       ADDR_E_RETURNCODE
***************************************************************************************************
*/
VOID SIAddrLib::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;
}

/**
***************************************************************************************************
*   SIAddrLib::HwlFmaskPostThunkSurfInfo
*
*   @brief
*       Copy hwl extra field after calling thunked ComputeSurfaceInfo
*   @return
*       ADDR_E_RETURNCODE
***************************************************************************************************
*/
VOID SIAddrLib::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->macroModeIndex = TileIndexInvalid;
    pFmaskOut->tileIndex = pSurfOut->tileIndex;
}

/**
***************************************************************************************************
*   SIAddrLib::HwlComputeFmaskBits
*   @brief
*       Computes fmask bits
*   @return
*       Fmask bits
***************************************************************************************************
*/
UINT_32 SIAddrLib::HwlComputeFmaskBits(
    const ADDR_COMPUTE_FMASK_INFO_INPUT* pIn,
    UINT_32* pNumSamples
    ) const
{
    UINT_32 numSamples = pIn->numSamples;
    UINT_32 numFrags = GetNumFragments(numSamples, pIn->numFrags);
    UINT_32 bpp;

    if (numFrags != numSamples) // EQAA
    {
        ADDR_ASSERT(numFrags <= 8);

        if (!pIn->resolved)
        {
            if (numFrags == 1)
            {
                bpp          = 1;
                numSamples   = numSamples == 16 ? 16 : 8;
            }
            else if (numFrags == 2)
            {
                ADDR_ASSERT(numSamples >= 4);

                bpp          = 2;
                numSamples   = numSamples;
            }
            else if (numFrags == 4)
            {
                ADDR_ASSERT(numSamples >= 4);

                bpp          = 4;
                numSamples   = numSamples;
            }
            else // numFrags == 8
            {
                ADDR_ASSERT(numSamples == 16);

                bpp          = 4;
                numSamples   = numSamples;
            }
        }
        else
        {
            if (numFrags == 1)
            {
                bpp          = (numSamples == 16) ? 16 : 8;
                numSamples   = 1;
            }
            else if (numFrags == 2)
            {
                ADDR_ASSERT(numSamples >= 4);

                bpp          = numSamples*2;
                numSamples   = 1;
            }
            else if (numFrags == 4)
            {
                ADDR_ASSERT(numSamples >= 4);

                bpp          = numSamples*4;
                numSamples   = 1;
            }
            else // numFrags == 8
            {
                ADDR_ASSERT(numSamples >= 16);

                bpp          = 16*4;
                numSamples   = 1;
            }
        }
    }
    else // Normal AA
    {
        if (!pIn->resolved)
        {
            bpp          = ComputeFmaskNumPlanesFromNumSamples(numSamples);
            numSamples   = numSamples == 2 ? 8 : numSamples;
        }
        else
        {
            // The same as 8XX
            bpp          = ComputeFmaskResolvedBppFromNumSamples(numSamples);
            numSamples   = 1; // 1x sample
        }
    }

    SafeAssign(pNumSamples, numSamples);

    return bpp;
}

/**
***************************************************************************************************
*   SIAddrLib::HwlOverrideTileMode
*
*   @brief
*       Override tile modes (for PRT only, avoid client passes in an invalid PRT mode for SI.
*
*   @return
*       Suitable tile mode
*
***************************************************************************************************
*/
BOOL_32 SIAddrLib::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;

    switch (tileMode)
    {
        case ADDR_TM_PRT_TILED_THIN1:
            tileMode    = ADDR_TM_2D_TILED_THIN1;
            break;

        case ADDR_TM_PRT_TILED_THICK:
            tileMode    = ADDR_TM_2D_TILED_THICK;
            break;

        case ADDR_TM_PRT_2D_TILED_THICK:
            tileMode    = ADDR_TM_2D_TILED_THICK;
            break;

        case ADDR_TM_PRT_3D_TILED_THICK:
            tileMode    = ADDR_TM_3D_TILED_THICK;
            break;

        default:
            break;
    }

    if (tileMode != *pTileMode)
    {
        *pTileMode = tileMode;
        bOverrided = TRUE;
        ADDR_ASSERT(pIn->flags.prt == TRUE);
    }

    return bOverrided;
}

