/*
 * Copyright © 2016 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  addr1lib.cpp
* @brief Contains the implementation for the Addr::V1::Lib base class.
****************************************************************************************************
*/

#include "addrinterface.h"
#include "addrlib1.h"
#include "addrcommon.h"

namespace Addr
{
namespace V1
{

////////////////////////////////////////////////////////////////////////////////////////////////////
//                               Static Const Member
////////////////////////////////////////////////////////////////////////////////////////////////////

const TileModeFlags Lib::ModeFlags[ADDR_TM_COUNT] =
{// T   L  1  2  3  P  Pr B
    {1, 1, 0, 0, 0, 0, 0, 0}, // ADDR_TM_LINEAR_GENERAL
    {1, 1, 0, 0, 0, 0, 0, 0}, // ADDR_TM_LINEAR_ALIGNED
    {1, 0, 1, 0, 0, 0, 0, 0}, // ADDR_TM_1D_TILED_THIN1
    {4, 0, 1, 0, 0, 0, 0, 0}, // ADDR_TM_1D_TILED_THICK
    {1, 0, 0, 1, 0, 0, 0, 0}, // ADDR_TM_2D_TILED_THIN1
    {1, 0, 0, 1, 0, 0, 0, 0}, // ADDR_TM_2D_TILED_THIN2
    {1, 0, 0, 1, 0, 0, 0, 0}, // ADDR_TM_2D_TILED_THIN4
    {4, 0, 0, 1, 0, 0, 0, 0}, // ADDR_TM_2D_TILED_THICK
    {1, 0, 0, 1, 0, 0, 0, 1}, // ADDR_TM_2B_TILED_THIN1
    {1, 0, 0, 1, 0, 0, 0, 1}, // ADDR_TM_2B_TILED_THIN2
    {1, 0, 0, 1, 0, 0, 0, 1}, // ADDR_TM_2B_TILED_THIN4
    {4, 0, 0, 1, 0, 0, 0, 1}, // ADDR_TM_2B_TILED_THICK
    {1, 0, 0, 1, 1, 0, 0, 0}, // ADDR_TM_3D_TILED_THIN1
    {4, 0, 0, 1, 1, 0, 0, 0}, // ADDR_TM_3D_TILED_THICK
    {1, 0, 0, 1, 1, 0, 0, 1}, // ADDR_TM_3B_TILED_THIN1
    {4, 0, 0, 1, 1, 0, 0, 1}, // ADDR_TM_3B_TILED_THICK
    {8, 0, 0, 1, 0, 0, 0, 0}, // ADDR_TM_2D_TILED_XTHICK
    {8, 0, 0, 1, 1, 0, 0, 0}, // ADDR_TM_3D_TILED_XTHICK
    {1, 0, 0, 0, 0, 0, 0, 0}, // ADDR_TM_POWER_SAVE
    {1, 0, 0, 1, 0, 1, 1, 0}, // ADDR_TM_PRT_TILED_THIN1
    {1, 0, 0, 1, 0, 1, 0, 0}, // ADDR_TM_PRT_2D_TILED_THIN1
    {1, 0, 0, 1, 1, 1, 0, 0}, // ADDR_TM_PRT_3D_TILED_THIN1
    {4, 0, 0, 1, 0, 1, 1, 0}, // ADDR_TM_PRT_TILED_THICK
    {4, 0, 0, 1, 0, 1, 0, 0}, // ADDR_TM_PRT_2D_TILED_THICK
    {4, 0, 0, 1, 1, 1, 0, 0}, // ADDR_TM_PRT_3D_TILED_THICK
    {0, 0, 0, 0, 0, 0, 0, 0}, // ADDR_TM_UNKNOWN
};

////////////////////////////////////////////////////////////////////////////////////////////////////
//                               Constructor/Destructor
////////////////////////////////////////////////////////////////////////////////////////////////////

/**
****************************************************************************************************
*   Lib::AddrLib1
*
*   @brief
*       Constructor for the AddrLib1 class
*
****************************************************************************************************
*/
Lib::Lib()
    :
    Addr::Lib()
{
}

/**
****************************************************************************************************
*   Lib::Lib
*
*   @brief
*       Constructor for the Addr::V1::Lib class with hClient as parameter
*
****************************************************************************************************
*/
Lib::Lib(const Client* pClient)
    :
    Addr::Lib(pClient)
{
}

/**
****************************************************************************************************
*   Lib::~AddrLib1
*
*   @brief
*       Destructor for the AddrLib1 class
*
****************************************************************************************************
*/
Lib::~Lib()
{
}

/**
****************************************************************************************************
*   Lib::GetLib
*
*   @brief
*       Get AddrLib1 pointer
*
*   @return
*      An Addr::V1::Lib class pointer
****************************************************************************************************
*/
Lib* Lib::GetLib(
    ADDR_HANDLE hLib)   ///< [in] handle of ADDR_HANDLE
{
    Addr::Lib* pAddrLib = Addr::Lib::GetLib(hLib);
    if ((pAddrLib != NULL) &&
        ((pAddrLib->GetChipFamily() == ADDR_CHIP_FAMILY_IVLD) ||
         (pAddrLib->GetChipFamily() > ADDR_CHIP_FAMILY_VI)))
    {
        // only valid and pre-VI AISC can use AddrLib1 function.
        ADDR_ASSERT_ALWAYS();
        hLib = NULL;
    }
    return static_cast<Lib*>(hLib);
}


////////////////////////////////////////////////////////////////////////////////////////////////////
//                               Surface Methods
////////////////////////////////////////////////////////////////////////////////////////////////////


/**
****************************************************************************************************
*   Lib::ComputeSurfaceInfo
*
*   @brief
*       Interface function stub of AddrComputeSurfaceInfo.
*
*   @return
*       ADDR_E_RETURNCODE
****************************************************************************************************
*/
ADDR_E_RETURNCODE Lib::ComputeSurfaceInfo(
     const ADDR_COMPUTE_SURFACE_INFO_INPUT* pIn,    ///< [in] input structure
     ADDR_COMPUTE_SURFACE_INFO_OUTPUT*      pOut    ///< [out] output structure
     ) const
{
    ADDR_E_RETURNCODE returnCode = ADDR_OK;

    if (GetFillSizeFieldsFlags() == TRUE)
    {
        if ((pIn->size != sizeof(ADDR_COMPUTE_SURFACE_INFO_INPUT)) ||
            (pOut->size != sizeof(ADDR_COMPUTE_SURFACE_INFO_OUTPUT)))
        {
            returnCode = ADDR_PARAMSIZEMISMATCH;
        }
    }

    // We suggest client do sanity check but a check here is also good
    if (pIn->bpp > 128)
    {
        returnCode = ADDR_INVALIDPARAMS;
    }

    if ((pIn->tileMode == ADDR_TM_UNKNOWN) && (pIn->mipLevel > 0))
    {
        returnCode = ADDR_INVALIDPARAMS;
    }

    // Thick modes don't support multisample
    if ((Thickness(pIn->tileMode) > 1) && (pIn->numSamples > 1))
    {
        returnCode = ADDR_INVALIDPARAMS;
    }

    if (returnCode == ADDR_OK)
    {
        // Get a local copy of input structure and only reference pIn for unadjusted values
        ADDR_COMPUTE_SURFACE_INFO_INPUT localIn = *pIn;
        ADDR_TILEINFO tileInfoNull = {0};

        if (UseTileInfo())
        {
            // If the original input has a valid ADDR_TILEINFO pointer then copy its contents.
            // Otherwise the default 0's in tileInfoNull are used.
            if (pIn->pTileInfo)
            {
                tileInfoNull = *pIn->pTileInfo;
            }
            localIn.pTileInfo  = &tileInfoNull;
        }

        localIn.numSamples = (pIn->numSamples == 0) ? 1 : pIn->numSamples;

        // Do mipmap check first
        // If format is BCn, pre-pad dimension to power-of-two according to HWL
        ComputeMipLevel(&localIn);

        if (m_configFlags.checkLast2DLevel)
        {
            // Save this level's original height in pixels
            pOut->height = pIn->height;
        }

        UINT_32 expandX = 1;
        UINT_32 expandY = 1;
        ElemMode elemMode;

        // Save outputs that may not go through HWL
        pOut->pixelBits = localIn.bpp;
        pOut->numSamples = localIn.numSamples;
        pOut->last2DLevel = FALSE;
        pOut->tcCompatible = FALSE;

#if !ALT_TEST
        if (localIn.numSamples > 1)
        {
            ADDR_ASSERT(localIn.mipLevel == 0);
        }
#endif

        if (localIn.format != ADDR_FMT_INVALID) // Set format to INVALID will skip this conversion
        {
            // Get compression/expansion factors and element mode
            // (which indicates compression/expansion
            localIn.bpp = GetElemLib()->GetBitsPerPixel(localIn.format,
                                                        &elemMode,
                                                        &expandX,
                                                        &expandY);

            // Special flag for 96 bit surface. 96 (or 48 if we support) bit surface's width is
            // pre-multiplied by 3 and bpp is divided by 3. So pitch alignment for linear-
            // aligned does not meet 64-pixel in real. We keep special handling in hwl since hw
            // restrictions are different.
            // Also Mip 1+ needs an element pitch of 32 bits so we do not need this workaround
            // but we use this flag to skip RestoreSurfaceInfo below

            if ((elemMode == ADDR_EXPANDED) && (expandX > 1))
            {
                ADDR_ASSERT(IsLinear(localIn.tileMode));
            }

            GetElemLib()->AdjustSurfaceInfo(elemMode,
                                            expandX,
                                            expandY,
                                            &localIn.bpp,
                                            &localIn.basePitch,
                                            &localIn.width,
                                            &localIn.height);

            // Overwrite these parameters if we have a valid format
        }
        else if (localIn.bpp != 0)
        {
            localIn.width  = (localIn.width != 0) ? localIn.width : 1;
            localIn.height = (localIn.height != 0) ? localIn.height : 1;
        }
        else // Rule out some invalid parameters
        {
            ADDR_ASSERT_ALWAYS();

            returnCode = ADDR_INVALIDPARAMS;
        }

        // Check mipmap after surface expansion
        if (returnCode == ADDR_OK)
        {
            returnCode = PostComputeMipLevel(&localIn, pOut);
        }

        if (returnCode == ADDR_OK)
        {
            if (UseTileIndex(localIn.tileIndex))
            {
                // Make sure pTileInfo is not NULL
                ADDR_ASSERT(localIn.pTileInfo);

                UINT_32 numSamples = GetNumFragments(localIn.numSamples, localIn.numFrags);

                INT_32 macroModeIndex = TileIndexNoMacroIndex;

                if (localIn.tileIndex != TileIndexLinearGeneral)
                {
                    // Try finding a macroModeIndex
                    macroModeIndex = HwlComputeMacroModeIndex(localIn.tileIndex,
                                                              localIn.flags,
                                                              localIn.bpp,
                                                              numSamples,
                                                              localIn.pTileInfo,
                                                              &localIn.tileMode,
                                                              &localIn.tileType);
                }

                // If macroModeIndex is not needed, then call HwlSetupTileCfg to get tile info
                if (macroModeIndex == TileIndexNoMacroIndex)
                {
                    returnCode = HwlSetupTileCfg(localIn.bpp,
                                                 localIn.tileIndex, macroModeIndex,
                                                 localIn.pTileInfo,
                                                 &localIn.tileMode, &localIn.tileType);
                }
                // If macroModeIndex is invalid, then assert this is not macro tiled
                else if (macroModeIndex == TileIndexInvalid)
                {
                    ADDR_ASSERT(!IsMacroTiled(localIn.tileMode));
                }

                pOut->macroModeIndex = macroModeIndex;
            }
        }

        if (returnCode == ADDR_OK)
        {
            localIn.flags.dccPipeWorkaround = localIn.flags.dccCompatible;

            if (localIn.tileMode == ADDR_TM_UNKNOWN)
            {
                // HWL layer may override tile mode if necessary
                HwlSelectTileMode(&localIn);
            }
            else
            {
                // HWL layer may override tile mode if necessary
                HwlOverrideTileMode(&localIn);

                // Optimize tile mode if possible
                OptimizeTileMode(&localIn);
            }
        }

        // Call main function to compute surface info
        if (returnCode == ADDR_OK)
        {
            returnCode = HwlComputeSurfaceInfo(&localIn, pOut);
        }

        if (returnCode == ADDR_OK)
        {
            // Since bpp might be changed we just pass it through
            pOut->bpp  = localIn.bpp;

            // Also original width/height/bpp
            pOut->pixelPitch    = pOut->pitch;
            pOut->pixelHeight   = pOut->height;

#if DEBUG
            if (localIn.flags.display)
            {
                ADDR_ASSERT((pOut->pitchAlign % 32) == 0);
            }
#endif //DEBUG

            if (localIn.format != ADDR_FMT_INVALID)
            {
                //
                // Note: For 96 bit surface, the pixelPitch returned might be an odd number, but it
                // is okay to program texture pitch as HW's mip calculator would multiply 3 first,
                // then do the appropriate paddings (linear alignment requirement and possible the
                // nearest power-of-two for mipmaps), which results in the original pitch.
                //
                GetElemLib()->RestoreSurfaceInfo(elemMode,
                                                 expandX,
                                                 expandY,
                                                 &localIn.bpp,
                                                 &pOut->pixelPitch,
                                                 &pOut->pixelHeight);
            }

            if (localIn.flags.qbStereo)
            {
                if (pOut->pStereoInfo)
                {
                    ComputeQbStereoInfo(pOut);
                }
            }

            if (localIn.flags.volume) // For volume sliceSize equals to all z-slices
            {
                pOut->sliceSize = pOut->surfSize;
            }
            else // For array: sliceSize is likely to have slice-padding (the last one)
            {
                pOut->sliceSize = pOut->surfSize / pOut->depth;

                // array or cubemap
                if (pIn->numSlices > 1)
                {
                    // If this is the last slice then add the padding size to this slice
                    if (pIn->slice == (pIn->numSlices - 1))
                    {
                        pOut->sliceSize += pOut->sliceSize * (pOut->depth - pIn->numSlices);
                    }
                    else if (m_configFlags.checkLast2DLevel)
                    {
                        // Reset last2DLevel flag if this is not the last array slice
                        pOut->last2DLevel = FALSE;
                    }
                }
            }

            pOut->pitchTileMax = pOut->pitch / 8 - 1;
            pOut->heightTileMax = pOut->height / 8 - 1;
            pOut->sliceTileMax = pOut->pitch * pOut->height / 64 - 1;
        }
    }

    return returnCode;
}

/**
****************************************************************************************************
*   Lib::ComputeSurfaceInfo
*
*   @brief
*       Interface function stub of AddrComputeSurfaceInfo.
*
*   @return
*       ADDR_E_RETURNCODE
****************************************************************************************************
*/
ADDR_E_RETURNCODE Lib::ComputeSurfaceAddrFromCoord(
    const ADDR_COMPUTE_SURFACE_ADDRFROMCOORD_INPUT* pIn,    ///< [in] input structure
    ADDR_COMPUTE_SURFACE_ADDRFROMCOORD_OUTPUT*      pOut    ///< [out] output structure
    ) const
{
    ADDR_E_RETURNCODE returnCode = ADDR_OK;

    if (GetFillSizeFieldsFlags() == TRUE)
    {
        if ((pIn->size != sizeof(ADDR_COMPUTE_SURFACE_ADDRFROMCOORD_INPUT)) ||
            (pOut->size != sizeof(ADDR_COMPUTE_SURFACE_ADDRFROMCOORD_OUTPUT)))
        {
            returnCode = ADDR_PARAMSIZEMISMATCH;
        }
    }

    if (returnCode == ADDR_OK)
    {
        ADDR_TILEINFO tileInfoNull;
        ADDR_COMPUTE_SURFACE_ADDRFROMCOORD_INPUT input;

        if (UseTileIndex(pIn->tileIndex))
        {
            input = *pIn;
            // Use temp tile info for calcalation
            input.pTileInfo = &tileInfoNull;

            const ADDR_SURFACE_FLAGS flags = {{0}};
            UINT_32 numSamples = GetNumFragments(pIn->numSamples, pIn->numFrags);

            // Try finding a macroModeIndex
            INT_32 macroModeIndex = HwlComputeMacroModeIndex(input.tileIndex,
                                                             flags,
                                                             input.bpp,
                                                             numSamples,
                                                             input.pTileInfo,
                                                             &input.tileMode,
                                                             &input.tileType);

            // If macroModeIndex is not needed, then call HwlSetupTileCfg to get tile info
            if (macroModeIndex == TileIndexNoMacroIndex)
            {
                returnCode = HwlSetupTileCfg(input.bpp, input.tileIndex, macroModeIndex,
                                             input.pTileInfo, &input.tileMode, &input.tileType);
            }
            // If macroModeIndex is invalid, then assert this is not macro tiled
            else if (macroModeIndex == TileIndexInvalid)
            {
                ADDR_ASSERT(!IsMacroTiled(input.tileMode));
            }

            // Change the input structure
            pIn = &input;
        }

        if (returnCode == ADDR_OK)
        {
            returnCode = HwlComputeSurfaceAddrFromCoord(pIn, pOut);

            if (returnCode == ADDR_OK)
            {
                pOut->prtBlockIndex = static_cast<UINT_32>(pOut->addr / (64 * 1024));
            }
        }
    }

    return returnCode;
}

/**
****************************************************************************************************
*   Lib::ComputeSurfaceCoordFromAddr
*
*   @brief
*       Interface function stub of ComputeSurfaceCoordFromAddr.
*
*   @return
*       ADDR_E_RETURNCODE
****************************************************************************************************
*/
ADDR_E_RETURNCODE Lib::ComputeSurfaceCoordFromAddr(
    const ADDR_COMPUTE_SURFACE_COORDFROMADDR_INPUT* pIn,    ///< [in] input structure
    ADDR_COMPUTE_SURFACE_COORDFROMADDR_OUTPUT*      pOut    ///< [out] output structure
    ) const
{
    ADDR_E_RETURNCODE returnCode = ADDR_OK;

    if (GetFillSizeFieldsFlags() == TRUE)
    {
        if ((pIn->size != sizeof(ADDR_COMPUTE_SURFACE_COORDFROMADDR_INPUT)) ||
            (pOut->size != sizeof(ADDR_COMPUTE_SURFACE_COORDFROMADDR_OUTPUT)))
        {
            returnCode = ADDR_PARAMSIZEMISMATCH;
        }
    }

    if (returnCode == ADDR_OK)
    {
        ADDR_TILEINFO tileInfoNull;
        ADDR_COMPUTE_SURFACE_COORDFROMADDR_INPUT input;

        if (UseTileIndex(pIn->tileIndex))
        {
            input = *pIn;
            // Use temp tile info for calcalation
            input.pTileInfo = &tileInfoNull;

            const ADDR_SURFACE_FLAGS flags = {{0}};
            UINT_32 numSamples = GetNumFragments(pIn->numSamples, pIn->numFrags);

            // Try finding a macroModeIndex
            INT_32 macroModeIndex = HwlComputeMacroModeIndex(input.tileIndex,
                                                             flags,
                                                             input.bpp,
                                                             numSamples,
                                                             input.pTileInfo,
                                                             &input.tileMode,
                                                             &input.tileType);

            // If macroModeIndex is not needed, then call HwlSetupTileCfg to get tile info
            if (macroModeIndex == TileIndexNoMacroIndex)
            {
                returnCode = HwlSetupTileCfg(input.bpp, input.tileIndex, macroModeIndex,
                                             input.pTileInfo, &input.tileMode, &input.tileType);
            }
            // If macroModeIndex is invalid, then assert this is not macro tiled
            else if (macroModeIndex == TileIndexInvalid)
            {
                ADDR_ASSERT(!IsMacroTiled(input.tileMode));
            }

            // Change the input structure
            pIn = &input;
        }

        if (returnCode == ADDR_OK)
        {
            returnCode = HwlComputeSurfaceCoordFromAddr(pIn, pOut);
        }
    }

    return returnCode;
}

/**
****************************************************************************************************
*   Lib::ComputeSliceTileSwizzle
*
*   @brief
*       Interface function stub of ComputeSliceTileSwizzle.
*
*   @return
*       ADDR_E_RETURNCODE
****************************************************************************************************
*/
ADDR_E_RETURNCODE Lib::ComputeSliceTileSwizzle(
    const ADDR_COMPUTE_SLICESWIZZLE_INPUT*  pIn,    ///< [in] input structure
    ADDR_COMPUTE_SLICESWIZZLE_OUTPUT*       pOut    ///< [out] output structure
    ) const
{
    ADDR_E_RETURNCODE returnCode = ADDR_OK;

    if (GetFillSizeFieldsFlags() == TRUE)
    {
        if ((pIn->size != sizeof(ADDR_COMPUTE_SLICESWIZZLE_INPUT)) ||
            (pOut->size != sizeof(ADDR_COMPUTE_SLICESWIZZLE_OUTPUT)))
        {
            returnCode = ADDR_PARAMSIZEMISMATCH;
        }
    }

    if (returnCode == ADDR_OK)
    {
        ADDR_TILEINFO tileInfoNull;
        ADDR_COMPUTE_SLICESWIZZLE_INPUT input;

        if (UseTileIndex(pIn->tileIndex))
        {
            input = *pIn;
            // Use temp tile info for calcalation
            input.pTileInfo = &tileInfoNull;

            returnCode = HwlSetupTileCfg(0, input.tileIndex, input.macroModeIndex,
                                         input.pTileInfo, &input.tileMode);
            // Change the input structure
            pIn = &input;
        }

        if (returnCode == ADDR_OK)
        {
            returnCode = HwlComputeSliceTileSwizzle(pIn, pOut);
        }
    }

    return returnCode;
}

/**
****************************************************************************************************
*   Lib::ExtractBankPipeSwizzle
*
*   @brief
*       Interface function stub of AddrExtractBankPipeSwizzle.
*
*   @return
*       ADDR_E_RETURNCODE
****************************************************************************************************
*/
ADDR_E_RETURNCODE Lib::ExtractBankPipeSwizzle(
    const ADDR_EXTRACT_BANKPIPE_SWIZZLE_INPUT*  pIn,    ///< [in] input structure
    ADDR_EXTRACT_BANKPIPE_SWIZZLE_OUTPUT*       pOut    ///< [out] output structure
    ) const
{
    ADDR_E_RETURNCODE returnCode = ADDR_OK;

    if (GetFillSizeFieldsFlags() == TRUE)
    {
        if ((pIn->size != sizeof(ADDR_EXTRACT_BANKPIPE_SWIZZLE_INPUT)) ||
            (pOut->size != sizeof(ADDR_EXTRACT_BANKPIPE_SWIZZLE_OUTPUT)))
        {
            returnCode = ADDR_PARAMSIZEMISMATCH;
        }
    }

    if (returnCode == ADDR_OK)
    {
        ADDR_TILEINFO tileInfoNull;
        ADDR_EXTRACT_BANKPIPE_SWIZZLE_INPUT input;

        if (UseTileIndex(pIn->tileIndex))
        {
            input = *pIn;
            // Use temp tile info for calcalation
            input.pTileInfo = &tileInfoNull;

            returnCode = HwlSetupTileCfg(0, input.tileIndex, input.macroModeIndex, input.pTileInfo);
            // Change the input structure
            pIn = &input;
        }

        if (returnCode == ADDR_OK)
        {
            returnCode = HwlExtractBankPipeSwizzle(pIn, pOut);
        }
    }

    return returnCode;
}

/**
****************************************************************************************************
*   Lib::CombineBankPipeSwizzle
*
*   @brief
*       Interface function stub of AddrCombineBankPipeSwizzle.
*
*   @return
*       ADDR_E_RETURNCODE
****************************************************************************************************
*/
ADDR_E_RETURNCODE Lib::CombineBankPipeSwizzle(
    const ADDR_COMBINE_BANKPIPE_SWIZZLE_INPUT*  pIn,    ///< [in] input structure
    ADDR_COMBINE_BANKPIPE_SWIZZLE_OUTPUT*       pOut    ///< [out] output structure
    ) const
{
    ADDR_E_RETURNCODE returnCode = ADDR_OK;

    if (GetFillSizeFieldsFlags() == TRUE)
    {
        if ((pIn->size != sizeof(ADDR_COMPUTE_FMASK_INFO_INPUT)) ||
            (pOut->size != sizeof(ADDR_COMPUTE_FMASK_INFO_OUTPUT)))
        {
            returnCode = ADDR_PARAMSIZEMISMATCH;
        }
    }

    if (returnCode == ADDR_OK)
    {
        ADDR_TILEINFO tileInfoNull;
        ADDR_COMBINE_BANKPIPE_SWIZZLE_INPUT input;

        if (UseTileIndex(pIn->tileIndex))
        {
            input = *pIn;
            // Use temp tile info for calcalation
            input.pTileInfo = &tileInfoNull;

            returnCode = HwlSetupTileCfg(0, input.tileIndex, input.macroModeIndex, input.pTileInfo);
            // Change the input structure
            pIn = &input;
        }

        if (returnCode == ADDR_OK)
        {
            returnCode = HwlCombineBankPipeSwizzle(pIn->bankSwizzle,
                                                   pIn->pipeSwizzle,
                                                   pIn->pTileInfo,
                                                   pIn->baseAddr,
                                                   &pOut->tileSwizzle);
        }
    }

    return returnCode;
}

/**
****************************************************************************************************
*   Lib::ComputeBaseSwizzle
*
*   @brief
*       Interface function stub of AddrCompueBaseSwizzle.
*   @return
*       ADDR_E_RETURNCODE
****************************************************************************************************
*/
ADDR_E_RETURNCODE Lib::ComputeBaseSwizzle(
    const ADDR_COMPUTE_BASE_SWIZZLE_INPUT*  pIn,
    ADDR_COMPUTE_BASE_SWIZZLE_OUTPUT* pOut) const
{
    ADDR_E_RETURNCODE returnCode = ADDR_OK;

    if (GetFillSizeFieldsFlags() == TRUE)
    {
        if ((pIn->size != sizeof(ADDR_COMPUTE_BASE_SWIZZLE_INPUT)) ||
            (pOut->size != sizeof(ADDR_COMPUTE_BASE_SWIZZLE_OUTPUT)))
        {
            returnCode = ADDR_PARAMSIZEMISMATCH;
        }
    }

    if (returnCode == ADDR_OK)
    {
        ADDR_TILEINFO tileInfoNull;
        ADDR_COMPUTE_BASE_SWIZZLE_INPUT input;

        if (UseTileIndex(pIn->tileIndex))
        {
            input = *pIn;
            // Use temp tile info for calcalation
            input.pTileInfo = &tileInfoNull;

            returnCode = HwlSetupTileCfg(0, input.tileIndex, input.macroModeIndex, input.pTileInfo);
            // Change the input structure
            pIn = &input;
        }

        if (returnCode == ADDR_OK)
        {
            if (IsMacroTiled(pIn->tileMode))
            {
                returnCode = HwlComputeBaseSwizzle(pIn, pOut);
            }
            else
            {
                pOut->tileSwizzle = 0;
            }
        }
    }

    return returnCode;
}

/**
****************************************************************************************************
*   Lib::ComputeFmaskInfo
*
*   @brief
*       Interface function stub of ComputeFmaskInfo.
*
*   @return
*       ADDR_E_RETURNCODE
****************************************************************************************************
*/
ADDR_E_RETURNCODE Lib::ComputeFmaskInfo(
    const ADDR_COMPUTE_FMASK_INFO_INPUT*    pIn,    ///< [in] input structure
    ADDR_COMPUTE_FMASK_INFO_OUTPUT*         pOut    ///< [out] output structure
    )
{
    ADDR_E_RETURNCODE returnCode = ADDR_OK;

    if (GetFillSizeFieldsFlags() == TRUE)
    {
        if ((pIn->size != sizeof(ADDR_COMPUTE_FMASK_INFO_INPUT)) ||
            (pOut->size != sizeof(ADDR_COMPUTE_FMASK_INFO_OUTPUT)))
        {
            returnCode = ADDR_PARAMSIZEMISMATCH;
        }
    }

    // No thick MSAA
    if (Thickness(pIn->tileMode) > 1)
    {
        returnCode = ADDR_INVALIDPARAMS;
    }

    if (returnCode == ADDR_OK)
    {
        ADDR_TILEINFO tileInfoNull;
        ADDR_COMPUTE_FMASK_INFO_INPUT input;

        if (UseTileIndex(pIn->tileIndex))
        {
            input = *pIn;

            if (pOut->pTileInfo)
            {
                // Use temp tile info for calcalation
                input.pTileInfo = pOut->pTileInfo;
            }
            else
            {
                input.pTileInfo = &tileInfoNull;
            }

            ADDR_SURFACE_FLAGS flags = {{0}};
            flags.fmask = 1;

            // Try finding a macroModeIndex
            INT_32 macroModeIndex = HwlComputeMacroModeIndex(pIn->tileIndex,
                                                             flags,
                                                             HwlComputeFmaskBits(pIn, NULL),
                                                             pIn->numSamples,
                                                             input.pTileInfo,
                                                             &input.tileMode);

            // If macroModeIndex is not needed, then call HwlSetupTileCfg to get tile info
            if (macroModeIndex == TileIndexNoMacroIndex)
            {
                returnCode = HwlSetupTileCfg(0, input.tileIndex, macroModeIndex,
                                             input.pTileInfo, &input.tileMode);
            }

            ADDR_ASSERT(macroModeIndex != TileIndexInvalid);

            // Change the input structure
            pIn = &input;
        }

        if (returnCode == ADDR_OK)
        {
            if (pIn->numSamples > 1)
            {
                returnCode = HwlComputeFmaskInfo(pIn, pOut);
            }
            else
            {
                memset(pOut, 0, sizeof(ADDR_COMPUTE_FMASK_INFO_OUTPUT));

                returnCode = ADDR_INVALIDPARAMS;
            }
        }
    }

    return returnCode;
}

/**
****************************************************************************************************
*   Lib::ComputeFmaskAddrFromCoord
*
*   @brief
*       Interface function stub of ComputeFmaskAddrFromCoord.
*
*   @return
*       ADDR_E_RETURNCODE
****************************************************************************************************
*/
ADDR_E_RETURNCODE Lib::ComputeFmaskAddrFromCoord(
    const ADDR_COMPUTE_FMASK_ADDRFROMCOORD_INPUT*   pIn,    ///< [in] input structure
    ADDR_COMPUTE_FMASK_ADDRFROMCOORD_OUTPUT*        pOut    ///< [out] output structure
    ) const
{
    ADDR_E_RETURNCODE returnCode = ADDR_OK;

    if (GetFillSizeFieldsFlags() == TRUE)
    {
        if ((pIn->size != sizeof(ADDR_COMPUTE_FMASK_ADDRFROMCOORD_INPUT)) ||
            (pOut->size != sizeof(ADDR_COMPUTE_FMASK_ADDRFROMCOORD_OUTPUT)))
        {
            returnCode = ADDR_PARAMSIZEMISMATCH;
        }
    }

    if (returnCode == ADDR_OK)
    {
        ADDR_ASSERT(pIn->numSamples > 1);

        if (pIn->numSamples > 1)
        {
            returnCode = HwlComputeFmaskAddrFromCoord(pIn, pOut);
        }
        else
        {
            returnCode = ADDR_INVALIDPARAMS;
        }
    }

    return returnCode;
}

/**
****************************************************************************************************
*   Lib::ComputeFmaskCoordFromAddr
*
*   @brief
*       Interface function stub of ComputeFmaskAddrFromCoord.
*
*   @return
*       ADDR_E_RETURNCODE
****************************************************************************************************
*/
ADDR_E_RETURNCODE Lib::ComputeFmaskCoordFromAddr(
    const ADDR_COMPUTE_FMASK_COORDFROMADDR_INPUT*  pIn,     ///< [in] input structure
    ADDR_COMPUTE_FMASK_COORDFROMADDR_OUTPUT* pOut           ///< [out] output structure
    ) const
{
    ADDR_E_RETURNCODE returnCode = ADDR_OK;

    if (GetFillSizeFieldsFlags() == TRUE)
    {
        if ((pIn->size != sizeof(ADDR_COMPUTE_FMASK_COORDFROMADDR_INPUT)) ||
            (pOut->size != sizeof(ADDR_COMPUTE_FMASK_COORDFROMADDR_OUTPUT)))
        {
            returnCode = ADDR_PARAMSIZEMISMATCH;
        }
    }

    if (returnCode == ADDR_OK)
    {
        ADDR_ASSERT(pIn->numSamples > 1);

        if (pIn->numSamples > 1)
        {
            returnCode = HwlComputeFmaskCoordFromAddr(pIn, pOut);
        }
        else
        {
            returnCode = ADDR_INVALIDPARAMS;
        }
    }

    return returnCode;
}

/**
****************************************************************************************************
*   Lib::ConvertTileInfoToHW
*
*   @brief
*       Convert tile info from real value to HW register value in HW layer
*
*   @return
*       ADDR_E_RETURNCODE
****************************************************************************************************
*/
ADDR_E_RETURNCODE Lib::ConvertTileInfoToHW(
    const ADDR_CONVERT_TILEINFOTOHW_INPUT* pIn, ///< [in] input structure
    ADDR_CONVERT_TILEINFOTOHW_OUTPUT* pOut      ///< [out] output structure
    ) const
{
    ADDR_E_RETURNCODE returnCode = ADDR_OK;

    if (GetFillSizeFieldsFlags() == TRUE)
    {
        if ((pIn->size != sizeof(ADDR_CONVERT_TILEINFOTOHW_INPUT)) ||
            (pOut->size != sizeof(ADDR_CONVERT_TILEINFOTOHW_OUTPUT)))
        {
            returnCode = ADDR_PARAMSIZEMISMATCH;
        }
    }

    if (returnCode == ADDR_OK)
    {
        ADDR_TILEINFO tileInfoNull;
        ADDR_CONVERT_TILEINFOTOHW_INPUT input;
        // if pIn->reverse is TRUE, indices are ignored
        if (pIn->reverse == FALSE && UseTileIndex(pIn->tileIndex))
        {
            input = *pIn;
            input.pTileInfo = &tileInfoNull;

            returnCode = HwlSetupTileCfg(input.bpp, input.tileIndex,
                                         input.macroModeIndex, input.pTileInfo);

            pIn = &input;
        }

        if (returnCode == ADDR_OK)
        {
            returnCode = HwlConvertTileInfoToHW(pIn, pOut);
        }
    }

    return returnCode;
}

/**
****************************************************************************************************
*   Lib::ConvertTileIndex
*
*   @brief
*       Convert tile index to tile mode/type/info
*
*   @return
*       ADDR_E_RETURNCODE
****************************************************************************************************
*/
ADDR_E_RETURNCODE Lib::ConvertTileIndex(
    const ADDR_CONVERT_TILEINDEX_INPUT* pIn, ///< [in] input structure
    ADDR_CONVERT_TILEINDEX_OUTPUT* pOut      ///< [out] output structure
    ) const
{
    ADDR_E_RETURNCODE returnCode = ADDR_OK;

    if (GetFillSizeFieldsFlags() == TRUE)
    {
        if ((pIn->size != sizeof(ADDR_CONVERT_TILEINDEX_INPUT)) ||
            (pOut->size != sizeof(ADDR_CONVERT_TILEINDEX_OUTPUT)))
        {
            returnCode = ADDR_PARAMSIZEMISMATCH;
        }
    }

    if (returnCode == ADDR_OK)
    {

        returnCode = HwlSetupTileCfg(pIn->bpp, pIn->tileIndex, pIn->macroModeIndex,
                                     pOut->pTileInfo, &pOut->tileMode, &pOut->tileType);

        if (returnCode == ADDR_OK && pIn->tileInfoHw)
        {
            ADDR_CONVERT_TILEINFOTOHW_INPUT hwInput = {0};
            ADDR_CONVERT_TILEINFOTOHW_OUTPUT hwOutput = {0};

            hwInput.pTileInfo = pOut->pTileInfo;
            hwInput.tileIndex = -1;
            hwOutput.pTileInfo = pOut->pTileInfo;

            returnCode = HwlConvertTileInfoToHW(&hwInput, &hwOutput);
        }
    }

    return returnCode;
}

/**
****************************************************************************************************
*   Lib::GetMacroModeIndex
*
*   @brief
*       Get macro mode index based on input info
*
*   @return
*       ADDR_E_RETURNCODE
****************************************************************************************************
*/
ADDR_E_RETURNCODE Lib::GetMacroModeIndex(
    const ADDR_GET_MACROMODEINDEX_INPUT* pIn, ///< [in] input structure
    ADDR_GET_MACROMODEINDEX_OUTPUT*      pOut ///< [out] output structure
    ) const
{
    ADDR_E_RETURNCODE returnCode = ADDR_OK;

    if (GetFillSizeFieldsFlags())
    {
        if ((pIn->size != sizeof(ADDR_GET_MACROMODEINDEX_INPUT)) ||
            (pOut->size != sizeof(ADDR_GET_MACROMODEINDEX_OUTPUT)))
        {
            returnCode = ADDR_PARAMSIZEMISMATCH;
        }
    }

    if (returnCode == ADDR_OK)
    {
        ADDR_TILEINFO tileInfo = {0};
        pOut->macroModeIndex = HwlComputeMacroModeIndex(pIn->tileIndex, pIn->flags, pIn->bpp,
                                                        pIn->numFrags, &tileInfo);
    }

    return returnCode;
}

/**
****************************************************************************************************
*   Lib::ConvertTileIndex1
*
*   @brief
*       Convert tile index to tile mode/type/info
*
*   @return
*       ADDR_E_RETURNCODE
****************************************************************************************************
*/
ADDR_E_RETURNCODE Lib::ConvertTileIndex1(
    const ADDR_CONVERT_TILEINDEX1_INPUT* pIn,   ///< [in] input structure
    ADDR_CONVERT_TILEINDEX_OUTPUT* pOut         ///< [out] output structure
    ) const
{
    ADDR_E_RETURNCODE returnCode = ADDR_OK;

    if (GetFillSizeFieldsFlags() == TRUE)
    {
        if ((pIn->size != sizeof(ADDR_CONVERT_TILEINDEX1_INPUT)) ||
            (pOut->size != sizeof(ADDR_CONVERT_TILEINDEX_OUTPUT)))
        {
            returnCode = ADDR_PARAMSIZEMISMATCH;
        }
    }

    if (returnCode == ADDR_OK)
    {
        ADDR_SURFACE_FLAGS flags = {{0}};

        HwlComputeMacroModeIndex(pIn->tileIndex, flags, pIn->bpp, pIn->numSamples,
                                 pOut->pTileInfo, &pOut->tileMode, &pOut->tileType);

        if (pIn->tileInfoHw)
        {
            ADDR_CONVERT_TILEINFOTOHW_INPUT hwInput = {0};
            ADDR_CONVERT_TILEINFOTOHW_OUTPUT hwOutput = {0};

            hwInput.pTileInfo = pOut->pTileInfo;
            hwInput.tileIndex = -1;
            hwOutput.pTileInfo = pOut->pTileInfo;

            returnCode = HwlConvertTileInfoToHW(&hwInput, &hwOutput);
        }
    }

    return returnCode;
}

/**
****************************************************************************************************
*   Lib::GetTileIndex
*
*   @brief
*       Get tile index from tile mode/type/info
*
*   @return
*       ADDR_E_RETURNCODE
****************************************************************************************************
*/
ADDR_E_RETURNCODE Lib::GetTileIndex(
    const ADDR_GET_TILEINDEX_INPUT* pIn, ///< [in] input structure
    ADDR_GET_TILEINDEX_OUTPUT* pOut      ///< [out] output structure
    ) const
{
    ADDR_E_RETURNCODE returnCode = ADDR_OK;

    if (GetFillSizeFieldsFlags() == TRUE)
    {
        if ((pIn->size != sizeof(ADDR_GET_TILEINDEX_INPUT)) ||
            (pOut->size != sizeof(ADDR_GET_TILEINDEX_OUTPUT)))
        {
            returnCode = ADDR_PARAMSIZEMISMATCH;
        }
    }

    if (returnCode == ADDR_OK)
    {
        returnCode = HwlGetTileIndex(pIn, pOut);
    }

    return returnCode;
}

/**
****************************************************************************************************
*   Lib::Thickness
*
*   @brief
*       Get tile mode thickness
*
*   @return
*       Tile mode thickness
****************************************************************************************************
*/
UINT_32 Lib::Thickness(
    AddrTileMode tileMode)    ///< [in] tile mode
{
    return ModeFlags[tileMode].thickness;
}



////////////////////////////////////////////////////////////////////////////////////////////////////
//                               CMASK/HTILE
////////////////////////////////////////////////////////////////////////////////////////////////////

/**
****************************************************************************************************
*   Lib::ComputeHtileInfo
*
*   @brief
*       Interface function stub of AddrComputeHtilenfo
*
*   @return
*       ADDR_E_RETURNCODE
****************************************************************************************************
*/
ADDR_E_RETURNCODE Lib::ComputeHtileInfo(
    const ADDR_COMPUTE_HTILE_INFO_INPUT*    pIn,    ///< [in] input structure
    ADDR_COMPUTE_HTILE_INFO_OUTPUT*         pOut    ///< [out] output structure
    ) const
{
    ADDR_E_RETURNCODE returnCode = ADDR_OK;

    BOOL_32 isWidth8  = (pIn->blockWidth == 8) ? TRUE : FALSE;
    BOOL_32 isHeight8 = (pIn->blockHeight == 8) ? TRUE : FALSE;

    if (GetFillSizeFieldsFlags() == TRUE)
    {
        if ((pIn->size != sizeof(ADDR_COMPUTE_HTILE_INFO_INPUT)) ||
            (pOut->size != sizeof(ADDR_COMPUTE_HTILE_INFO_OUTPUT)))
        {
            returnCode = ADDR_PARAMSIZEMISMATCH;
        }
    }

    if (returnCode == ADDR_OK)
    {
        ADDR_TILEINFO tileInfoNull;
        ADDR_COMPUTE_HTILE_INFO_INPUT input;

        if (UseTileIndex(pIn->tileIndex))
        {
            input = *pIn;
            // Use temp tile info for calcalation
            input.pTileInfo = &tileInfoNull;

            returnCode = HwlSetupTileCfg(0, input.tileIndex, input.macroModeIndex, input.pTileInfo);

            // Change the input structure
            pIn = &input;
        }

        if (returnCode == ADDR_OK)
        {
            pOut->bpp = ComputeHtileInfo(pIn->flags,
                                         pIn->pitch,
                                         pIn->height,
                                         pIn->numSlices,
                                         pIn->isLinear,
                                         isWidth8,
                                         isHeight8,
                                         pIn->pTileInfo,
                                         &pOut->pitch,
                                         &pOut->height,
                                         &pOut->htileBytes,
                                         &pOut->macroWidth,
                                         &pOut->macroHeight,
                                         &pOut->sliceSize,
                                         &pOut->baseAlign);

            if (pIn->flags.tcCompatible && (pIn->numSlices > 1))
            {
                pOut->sliceSize = pIn->pitch * pIn->height * 4 / (8 * 8);

                const UINT_32 align = HwlGetPipes(pIn->pTileInfo) * pIn->pTileInfo->banks * m_pipeInterleaveBytes;

                if ((pOut->sliceSize % align) == 0)
                {
                    pOut->sliceInterleaved = FALSE;
                }
                else
                {
                    pOut->sliceInterleaved = TRUE;
                }
            }
        }
    }

    return returnCode;
}

/**
****************************************************************************************************
*   Lib::ComputeCmaskInfo
*
*   @brief
*       Interface function stub of AddrComputeCmaskInfo
*
*   @return
*       ADDR_E_RETURNCODE
****************************************************************************************************
*/
ADDR_E_RETURNCODE Lib::ComputeCmaskInfo(
    const ADDR_COMPUTE_CMASK_INFO_INPUT*    pIn,    ///< [in] input structure
    ADDR_COMPUTE_CMASK_INFO_OUTPUT*         pOut    ///< [out] output structure
    ) const
{
    ADDR_E_RETURNCODE returnCode = ADDR_OK;

    if (GetFillSizeFieldsFlags() == TRUE)
    {
        if ((pIn->size != sizeof(ADDR_COMPUTE_CMASK_INFO_INPUT)) ||
            (pOut->size != sizeof(ADDR_COMPUTE_CMASK_INFO_OUTPUT)))
        {
            returnCode = ADDR_PARAMSIZEMISMATCH;
        }
    }

    if (returnCode == ADDR_OK)
    {
        ADDR_TILEINFO tileInfoNull;
        ADDR_COMPUTE_CMASK_INFO_INPUT input;

        if (UseTileIndex(pIn->tileIndex))
        {
            input = *pIn;
            // Use temp tile info for calcalation
            input.pTileInfo = &tileInfoNull;

            returnCode = HwlSetupTileCfg(0, input.tileIndex, input.macroModeIndex, input.pTileInfo);

            // Change the input structure
            pIn = &input;
        }

        if (returnCode == ADDR_OK)
        {
            returnCode = ComputeCmaskInfo(pIn->flags,
                                          pIn->pitch,
                                          pIn->height,
                                          pIn->numSlices,
                                          pIn->isLinear,
                                          pIn->pTileInfo,
                                          &pOut->pitch,
                                          &pOut->height,
                                          &pOut->cmaskBytes,
                                          &pOut->macroWidth,
                                          &pOut->macroHeight,
                                          &pOut->sliceSize,
                                          &pOut->baseAlign,
                                          &pOut->blockMax);
        }
    }

    return returnCode;
}

/**
****************************************************************************************************
*   Lib::ComputeDccInfo
*
*   @brief
*       Interface function to compute DCC key info
*
*   @return
*       return code of HwlComputeDccInfo
****************************************************************************************************
*/
ADDR_E_RETURNCODE Lib::ComputeDccInfo(
    const ADDR_COMPUTE_DCCINFO_INPUT*    pIn,    ///< [in] input structure
    ADDR_COMPUTE_DCCINFO_OUTPUT*         pOut    ///< [out] output structure
    ) const
{
    ADDR_E_RETURNCODE ret = ADDR_OK;

    if (GetFillSizeFieldsFlags() == TRUE)
    {
        if ((pIn->size != sizeof(ADDR_COMPUTE_DCCINFO_INPUT)) ||
            (pOut->size != sizeof(ADDR_COMPUTE_DCCINFO_OUTPUT)))
        {
            ret = ADDR_PARAMSIZEMISMATCH;
        }
    }

    if (ret == ADDR_OK)
    {
        ADDR_COMPUTE_DCCINFO_INPUT input;

        if (UseTileIndex(pIn->tileIndex))
        {
            input = *pIn;

            ret = HwlSetupTileCfg(input.bpp, input.tileIndex, input.macroModeIndex,
                                  &input.tileInfo, &input.tileMode);

            pIn = &input;
        }

        if (ADDR_OK == ret)
        {
            ret = HwlComputeDccInfo(pIn, pOut);
        }
    }

    return ret;
}

/**
****************************************************************************************************
*   Lib::ComputeHtileAddrFromCoord
*
*   @brief
*       Interface function stub of AddrComputeHtileAddrFromCoord
*
*   @return
*       ADDR_E_RETURNCODE
****************************************************************************************************
*/
ADDR_E_RETURNCODE Lib::ComputeHtileAddrFromCoord(
    const ADDR_COMPUTE_HTILE_ADDRFROMCOORD_INPUT*   pIn,    ///< [in] input structure
    ADDR_COMPUTE_HTILE_ADDRFROMCOORD_OUTPUT*        pOut    ///< [out] output structure
    ) const
{
    ADDR_E_RETURNCODE returnCode = ADDR_OK;

    BOOL_32 isWidth8  = (pIn->blockWidth == 8) ? TRUE : FALSE;
    BOOL_32 isHeight8 = (pIn->blockHeight == 8) ? TRUE : FALSE;

    if (GetFillSizeFieldsFlags() == TRUE)
    {
        if ((pIn->size != sizeof(ADDR_COMPUTE_HTILE_ADDRFROMCOORD_INPUT)) ||
            (pOut->size != sizeof(ADDR_COMPUTE_HTILE_ADDRFROMCOORD_OUTPUT)))
        {
            returnCode = ADDR_PARAMSIZEMISMATCH;
        }
    }

    if (returnCode == ADDR_OK)
    {
        ADDR_TILEINFO tileInfoNull;
        ADDR_COMPUTE_HTILE_ADDRFROMCOORD_INPUT input;

        if (UseTileIndex(pIn->tileIndex))
        {
            input = *pIn;
            // Use temp tile info for calcalation
            input.pTileInfo = &tileInfoNull;

            returnCode = HwlSetupTileCfg(0, input.tileIndex, input.macroModeIndex, input.pTileInfo);

            // Change the input structure
            pIn = &input;
        }

        if (returnCode == ADDR_OK)
        {
            if (pIn->flags.tcCompatible)
            {
                HwlComputeHtileAddrFromCoord(pIn, pOut);
            }
            else
            {
                pOut->addr = HwlComputeXmaskAddrFromCoord(pIn->pitch,
                                                          pIn->height,
                                                          pIn->x,
                                                          pIn->y,
                                                          pIn->slice,
                                                          pIn->numSlices,
                                                          1,
                                                          pIn->isLinear,
                                                          isWidth8,
                                                          isHeight8,
                                                          pIn->pTileInfo,
                                                          &pOut->bitPosition);
            }
        }
    }

    return returnCode;

}

/**
****************************************************************************************************
*   Lib::ComputeHtileCoordFromAddr
*
*   @brief
*       Interface function stub of AddrComputeHtileCoordFromAddr
*
*   @return
*       ADDR_E_RETURNCODE
****************************************************************************************************
*/
ADDR_E_RETURNCODE Lib::ComputeHtileCoordFromAddr(
    const ADDR_COMPUTE_HTILE_COORDFROMADDR_INPUT*   pIn,    ///< [in] input structure
    ADDR_COMPUTE_HTILE_COORDFROMADDR_OUTPUT*        pOut    ///< [out] output structure
    ) const
{
    ADDR_E_RETURNCODE returnCode = ADDR_OK;

    BOOL_32 isWidth8  = (pIn->blockWidth == 8) ? TRUE : FALSE;
    BOOL_32 isHeight8 = (pIn->blockHeight == 8) ? TRUE : FALSE;

    if (GetFillSizeFieldsFlags() == TRUE)
    {
        if ((pIn->size != sizeof(ADDR_COMPUTE_HTILE_COORDFROMADDR_INPUT)) ||
            (pOut->size != sizeof(ADDR_COMPUTE_HTILE_COORDFROMADDR_OUTPUT)))
        {
            returnCode = ADDR_PARAMSIZEMISMATCH;
        }
    }

    if (returnCode == ADDR_OK)
    {
        ADDR_TILEINFO tileInfoNull;
        ADDR_COMPUTE_HTILE_COORDFROMADDR_INPUT input;

        if (UseTileIndex(pIn->tileIndex))
        {
            input = *pIn;
            // Use temp tile info for calcalation
            input.pTileInfo = &tileInfoNull;

            returnCode = HwlSetupTileCfg(0, input.tileIndex, input.macroModeIndex, input.pTileInfo);

            // Change the input structure
            pIn = &input;
        }

        if (returnCode == ADDR_OK)
        {
            HwlComputeXmaskCoordFromAddr(pIn->addr,
                                         pIn->bitPosition,
                                         pIn->pitch,
                                         pIn->height,
                                         pIn->numSlices,
                                         1,
                                         pIn->isLinear,
                                         isWidth8,
                                         isHeight8,
                                         pIn->pTileInfo,
                                         &pOut->x,
                                         &pOut->y,
                                         &pOut->slice);
        }
    }

    return returnCode;
}

/**
****************************************************************************************************
*   Lib::ComputeCmaskAddrFromCoord
*
*   @brief
*       Interface function stub of AddrComputeCmaskAddrFromCoord
*
*   @return
*       ADDR_E_RETURNCODE
****************************************************************************************************
*/
ADDR_E_RETURNCODE Lib::ComputeCmaskAddrFromCoord(
    const ADDR_COMPUTE_CMASK_ADDRFROMCOORD_INPUT*   pIn,    ///< [in] input structure
    ADDR_COMPUTE_CMASK_ADDRFROMCOORD_OUTPUT*        pOut    ///< [out] output structure
    ) const
{
    ADDR_E_RETURNCODE returnCode = ADDR_OK;

    if (GetFillSizeFieldsFlags() == TRUE)
    {
        if ((pIn->size != sizeof(ADDR_COMPUTE_CMASK_ADDRFROMCOORD_INPUT)) ||
            (pOut->size != sizeof(ADDR_COMPUTE_CMASK_ADDRFROMCOORD_OUTPUT)))
        {
            returnCode = ADDR_PARAMSIZEMISMATCH;
        }
    }

    if (returnCode == ADDR_OK)
    {
        ADDR_TILEINFO tileInfoNull;
        ADDR_COMPUTE_CMASK_ADDRFROMCOORD_INPUT input;

        if (UseTileIndex(pIn->tileIndex))
        {
            input = *pIn;
            // Use temp tile info for calcalation
            input.pTileInfo = &tileInfoNull;

            returnCode = HwlSetupTileCfg(0, input.tileIndex, input.macroModeIndex, input.pTileInfo);

            // Change the input structure
            pIn = &input;
        }

        if (returnCode == ADDR_OK)
        {
            if (pIn->flags.tcCompatible == TRUE)
            {
                returnCode = HwlComputeCmaskAddrFromCoord(pIn, pOut);
            }
            else
            {
                pOut->addr = HwlComputeXmaskAddrFromCoord(pIn->pitch,
                                                          pIn->height,
                                                          pIn->x,
                                                          pIn->y,
                                                          pIn->slice,
                                                          pIn->numSlices,
                                                          2,
                                                          pIn->isLinear,
                                                          FALSE, //this is cmask, isWidth8 is not needed
                                                          FALSE, //this is cmask, isHeight8 is not needed
                                                          pIn->pTileInfo,
                                                          &pOut->bitPosition);
            }

        }
    }

    return returnCode;
}

/**
****************************************************************************************************
*   Lib::ComputeCmaskCoordFromAddr
*
*   @brief
*       Interface function stub of AddrComputeCmaskCoordFromAddr
*
*   @return
*       ADDR_E_RETURNCODE
****************************************************************************************************
*/
ADDR_E_RETURNCODE Lib::ComputeCmaskCoordFromAddr(
    const ADDR_COMPUTE_CMASK_COORDFROMADDR_INPUT*   pIn,    ///< [in] input structure
    ADDR_COMPUTE_CMASK_COORDFROMADDR_OUTPUT*        pOut    ///< [out] output structure
    ) const
{
    ADDR_E_RETURNCODE returnCode = ADDR_OK;

    if (GetFillSizeFieldsFlags() == TRUE)
    {
        if ((pIn->size != sizeof(ADDR_COMPUTE_CMASK_COORDFROMADDR_INPUT)) ||
            (pOut->size != sizeof(ADDR_COMPUTE_CMASK_COORDFROMADDR_OUTPUT)))
        {
            returnCode = ADDR_PARAMSIZEMISMATCH;
        }
    }

    if (returnCode == ADDR_OK)
    {
        ADDR_TILEINFO tileInfoNull;
        ADDR_COMPUTE_CMASK_COORDFROMADDR_INPUT input;

        if (UseTileIndex(pIn->tileIndex))
        {
            input = *pIn;
            // Use temp tile info for calcalation
            input.pTileInfo = &tileInfoNull;

            returnCode = HwlSetupTileCfg(0, input.tileIndex, input.macroModeIndex, input.pTileInfo);

            // Change the input structure
            pIn = &input;
        }

        if (returnCode == ADDR_OK)
        {
            HwlComputeXmaskCoordFromAddr(pIn->addr,
                                         pIn->bitPosition,
                                         pIn->pitch,
                                         pIn->height,
                                         pIn->numSlices,
                                         2,
                                         pIn->isLinear,
                                         FALSE,
                                         FALSE,
                                         pIn->pTileInfo,
                                         &pOut->x,
                                         &pOut->y,
                                         &pOut->slice);
        }
    }

    return returnCode;
}

/**
****************************************************************************************************
*   Lib::ComputeTileDataWidthAndHeight
*
*   @brief
*       Compute the squared cache shape for per-tile data (CMASK and HTILE)
*
*   @return
*       N/A
*
*   @note
*       MacroWidth and macroHeight are measured in pixels
****************************************************************************************************
*/
VOID Lib::ComputeTileDataWidthAndHeight(
    UINT_32         bpp,             ///< [in] bits per pixel
    UINT_32         cacheBits,       ///< [in] bits of cache
    ADDR_TILEINFO*  pTileInfo,       ///< [in] Tile info
    UINT_32*        pMacroWidth,     ///< [out] macro tile width
    UINT_32*        pMacroHeight     ///< [out] macro tile height
    ) const
{
    UINT_32 height = 1;
    UINT_32 width  = cacheBits / bpp;
    UINT_32 pipes  = HwlGetPipes(pTileInfo);

    // Double height until the macro-tile is close to square
    // Height can only be doubled if width is even

    while ((width > height * 2 * pipes) && !(width & 1))
    {
        width  /= 2;
        height *= 2;
    }

    *pMacroWidth  = 8 * width;
    *pMacroHeight = 8 * height * pipes;

    // Note: The above iterative comptuation is equivalent to the following
    //
    //int log2_height = ((log2(cacheBits)-log2(bpp)-log2(pipes))/2);
    //int macroHeight = pow2( 3+log2(pipes)+log2_height );
}

/**
****************************************************************************************************
*   Lib::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 Lib::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(bpp != 4);              // Cmask does not support linear layout prior to SI
    *pMacroWidth  = 8 * 512 / bpp;      // Align width to 512-bit memory accesses
    *pMacroHeight = 8 * m_pipes;        // Align height to number of pipes
}

/**
****************************************************************************************************
*   Lib::ComputeHtileInfo
*
*   @brief
*       Compute htile pitch,width, bytes per 2D slice
*
*   @return
*       Htile bpp i.e. How many bits for an 8x8 tile
*       Also returns by output parameters:
*       *Htile pitch, height, total size in bytes, macro-tile dimensions and slice size*
****************************************************************************************************
*/
UINT_32 Lib::ComputeHtileInfo(
    ADDR_HTILE_FLAGS flags,             ///< [in] htile flags
    UINT_32          pitchIn,           ///< [in] pitch input
    UINT_32          heightIn,          ///< [in] height input
    UINT_32          numSlices,         ///< [in] number of slices
    BOOL_32          isLinear,          ///< [in] if it is linear mode
    BOOL_32          isWidth8,          ///< [in] if htile block width is 8
    BOOL_32          isHeight8,         ///< [in] if htile block height is 8
    ADDR_TILEINFO*   pTileInfo,         ///< [in] Tile info
    UINT_32*         pPitchOut,         ///< [out] pitch output
    UINT_32*         pHeightOut,        ///< [out] height output
    UINT_64*         pHtileBytes,       ///< [out] bytes per 2D slice
    UINT_32*         pMacroWidth,       ///< [out] macro-tile width in pixels
    UINT_32*         pMacroHeight,      ///< [out] macro-tile width in pixels
    UINT_64*         pSliceSize,        ///< [out] slice size in bytes
    UINT_32*         pBaseAlign         ///< [out] base alignment
    ) const
{

    UINT_32 macroWidth;
    UINT_32 macroHeight;
    UINT_32 baseAlign;
    UINT_64 surfBytes;
    UINT_64 sliceBytes;

    numSlices = Max(1u, numSlices);

    const UINT_32 bpp = HwlComputeHtileBpp(isWidth8, isHeight8);
    const UINT_32 cacheBits = HtileCacheBits;

    if (isLinear)
    {
        HwlComputeTileDataWidthAndHeightLinear(&macroWidth,
                                               &macroHeight,
                                               bpp,
                                               pTileInfo);
    }
    else
    {
        ComputeTileDataWidthAndHeight(bpp,
                                      cacheBits,
                                      pTileInfo,
                                      &macroWidth,
                                      &macroHeight);
    }

    *pPitchOut = PowTwoAlign(pitchIn,  macroWidth);
    *pHeightOut = PowTwoAlign(heightIn,  macroHeight);

    baseAlign = HwlComputeHtileBaseAlign(flags.tcCompatible, isLinear, pTileInfo);

    surfBytes = HwlComputeHtileBytes(*pPitchOut,
                                     *pHeightOut,
                                     bpp,
                                     isLinear,
                                     numSlices,
                                     &sliceBytes,
                                     baseAlign);

    *pHtileBytes = surfBytes;

    //
    // Use SafeAssign since they are optional
    //
    SafeAssign(pMacroWidth, macroWidth);

    SafeAssign(pMacroHeight, macroHeight);

    SafeAssign(pSliceSize,  sliceBytes);

    SafeAssign(pBaseAlign, baseAlign);

    return bpp;
}

/**
****************************************************************************************************
*   Lib::ComputeCmaskBaseAlign
*
*   @brief
*       Compute cmask base alignment
*
*   @return
*       Cmask base alignment
****************************************************************************************************
*/
UINT_32 Lib::ComputeCmaskBaseAlign(
    ADDR_CMASK_FLAGS flags,           ///< [in] Cmask flags
    ADDR_TILEINFO*   pTileInfo        ///< [in] Tile info
    ) const
{
    UINT_32 baseAlign = m_pipeInterleaveBytes * HwlGetPipes(pTileInfo);

    if (flags.tcCompatible)
    {
        ADDR_ASSERT(pTileInfo != NULL);
        if (pTileInfo)
        {
            baseAlign *= pTileInfo->banks;
        }
    }

    return baseAlign;
}

/**
****************************************************************************************************
*   Lib::ComputeCmaskBytes
*
*   @brief
*       Compute cmask size in bytes
*
*   @return
*       Cmask size in bytes
****************************************************************************************************
*/
UINT_64 Lib::ComputeCmaskBytes(
    UINT_32 pitch,        ///< [in] pitch
    UINT_32 height,       ///< [in] height
    UINT_32 numSlices     ///< [in] number of slices
    ) const
{
    return BITS_TO_BYTES(static_cast<UINT_64>(pitch) * height * numSlices * CmaskElemBits) /
        MicroTilePixels;
}

/**
****************************************************************************************************
*   Lib::ComputeCmaskInfo
*
*   @brief
*       Compute cmask pitch,width, bytes per 2D slice
*
*   @return
*       BlockMax. Also by output parameters: Cmask pitch,height, total size in bytes,
*       macro-tile dimensions
****************************************************************************************************
*/
ADDR_E_RETURNCODE Lib::ComputeCmaskInfo(
    ADDR_CMASK_FLAGS flags,            ///< [in] cmask flags
    UINT_32          pitchIn,           ///< [in] pitch input
    UINT_32          heightIn,          ///< [in] height input
    UINT_32          numSlices,         ///< [in] number of slices
    BOOL_32          isLinear,          ///< [in] is linear mode
    ADDR_TILEINFO*   pTileInfo,         ///< [in] Tile info
    UINT_32*         pPitchOut,         ///< [out] pitch output
    UINT_32*         pHeightOut,        ///< [out] height output
    UINT_64*         pCmaskBytes,       ///< [out] bytes per 2D slice
    UINT_32*         pMacroWidth,       ///< [out] macro-tile width in pixels
    UINT_32*         pMacroHeight,      ///< [out] macro-tile width in pixels
    UINT_64*         pSliceSize,        ///< [out] slice size in bytes
    UINT_32*         pBaseAlign,        ///< [out] base alignment
    UINT_32*         pBlockMax          ///< [out] block max == slice / 128 / 128 - 1
    ) const
{
    UINT_32 macroWidth;
    UINT_32 macroHeight;
    UINT_32 baseAlign;
    UINT_64 surfBytes;
    UINT_64 sliceBytes;

    numSlices = Max(1u, numSlices);

    const UINT_32 bpp = CmaskElemBits;
    const UINT_32 cacheBits = CmaskCacheBits;

    ADDR_E_RETURNCODE returnCode = ADDR_OK;

    if (isLinear)
    {
        HwlComputeTileDataWidthAndHeightLinear(&macroWidth,
                                               &macroHeight,
                                               bpp,
                                               pTileInfo);
    }
    else
    {
        ComputeTileDataWidthAndHeight(bpp,
                                      cacheBits,
                                      pTileInfo,
                                      &macroWidth,
                                      &macroHeight);
    }

    *pPitchOut = (pitchIn + macroWidth - 1) & ~(macroWidth - 1);
    *pHeightOut = (heightIn + macroHeight - 1) & ~(macroHeight - 1);


    sliceBytes = ComputeCmaskBytes(*pPitchOut,
                                   *pHeightOut,
                                   1);

    baseAlign = ComputeCmaskBaseAlign(flags, pTileInfo);

    while (sliceBytes % baseAlign)
    {
        *pHeightOut += macroHeight;

        sliceBytes = ComputeCmaskBytes(*pPitchOut,
                                       *pHeightOut,
                                       1);
    }

    surfBytes = sliceBytes * numSlices;

    *pCmaskBytes = surfBytes;

    //
    // Use SafeAssign since they are optional
    //
    SafeAssign(pMacroWidth, macroWidth);

    SafeAssign(pMacroHeight, macroHeight);

    SafeAssign(pBaseAlign, baseAlign);

    SafeAssign(pSliceSize, sliceBytes);

    UINT_32 slice = (*pPitchOut) * (*pHeightOut);
    UINT_32 blockMax = slice / 128 / 128 - 1;

#if DEBUG
    if (slice % (64*256) != 0)
    {
        ADDR_ASSERT_ALWAYS();
    }
#endif //DEBUG

    UINT_32 maxBlockMax = HwlGetMaxCmaskBlockMax();

    if (blockMax > maxBlockMax)
    {
        blockMax = maxBlockMax;
        returnCode = ADDR_INVALIDPARAMS;
    }

    SafeAssign(pBlockMax, blockMax);

    return returnCode;
}

/**
****************************************************************************************************
*   Lib::ComputeXmaskCoordYFromPipe
*
*   @brief
*       Compute the Y coord from pipe number for cmask/htile
*
*   @return
*       Y coordinate
*
****************************************************************************************************
*/
UINT_32 Lib::ComputeXmaskCoordYFromPipe(
    UINT_32         pipe,       ///< [in] pipe number
    UINT_32         x           ///< [in] x coordinate
    ) const
{
    UINT_32 pipeBit0;
    UINT_32 pipeBit1;
    UINT_32 xBit0;
    UINT_32 xBit1;
    UINT_32 yBit0;
    UINT_32 yBit1;

    UINT_32 y = 0;

    UINT_32 numPipes = m_pipes; // SI has its implementation
    //
    // Convert pipe + x to y coordinate.
    //
    switch (numPipes)
    {
        case 1:
            //
            // 1 pipe
            //
            // p0 = 0
            //
            y = 0;
            break;
        case 2:
            //
            // 2 pipes
            //
            // p0 = x0 ^ y0
            //
            // y0 = p0 ^ x0
            //
            pipeBit0 = pipe & 0x1;

            xBit0 = x & 0x1;

            yBit0 = pipeBit0 ^ xBit0;

            y = yBit0;
            break;
        case 4:
            //
            // 4 pipes
            //
            // p0 = x1 ^ y0
            // p1 = x0 ^ y1
            //
            // y0 = p0 ^ x1
            // y1 = p1 ^ x0
            //
            pipeBit0 =  pipe & 0x1;
            pipeBit1 = (pipe & 0x2) >> 1;

            xBit0 =  x & 0x1;
            xBit1 = (x & 0x2) >> 1;

            yBit0 = pipeBit0 ^ xBit1;
            yBit1 = pipeBit1 ^ xBit0;

            y = (yBit0 |
                 (yBit1 << 1));
            break;
        case 8:
            //
            // 8 pipes
            //
            // r600 and r800 have different method
            //
            y = HwlComputeXmaskCoordYFrom8Pipe(pipe, x);
            break;
        default:
            break;
    }
    return y;
}

/**
****************************************************************************************************
*   Lib::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 Lib::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] 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*        pX,             ///< [out] x coord
    UINT_32*        pY,             ///< [out] y coord
    UINT_32*        pSlice          ///< [out] slice index
    ) const
{
    UINT_32 pipe;
    UINT_32 numPipes;
    UINT_32 numPipeBits;
    UINT_32 macroTilePitch;
    UINT_32 macroTileHeight;

    UINT_64 bitAddr;

    UINT_32 microTileCoordY;

    UINT_32 elemBits;

    UINT_32 pitchAligned = pitch;
    UINT_32 heightAligned = height;
    UINT_64 totalBytes;

    UINT_64 elemOffset;

    UINT_64 macroIndex;
    UINT_32 microIndex;

    UINT_64 macroNumber;
    UINT_32 microNumber;

    UINT_32 macroX;
    UINT_32 macroY;
    UINT_32 macroZ;

    UINT_32 microX;
    UINT_32 microY;

    UINT_32 tilesPerMacro;
    UINT_32 macrosPerPitch;
    UINT_32 macrosPerSlice;

    //
    // Extract pipe.
    //
    numPipes = HwlGetPipes(pTileInfo);
    pipe = ComputePipeFromAddr(addr, numPipes);

    //
    // Compute the number of group and pipe bits.
    //
    numPipeBits  = Log2(numPipes);

    UINT_32 groupBits = 8 * m_pipeInterleaveBytes;
    UINT_32 pipes = numPipes;


    //
    // Compute the micro tile size, in bits. And macro tile pitch and height.
    //
    if (factor == 2) //CMASK
    {
        ADDR_CMASK_FLAGS flags = {{0}};

        elemBits = CmaskElemBits;

        ComputeCmaskInfo(flags,
                         pitch,
                         height,
                         numSlices,
                         isLinear,
                         pTileInfo,
                         &pitchAligned,
                         &heightAligned,
                         &totalBytes,
                         &macroTilePitch,
                         &macroTileHeight);
    }
    else  //HTILE
    {
        ADDR_HTILE_FLAGS flags = {{0}};

        if (factor != 1)
        {
            factor = 1;
        }

        elemBits = HwlComputeHtileBpp(isWidth8, isHeight8);

        ComputeHtileInfo(flags,
                         pitch,
                         height,
                         numSlices,
                         isLinear,
                         isWidth8,
                         isHeight8,
                         pTileInfo,
                         &pitchAligned,
                         &heightAligned,
                         &totalBytes,
                         &macroTilePitch,
                         &macroTileHeight);
    }

    // Should use aligned dims
    //
    pitch = pitchAligned;
    height = heightAligned;


    //
    // Convert byte address to bit address.
    //
    bitAddr = BYTES_TO_BITS(addr) + bitPosition;


    //
    // Remove pipe bits from address.
    //

    bitAddr = (bitAddr % groupBits) + ((bitAddr/groupBits/pipes)*groupBits);


    elemOffset = bitAddr / elemBits;

    tilesPerMacro = (macroTilePitch/factor) * macroTileHeight / MicroTilePixels >> numPipeBits;

    macrosPerPitch = pitch / (macroTilePitch/factor);
    macrosPerSlice = macrosPerPitch * height / macroTileHeight;

    macroIndex = elemOffset / factor / tilesPerMacro;
    microIndex = static_cast<UINT_32>(elemOffset % (tilesPerMacro * factor));

    macroNumber = macroIndex * factor + microIndex % factor;
    microNumber = microIndex / factor;

    macroX = static_cast<UINT_32>((macroNumber % macrosPerPitch));
    macroY = static_cast<UINT_32>((macroNumber % macrosPerSlice) / macrosPerPitch);
    macroZ = static_cast<UINT_32>((macroNumber / macrosPerSlice));


    microX = microNumber % (macroTilePitch / factor / MicroTileWidth);
    microY = (microNumber / (macroTilePitch / factor / MicroTileHeight));

    *pX = macroX * (macroTilePitch/factor) + microX * MicroTileWidth;
    *pY = macroY * macroTileHeight + (microY * MicroTileHeight << numPipeBits);
    *pSlice = macroZ;

    microTileCoordY = ComputeXmaskCoordYFromPipe(pipe,
                                                 *pX/MicroTileWidth);


    //
    // Assemble final coordinates.
    //
    *pY += microTileCoordY * MicroTileHeight;

}

/**
****************************************************************************************************
*   Lib::HwlComputeXmaskAddrFromCoord
*
*   @brief
*       Compute the address from an address of cmask (prior to si)
*
*   @return
*       Address in bytes
*
****************************************************************************************************
*/
UINT_64 Lib::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_64 addr;
    UINT_32 numGroupBits;
    UINT_32 numPipeBits;
    UINT_32 newPitch = 0;
    UINT_32 newHeight = 0;
    UINT_64 sliceBytes = 0;
    UINT_64 totalBytes = 0;
    UINT_64 sliceOffset;
    UINT_32 pipe;
    UINT_32 macroTileWidth;
    UINT_32 macroTileHeight;
    UINT_32 macroTilesPerRow;
    UINT_32 macroTileBytes;
    UINT_32 macroTileIndexX;
    UINT_32 macroTileIndexY;
    UINT_64 macroTileOffset;
    UINT_32 pixelBytesPerRow;
    UINT_32 pixelOffsetX;
    UINT_32 pixelOffsetY;
    UINT_32 pixelOffset;
    UINT_64 totalOffset;
    UINT_64 offsetLo;
    UINT_64 offsetHi;
    UINT_64 groupMask;


    UINT_32 elemBits = 0;

    UINT_32 numPipes = m_pipes; // This function is accessed prior to si only

    if (factor == 2) //CMASK
    {
        elemBits = CmaskElemBits;

        // For asics before SI, cmask is always tiled
        isLinear = FALSE;
    }
    else //HTILE
    {
        if (factor != 1) // Fix compile warning
        {
            factor = 1;
        }

        elemBits = HwlComputeHtileBpp(isWidth8, isHeight8);
    }

    //
    // Compute the number of group bits and pipe bits.
    //
    numGroupBits = Log2(m_pipeInterleaveBytes);
    numPipeBits  = Log2(numPipes);

    //
    // Compute macro tile dimensions.
    //
    if (factor == 2) // CMASK
    {
        ADDR_CMASK_FLAGS flags = {{0}};

        ComputeCmaskInfo(flags,
                         pitch,
                         height,
                         numSlices,
                         isLinear,
                         pTileInfo,
                         &newPitch,
                         &newHeight,
                         &totalBytes,
                         &macroTileWidth,
                         &macroTileHeight);

        sliceBytes = totalBytes / numSlices;
    }
    else // HTILE
    {
        ADDR_HTILE_FLAGS flags = {{0}};

        ComputeHtileInfo(flags,
                         pitch,
                         height,
                         numSlices,
                         isLinear,
                         isWidth8,
                         isHeight8,
                         pTileInfo,
                         &newPitch,
                         &newHeight,
                         &totalBytes,
                         &macroTileWidth,
                         &macroTileHeight,
                         &sliceBytes);
    }

    sliceOffset = slice * sliceBytes;

    //
    // Get the pipe.  Note that neither slice rotation nor pipe swizzling apply for CMASK.
    //
    pipe = ComputePipeFromCoord(x,
                                y,
                                0,
                                ADDR_TM_2D_TILED_THIN1,
                                0,
                                FALSE,
                                pTileInfo);

    //
    // Compute the number of macro tiles per row.
    //
    macroTilesPerRow = newPitch / macroTileWidth;

    //
    // Compute the number of bytes per macro tile.
    //
    macroTileBytes = BITS_TO_BYTES((macroTileWidth * macroTileHeight * elemBits) / MicroTilePixels);

    //
    // Compute the offset to the macro tile containing the specified coordinate.
    //
    macroTileIndexX = x / macroTileWidth;
    macroTileIndexY = y / macroTileHeight;
    macroTileOffset = ((macroTileIndexY * macroTilesPerRow) + macroTileIndexX) * macroTileBytes;

    //
    // Compute the pixel offset within the macro tile.
    //
    pixelBytesPerRow = BITS_TO_BYTES(macroTileWidth * elemBits) / MicroTileWidth;

    //
    // The nibbles are interleaved (see below), so the part of the offset relative to the x
    // coordinate repeats halfway across the row. (Not for HTILE)
    //
    if (factor == 2)
    {
        pixelOffsetX = (x % (macroTileWidth / 2)) / MicroTileWidth;
    }
    else
    {
        pixelOffsetX = (x % (macroTileWidth)) / MicroTileWidth * BITS_TO_BYTES(elemBits);
    }

    //
    // Compute the y offset within the macro tile.
    //
    pixelOffsetY = (((y % macroTileHeight) / MicroTileHeight) / numPipes) * pixelBytesPerRow;

    pixelOffset = pixelOffsetX + pixelOffsetY;

    //
    // Combine the slice offset and macro tile offset with the pixel offset, accounting for the
    // pipe bits in the middle of the address.
    //
    totalOffset = ((sliceOffset + macroTileOffset) >> numPipeBits) + pixelOffset;

    //
    // Split the offset to put some bits below the pipe bits and some above.
    //
    groupMask = (1 << numGroupBits) - 1;
    offsetLo  = totalOffset &  groupMask;
    offsetHi  = (totalOffset & ~groupMask) << numPipeBits;

    //
    // Assemble the address from its components.
    //
    addr  = offsetLo;
    addr |= offsetHi;
    // This is to remove warning with /analyze option
    UINT_32 pipeBits = pipe << numGroupBits;
    addr |= pipeBits;

    //
    // Compute the bit position.  The lower nibble is used when the x coordinate within the macro
    // tile is less than half of the macro tile width, and the upper nibble is used when the x
    // coordinate within the macro tile is greater than or equal to half the macro tile width.
    //
    *pBitPosition = ((x % macroTileWidth) < (macroTileWidth / factor)) ? 0 : 4;

    return addr;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
//                               Surface Addressing Shared
////////////////////////////////////////////////////////////////////////////////////////////////////

/**
****************************************************************************************************
*   Lib::ComputeSurfaceAddrFromCoordLinear
*
*   @brief
*       Compute address from coord for linear surface
*
*   @return
*       Address in bytes
*
****************************************************************************************************
*/
UINT_64 Lib::ComputeSurfaceAddrFromCoordLinear(
    UINT_32  x,              ///< [in] x coord
    UINT_32  y,              ///< [in] y coord
    UINT_32  slice,          ///< [in] slice/depth index
    UINT_32  sample,         ///< [in] sample index
    UINT_32  bpp,            ///< [in] bits per pixel
    UINT_32  pitch,          ///< [in] pitch
    UINT_32  height,         ///< [in] height
    UINT_32  numSlices,      ///< [in] number of slices
    UINT_32* pBitPosition    ///< [out] bit position inside a byte
    ) const
{
    const UINT_64 sliceSize = static_cast<UINT_64>(pitch) * height;

    UINT_64 sliceOffset = (slice + sample * numSlices)* sliceSize;
    UINT_64 rowOffset   = static_cast<UINT_64>(y) * pitch;
    UINT_64 pixOffset   = x;

    UINT_64 addr = (sliceOffset + rowOffset + pixOffset) * bpp;

    *pBitPosition = static_cast<UINT_32>(addr % 8);
    addr /= 8;

    return addr;
}

/**
****************************************************************************************************
*   Lib::ComputeSurfaceCoordFromAddrLinear
*
*   @brief
*       Compute the coord from an address of a linear surface
*
*   @return
*       N/A
****************************************************************************************************
*/
VOID Lib::ComputeSurfaceCoordFromAddrLinear(
    UINT_64  addr,           ///< [in] address
    UINT_32  bitPosition,    ///< [in] bitPosition in a byte
    UINT_32  bpp,            ///< [in] bits per pixel
    UINT_32  pitch,          ///< [in] pitch
    UINT_32  height,         ///< [in] height
    UINT_32  numSlices,      ///< [in] number of slices
    UINT_32* pX,             ///< [out] x coord
    UINT_32* pY,             ///< [out] y coord
    UINT_32* pSlice,         ///< [out] slice/depth index
    UINT_32* pSample         ///< [out] sample index
    ) const
{
    const UINT_64 sliceSize = static_cast<UINT_64>(pitch) * height;
    const UINT_64 linearOffset = (BYTES_TO_BITS(addr) + bitPosition) / bpp;

    *pX = static_cast<UINT_32>((linearOffset % sliceSize) % pitch);
    *pY = static_cast<UINT_32>((linearOffset % sliceSize) / pitch % height);
    *pSlice  = static_cast<UINT_32>((linearOffset / sliceSize) % numSlices);
    *pSample = static_cast<UINT_32>((linearOffset / sliceSize) / numSlices);
}

/**
****************************************************************************************************
*   Lib::ComputeSurfaceCoordFromAddrMicroTiled
*
*   @brief
*       Compute the coord from an address of a micro tiled surface
*
*   @return
*       N/A
****************************************************************************************************
*/
VOID Lib::ComputeSurfaceCoordFromAddrMicroTiled(
    UINT_64         addr,               ///< [in] address
    UINT_32         bitPosition,        ///< [in] bitPosition in a byte
    UINT_32         bpp,                ///< [in] bits per pixel
    UINT_32         pitch,              ///< [in] pitch
    UINT_32         height,             ///< [in] height
    UINT_32         numSamples,         ///< [in] number of samples
    AddrTileMode    tileMode,           ///< [in] tile mode
    UINT_32         tileBase,           ///< [in] base offset within a tile
    UINT_32         compBits,           ///< [in] component bits actually needed(for planar surface)
    UINT_32*        pX,                 ///< [out] x coord
    UINT_32*        pY,                 ///< [out] y coord
    UINT_32*        pSlice,             ///< [out] slice/depth index
    UINT_32*        pSample,            ///< [out] sample index,
    AddrTileType    microTileType,      ///< [in] micro tiling order
    BOOL_32         isDepthSampleOrder  ///< [in] TRUE if in depth sample order
    ) const
{
    UINT_64 bitAddr;
    UINT_32 microTileThickness;
    UINT_32 microTileBits;
    UINT_64 sliceBits;
    UINT_64 rowBits;
    UINT_32 sliceIndex;
    UINT_32 microTileCoordX;
    UINT_32 microTileCoordY;
    UINT_32 pixelOffset;
    UINT_32 pixelCoordX = 0;
    UINT_32 pixelCoordY = 0;
    UINT_32 pixelCoordZ = 0;
    UINT_32 pixelCoordS = 0;

    //
    // Convert byte address to bit address.
    //
    bitAddr = BYTES_TO_BITS(addr) + bitPosition;

    //
    // Compute the micro tile size, in bits.
    //
    switch (tileMode)
    {
        case ADDR_TM_1D_TILED_THICK:
            microTileThickness = ThickTileThickness;
            break;
        default:
            microTileThickness = 1;
            break;
    }

    microTileBits = MicroTilePixels * microTileThickness * bpp * numSamples;

    //
    // Compute number of bits per slice and number of bits per row of micro tiles.
    //
    sliceBits = static_cast<UINT_64>(pitch) * height * microTileThickness * bpp * numSamples;

    rowBits   = (pitch / MicroTileWidth) * microTileBits;

    //
    // Extract the slice index.
    //
    sliceIndex = static_cast<UINT_32>(bitAddr / sliceBits);
    bitAddr -= sliceIndex * sliceBits;

    //
    // Extract the y coordinate of the micro tile.
    //
    microTileCoordY = static_cast<UINT_32>(bitAddr / rowBits) * MicroTileHeight;
    bitAddr -= (microTileCoordY / MicroTileHeight) * rowBits;

    //
    // Extract the x coordinate of the micro tile.
    //
    microTileCoordX = static_cast<UINT_32>(bitAddr / microTileBits) * MicroTileWidth;

    //
    // Compute the pixel offset within the micro tile.
    //
    pixelOffset = static_cast<UINT_32>(bitAddr % microTileBits);

    //
    // Extract pixel coordinates from the offset.
    //
    HwlComputePixelCoordFromOffset(pixelOffset,
                                   bpp,
                                   numSamples,
                                   tileMode,
                                   tileBase,
                                   compBits,
                                   &pixelCoordX,
                                   &pixelCoordY,
                                   &pixelCoordZ,
                                   &pixelCoordS,
                                   microTileType,
                                   isDepthSampleOrder);

    //
    // Assemble final coordinates.
    //
    *pX     = microTileCoordX + pixelCoordX;
    *pY     = microTileCoordY + pixelCoordY;
    *pSlice = (sliceIndex * microTileThickness) + pixelCoordZ;
    *pSample = pixelCoordS;

    if (microTileThickness > 1)
    {
        *pSample = 0;
    }
}

/**
****************************************************************************************************
*   Lib::ComputePipeFromAddr
*
*   @brief
*       Compute the pipe number from an address
*
*   @return
*       Pipe number
*
****************************************************************************************************
*/
UINT_32 Lib::ComputePipeFromAddr(
    UINT_64 addr,        ///< [in] address
    UINT_32 numPipes     ///< [in] number of banks
    ) const
{
    UINT_32 pipe;

    UINT_32 groupBytes = m_pipeInterleaveBytes; //just different terms

    // R600
    // The LSBs of the address are arranged as follows:
    //   bank | pipe | group
    //
    // To get the pipe number, shift off the group bits and mask the pipe bits.
    //

    // R800
    // The LSBs of the address are arranged as follows:
    //   bank | bankInterleave | pipe | pipeInterleave
    //
    // To get the pipe number, shift off the pipe interleave bits and mask the pipe bits.
    //

    pipe = static_cast<UINT_32>(addr >> Log2(groupBytes)) & (numPipes - 1);

    return pipe;
}

/**
****************************************************************************************************
*   Lib::ComputeMicroTileEquation
*
*   @brief
*       Compute micro tile equation
*
*   @return
*       If equation can be computed
*
****************************************************************************************************
*/
ADDR_E_RETURNCODE Lib::ComputeMicroTileEquation(
    UINT_32         log2BytesPP,    ///< [in] log2 of bytes per pixel
    AddrTileMode    tileMode,       ///< [in] tile mode
    AddrTileType    microTileType,  ///< [in] pixel order in display/non-display mode
    ADDR_EQUATION*  pEquation       ///< [out] equation
    ) const
{
    ADDR_E_RETURNCODE retCode = ADDR_OK;

    for (UINT_32 i = 0; i < log2BytesPP; i++)
    {
        pEquation->addr[i].valid = 1;
        pEquation->addr[i].channel = 0;
        pEquation->addr[i].index = i;
    }

    ADDR_CHANNEL_SETTING* pixelBit = &pEquation->addr[log2BytesPP];

    ADDR_CHANNEL_SETTING x0 = InitChannel(1, 0, log2BytesPP + 0);
    ADDR_CHANNEL_SETTING x1 = InitChannel(1, 0, log2BytesPP + 1);
    ADDR_CHANNEL_SETTING x2 = InitChannel(1, 0, log2BytesPP + 2);
    ADDR_CHANNEL_SETTING y0 = InitChannel(1, 1, 0);
    ADDR_CHANNEL_SETTING y1 = InitChannel(1, 1, 1);
    ADDR_CHANNEL_SETTING y2 = InitChannel(1, 1, 2);
    ADDR_CHANNEL_SETTING z0 = InitChannel(1, 2, 0);
    ADDR_CHANNEL_SETTING z1 = InitChannel(1, 2, 1);
    ADDR_CHANNEL_SETTING z2 = InitChannel(1, 2, 2);

    UINT_32 thickness = Thickness(tileMode);
    UINT_32 bpp = 1 << (log2BytesPP + 3);

    if (microTileType != ADDR_THICK)
    {
        if (microTileType == ADDR_DISPLAYABLE)
        {
            switch (bpp)
            {
                case 8:
                    pixelBit[0] = x0;
                    pixelBit[1] = x1;
                    pixelBit[2] = x2;
                    pixelBit[3] = y1;
                    pixelBit[4] = y0;
                    pixelBit[5] = y2;
                    break;
                case 16:
                    pixelBit[0] = x0;
                    pixelBit[1] = x1;
                    pixelBit[2] = x2;
                    pixelBit[3] = y0;
                    pixelBit[4] = y1;
                    pixelBit[5] = y2;
                    break;
                case 32:
                    pixelBit[0] = x0;
                    pixelBit[1] = x1;
                    pixelBit[2] = y0;
                    pixelBit[3] = x2;
                    pixelBit[4] = y1;
                    pixelBit[5] = y2;
                    break;
                case 64:
                    pixelBit[0] = x0;
                    pixelBit[1] = y0;
                    pixelBit[2] = x1;
                    pixelBit[3] = x2;
                    pixelBit[4] = y1;
                    pixelBit[5] = y2;
                    break;
                case 128:
                    pixelBit[0] = y0;
                    pixelBit[1] = x0;
                    pixelBit[2] = x1;
                    pixelBit[3] = x2;
                    pixelBit[4] = y1;
                    pixelBit[5] = y2;
                    break;
                default:
                    ADDR_ASSERT_ALWAYS();
                    break;
            }
        }
        else if (microTileType == ADDR_NON_DISPLAYABLE || microTileType == ADDR_DEPTH_SAMPLE_ORDER)
        {
            pixelBit[0] = x0;
            pixelBit[1] = y0;
            pixelBit[2] = x1;
            pixelBit[3] = y1;
            pixelBit[4] = x2;
            pixelBit[5] = y2;
        }
        else if (microTileType == ADDR_ROTATED)
        {
            ADDR_ASSERT(thickness == 1);

            switch (bpp)
            {
                case 8:
                    pixelBit[0] = y0;
                    pixelBit[1] = y1;
                    pixelBit[2] = y2;
                    pixelBit[3] = x1;
                    pixelBit[4] = x0;
                    pixelBit[5] = x2;
                    break;
                case 16:
                    pixelBit[0] = y0;
                    pixelBit[1] = y1;
                    pixelBit[2] = y2;
                    pixelBit[3] = x0;
                    pixelBit[4] = x1;
                    pixelBit[5] = x2;
                    break;
                case 32:
                    pixelBit[0] = y0;
                    pixelBit[1] = y1;
                    pixelBit[2] = x0;
                    pixelBit[3] = y2;
                    pixelBit[4] = x1;
                    pixelBit[5] = x2;
                    break;
                case 64:
                    pixelBit[0] = y0;
                    pixelBit[1] = x0;
                    pixelBit[2] = y1;
                    pixelBit[3] = x1;
                    pixelBit[4] = x2;
                    pixelBit[5] = y2;
                    break;
                default:
                    retCode = ADDR_NOTSUPPORTED;
                    break;
            }
        }

        if (thickness > 1)
        {
            pixelBit[6] = z0;
            pixelBit[7] = z1;
            pEquation->numBits = 8 + log2BytesPP;
        }
        else
        {
            pEquation->numBits = 6 + log2BytesPP;
        }
    }
    else // ADDR_THICK
    {
        ADDR_ASSERT(thickness > 1);

        switch (bpp)
        {
            case 8:
            case 16:
                pixelBit[0] = x0;
                pixelBit[1] = y0;
                pixelBit[2] = x1;
                pixelBit[3] = y1;
                pixelBit[4] = z0;
                pixelBit[5] = z1;
                break;
            case 32:
                pixelBit[0] = x0;
                pixelBit[1] = y0;
                pixelBit[2] = x1;
                pixelBit[3] = z0;
                pixelBit[4] = y1;
                pixelBit[5] = z1;
                break;
            case 64:
            case 128:
                pixelBit[0] = x0;
                pixelBit[1] = y0;
                pixelBit[2] = z0;
                pixelBit[3] = x1;
                pixelBit[4] = y1;
                pixelBit[5] = z1;
                break;
            default:
                ADDR_ASSERT_ALWAYS();
                break;
        }

        pixelBit[6] = x2;
        pixelBit[7] = y2;
        pEquation->numBits = 8 + log2BytesPP;
    }

    if (thickness == 8)
    {
        pixelBit[8] = z2;
        pEquation->numBits = 9 + log2BytesPP;
    }

    // stackedDepthSlices is used for addressing mode that a tile block contains multiple slices,
    // which is not supported by our address lib
    pEquation->stackedDepthSlices = FALSE;

    return retCode;
}

/**
****************************************************************************************************
*   Lib::ComputePixelIndexWithinMicroTile
*
*   @brief
*       Compute the pixel index inside a micro tile of surface
*
*   @return
*       Pixel index
*
****************************************************************************************************
*/
UINT_32 Lib::ComputePixelIndexWithinMicroTile(
    UINT_32         x,              ///< [in] x coord
    UINT_32         y,              ///< [in] y coord
    UINT_32         z,              ///< [in] slice/depth index
    UINT_32         bpp,            ///< [in] bits per pixel
    AddrTileMode    tileMode,       ///< [in] tile mode
    AddrTileType    microTileType   ///< [in] pixel order in display/non-display mode
    ) const
{
    UINT_32 pixelBit0 = 0;
    UINT_32 pixelBit1 = 0;
    UINT_32 pixelBit2 = 0;
    UINT_32 pixelBit3 = 0;
    UINT_32 pixelBit4 = 0;
    UINT_32 pixelBit5 = 0;
    UINT_32 pixelBit6 = 0;
    UINT_32 pixelBit7 = 0;
    UINT_32 pixelBit8 = 0;
    UINT_32 pixelNumber;

    UINT_32 x0 = _BIT(x, 0);
    UINT_32 x1 = _BIT(x, 1);
    UINT_32 x2 = _BIT(x, 2);
    UINT_32 y0 = _BIT(y, 0);
    UINT_32 y1 = _BIT(y, 1);
    UINT_32 y2 = _BIT(y, 2);
    UINT_32 z0 = _BIT(z, 0);
    UINT_32 z1 = _BIT(z, 1);
    UINT_32 z2 = _BIT(z, 2);

    UINT_32 thickness = Thickness(tileMode);

    // Compute the pixel number within the micro tile.

    if (microTileType != ADDR_THICK)
    {
        if (microTileType == ADDR_DISPLAYABLE)
        {
            switch (bpp)
            {
                case 8:
                    pixelBit0 = x0;
                    pixelBit1 = x1;
                    pixelBit2 = x2;
                    pixelBit3 = y1;
                    pixelBit4 = y0;
                    pixelBit5 = y2;
                    break;
                case 16:
                    pixelBit0 = x0;
                    pixelBit1 = x1;
                    pixelBit2 = x2;
                    pixelBit3 = y0;
                    pixelBit4 = y1;
                    pixelBit5 = y2;
                    break;
                case 32:
                    pixelBit0 = x0;
                    pixelBit1 = x1;
                    pixelBit2 = y0;
                    pixelBit3 = x2;
                    pixelBit4 = y1;
                    pixelBit5 = y2;
                    break;
                case 64:
                    pixelBit0 = x0;
                    pixelBit1 = y0;
                    pixelBit2 = x1;
                    pixelBit3 = x2;
                    pixelBit4 = y1;
                    pixelBit5 = y2;
                    break;
                case 128:
                    pixelBit0 = y0;
                    pixelBit1 = x0;
                    pixelBit2 = x1;
                    pixelBit3 = x2;
                    pixelBit4 = y1;
                    pixelBit5 = y2;
                    break;
                default:
                    ADDR_ASSERT_ALWAYS();
                    break;
            }
        }
        else if (microTileType == ADDR_NON_DISPLAYABLE || microTileType == ADDR_DEPTH_SAMPLE_ORDER)
        {
            pixelBit0 = x0;
            pixelBit1 = y0;
            pixelBit2 = x1;
            pixelBit3 = y1;
            pixelBit4 = x2;
            pixelBit5 = y2;
        }
        else if (microTileType == ADDR_ROTATED)
        {
            ADDR_ASSERT(thickness == 1);

            switch (bpp)
            {
                case 8:
                    pixelBit0 = y0;
                    pixelBit1 = y1;
                    pixelBit2 = y2;
                    pixelBit3 = x1;
                    pixelBit4 = x0;
                    pixelBit5 = x2;
                    break;
                case 16:
                    pixelBit0 = y0;
                    pixelBit1 = y1;
                    pixelBit2 = y2;
                    pixelBit3 = x0;
                    pixelBit4 = x1;
                    pixelBit5 = x2;
                    break;
                case 32:
                    pixelBit0 = y0;
                    pixelBit1 = y1;
                    pixelBit2 = x0;
                    pixelBit3 = y2;
                    pixelBit4 = x1;
                    pixelBit5 = x2;
                    break;
                case 64:
                    pixelBit0 = y0;
                    pixelBit1 = x0;
                    pixelBit2 = y1;
                    pixelBit3 = x1;
                    pixelBit4 = x2;
                    pixelBit5 = y2;
                    break;
                default:
                    ADDR_ASSERT_ALWAYS();
                    break;
            }
        }

        if (thickness > 1)
        {
            pixelBit6 = z0;
            pixelBit7 = z1;
        }
    }
    else // ADDR_THICK
    {
        ADDR_ASSERT(thickness > 1);

        switch (bpp)
        {
            case 8:
            case 16:
                pixelBit0 = x0;
                pixelBit1 = y0;
                pixelBit2 = x1;
                pixelBit3 = y1;
                pixelBit4 = z0;
                pixelBit5 = z1;
                break;
            case 32:
                pixelBit0 = x0;
                pixelBit1 = y0;
                pixelBit2 = x1;
                pixelBit3 = z0;
                pixelBit4 = y1;
                pixelBit5 = z1;
                break;
            case 64:
            case 128:
                pixelBit0 = x0;
                pixelBit1 = y0;
                pixelBit2 = z0;
                pixelBit3 = x1;
                pixelBit4 = y1;
                pixelBit5 = z1;
                break;
            default:
                ADDR_ASSERT_ALWAYS();
                break;
        }

        pixelBit6 = x2;
        pixelBit7 = y2;
    }

    if (thickness == 8)
    {
        pixelBit8 = z2;
    }

    pixelNumber = ((pixelBit0     ) |
                   (pixelBit1 << 1) |
                   (pixelBit2 << 2) |
                   (pixelBit3 << 3) |
                   (pixelBit4 << 4) |
                   (pixelBit5 << 5) |
                   (pixelBit6 << 6) |
                   (pixelBit7 << 7) |
                   (pixelBit8 << 8));

    return pixelNumber;
}

/**
****************************************************************************************************
*   Lib::AdjustPitchAlignment
*
*   @brief
*       Adjusts pitch alignment for flipping surface
*
*   @return
*       N/A
*
****************************************************************************************************
*/
VOID Lib::AdjustPitchAlignment(
    ADDR_SURFACE_FLAGS  flags,      ///< [in] Surface flags
    UINT_32*            pPitchAlign ///< [out] Pointer to pitch alignment
    ) const
{
    // Display engine hardwires lower 5 bit of GRPH_PITCH to ZERO which means 32 pixel alignment
    // Maybe it will be fixed in future but let's make it general for now.
    if (flags.display || flags.overlay)
    {
        *pPitchAlign = PowTwoAlign(*pPitchAlign, 32);

        if(flags.display)
        {
            *pPitchAlign = Max(m_minPitchAlignPixels, *pPitchAlign);
        }
    }
}

/**
****************************************************************************************************
*   Lib::PadDimensions
*
*   @brief
*       Helper function to pad dimensions
*
*   @return
*       N/A
*
****************************************************************************************************
*/
VOID Lib::PadDimensions(
    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*            pPitchAlign, ///< [in,out] pitch align could be changed in HwlPadDimensions
    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
{
    UINT_32 pitchAlign = *pPitchAlign;
    UINT_32 thickness = Thickness(tileMode);

    ADDR_ASSERT(padDims <= 3);

    //
    // Override padding for mip levels
    //
    if (mipLevel > 0)
    {
        if (flags.cube)
        {
            // for cubemap, we only pad when client call with 6 faces as an identity
            if (*pSlices > 1)
            {
                padDims = 3; // we should pad cubemap sub levels when we treat it as 3d texture
            }
            else
            {
                padDims = 2;
            }
        }
    }

    // Any possibilities that padDims is 0?
    if (padDims == 0)
    {
        padDims = 3;
    }

    if (IsPow2(pitchAlign))
    {
        *pPitch = PowTwoAlign((*pPitch), pitchAlign);
    }
    else // add this code to pass unit test, r600 linear mode is not align bpp to pow2 for linear
    {
        *pPitch += pitchAlign - 1;
        *pPitch /= pitchAlign;
        *pPitch *= pitchAlign;
    }

    if (padDims > 1)
    {
        if (IsPow2(heightAlign))
        {
            *pHeight = PowTwoAlign((*pHeight), heightAlign);
        }
        else
        {
            *pHeight += heightAlign - 1;
            *pHeight /= heightAlign;
            *pHeight *= heightAlign;
        }
    }

    if (padDims > 2 || thickness > 1)
    {
        // for cubemap single face, we do not pad slices.
        // if we pad it, the slice number should be set to 6 and current mip level > 1
        if (flags.cube && (!m_configFlags.noCubeMipSlicesPad || flags.cubeAsArray))
        {
            *pSlices = NextPow2(*pSlices);
        }

        // normal 3D texture or arrays or cubemap has a thick mode? (Just pass unit test)
        if (thickness > 1)
        {
            *pSlices = PowTwoAlign((*pSlices), sliceAlign);
        }

    }

    HwlPadDimensions(tileMode,
                     bpp,
                     flags,
                     numSamples,
                     pTileInfo,
                     mipLevel,
                     pPitch,
                     pPitchAlign,
                     *pHeight,
                     heightAlign);
}


/**
****************************************************************************************************
*   Lib::HwlPreHandleBaseLvl3xPitch
*
*   @brief
*       Pre-handler of 3x pitch (96 bit) adjustment
*
*   @return
*       Expected pitch
****************************************************************************************************
*/
UINT_32 Lib::HwlPreHandleBaseLvl3xPitch(
    const ADDR_COMPUTE_SURFACE_INFO_INPUT*  pIn,        ///< [in] input
    UINT_32                                 expPitch    ///< [in] pitch
    ) const
{
    ADDR_ASSERT(pIn->width == expPitch);
    //
    // If pitch is pre-multiplied by 3, we retrieve original one here to get correct miplevel size
    //
    if (ElemLib::IsExpand3x(pIn->format) &&
        pIn->mipLevel == 0 &&
        pIn->tileMode == ADDR_TM_LINEAR_ALIGNED)
    {
        expPitch /= 3;
        expPitch = NextPow2(expPitch);
    }

    return expPitch;
}

/**
****************************************************************************************************
*   Lib::HwlPostHandleBaseLvl3xPitch
*
*   @brief
*       Post-handler of 3x pitch adjustment
*
*   @return
*       Expected pitch
****************************************************************************************************
*/
UINT_32 Lib::HwlPostHandleBaseLvl3xPitch(
    const ADDR_COMPUTE_SURFACE_INFO_INPUT*  pIn,        ///< [in] input
    UINT_32                                 expPitch    ///< [in] pitch
    ) const
{
    //
    // 96 bits surface of sub levels require element pitch of 32 bits instead
    // So we just return pitch in 32 bit pixels without timing 3
    //
    if (ElemLib::IsExpand3x(pIn->format) &&
        pIn->mipLevel == 0 &&
        pIn->tileMode == ADDR_TM_LINEAR_ALIGNED)
    {
        expPitch *= 3;
    }

    return expPitch;
}


/**
****************************************************************************************************
*   Lib::IsMacroTiled
*
*   @brief
*       Check if the tile mode is macro tiled
*
*   @return
*       TRUE if it is macro tiled (2D/2B/3D/3B)
****************************************************************************************************
*/
BOOL_32 Lib::IsMacroTiled(
    AddrTileMode tileMode)  ///< [in] tile mode
{
   return ModeFlags[tileMode].isMacro;
}

/**
****************************************************************************************************
*   Lib::IsMacro3dTiled
*
*   @brief
*       Check if the tile mode is 3D macro tiled
*
*   @return
*       TRUE if it is 3D macro tiled
****************************************************************************************************
*/
BOOL_32 Lib::IsMacro3dTiled(
    AddrTileMode tileMode)  ///< [in] tile mode
{
    return ModeFlags[tileMode].isMacro3d;
}

/**
****************************************************************************************************
*   Lib::IsMicroTiled
*
*   @brief
*       Check if the tile mode is micro tiled
*
*   @return
*       TRUE if micro tiled
****************************************************************************************************
*/
BOOL_32 Lib::IsMicroTiled(
    AddrTileMode tileMode)  ///< [in] tile mode
{
    return ModeFlags[tileMode].isMicro;
}

/**
****************************************************************************************************
*   Lib::IsLinear
*
*   @brief
*       Check if the tile mode is linear
*
*   @return
*       TRUE if linear
****************************************************************************************************
*/
BOOL_32 Lib::IsLinear(
    AddrTileMode tileMode)  ///< [in] tile mode
{
    return ModeFlags[tileMode].isLinear;
}

/**
****************************************************************************************************
*   Lib::IsPrtNoRotationTileMode
*
*   @brief
*       Return TRUE if it is prt tile without rotation
*   @note
*       This function just used by CI
****************************************************************************************************
*/
BOOL_32 Lib::IsPrtNoRotationTileMode(
    AddrTileMode tileMode)
{
    return ModeFlags[tileMode].isPrtNoRotation;
}

/**
****************************************************************************************************
*   Lib::IsPrtTileMode
*
*   @brief
*       Return TRUE if it is prt tile
*   @note
*       This function just used by CI
****************************************************************************************************
*/
BOOL_32 Lib::IsPrtTileMode(
    AddrTileMode tileMode)
{
    return ModeFlags[tileMode].isPrt;
}

/**
****************************************************************************************************
*   Lib::ComputeMipLevel
*
*   @brief
*       Compute mipmap level width/height/slices
*   @return
*      N/A
****************************************************************************************************
*/
VOID Lib::ComputeMipLevel(
    ADDR_COMPUTE_SURFACE_INFO_INPUT* pIn ///< [in,out] Input structure
    ) const
{
    if (ElemLib::IsBlockCompressed(pIn->format))
    {
        if (pIn->mipLevel == 0)
        {
            // DXTn's level 0 must be multiple of 4
            // But there are exceptions:
            // 1. Internal surface creation in hostblt/vsblt/etc...
            // 2. Runtime doesn't reject ATI1/ATI2 whose width/height are not multiple of 4
            pIn->width = PowTwoAlign(pIn->width, 4);
            pIn->height = PowTwoAlign(pIn->height, 4);
        }
    }

    HwlComputeMipLevel(pIn);
}

/**
****************************************************************************************************
*   Lib::DegradeTo1D
*
*   @brief
*       Check if surface can be degraded to 1D
*   @return
*       TRUE if degraded
****************************************************************************************************
*/
BOOL_32 Lib::DegradeTo1D(
    UINT_32 width,                  ///< surface width
    UINT_32 height,                 ///< surface height
    UINT_32 macroTilePitchAlign,    ///< macro tile pitch align
    UINT_32 macroTileHeightAlign    ///< macro tile height align
    )
{
    BOOL_32 degrade = ((width < macroTilePitchAlign) || (height < macroTileHeightAlign));

    // Check whether 2D tiling still has too much footprint
    if (degrade == FALSE)
    {
        // Only check width and height as slices are aligned to thickness
        UINT_64 unalignedSize = width * height;

        UINT_32 alignedPitch = PowTwoAlign(width, macroTilePitchAlign);
        UINT_32 alignedHeight = PowTwoAlign(height, macroTileHeightAlign);
        UINT_64 alignedSize = alignedPitch * alignedHeight;

        // alignedSize > 1.5 * unalignedSize
        if (2 * alignedSize > 3 * unalignedSize)
        {
            degrade = TRUE;
        }
    }

    return degrade;
}

/**
****************************************************************************************************
*   Lib::OptimizeTileMode
*
*   @brief
*       Check if base level's tile mode can be optimized (degraded)
*   @return
*       N/A
****************************************************************************************************
*/
VOID Lib::OptimizeTileMode(
    ADDR_COMPUTE_SURFACE_INFO_INPUT*  pInOut     ///< [in, out] structure for surface info
    ) const
{
    AddrTileMode tileMode = pInOut->tileMode;

    BOOL_32 doOpt = (pInOut->flags.opt4Space == TRUE) ||
                    (pInOut->flags.minimizeAlignment == TRUE) ||
                    (pInOut->maxBaseAlign != 0);

    BOOL_32 convertToPrt = FALSE;

    // Optimization can only be done on level 0 and samples <= 1
    if ((doOpt == TRUE)                     &&
        (pInOut->mipLevel == 0)             &&
        (IsPrtTileMode(tileMode) == FALSE)  &&
        (pInOut->flags.prt == FALSE))
    {
        UINT_32 width = pInOut->width;
        UINT_32 height = pInOut->height;
        UINT_32 thickness = Thickness(tileMode);
        BOOL_32 macroTiledOK = TRUE;
        UINT_32 macroWidthAlign = 0;
        UINT_32 macroHeightAlign = 0;
        UINT_32 macroSizeAlign = 0;

        if (IsMacroTiled(tileMode))
        {
            macroTiledOK = HwlGetAlignmentInfoMacroTiled(pInOut,
                                                         &macroWidthAlign,
                                                         &macroHeightAlign,
                                                         &macroSizeAlign);
        }

        if (macroTiledOK)
        {
            if ((pInOut->flags.display == FALSE) &&
                (pInOut->flags.opt4Space == TRUE) &&
                (pInOut->numSamples <= 1))
            {
                // Check if linear mode is optimal
                if ((pInOut->height == 1) &&
                    (IsLinear(tileMode) == FALSE) &&
                    (ElemLib::IsBlockCompressed(pInOut->format) == FALSE) &&
                    (pInOut->flags.depth == FALSE) &&
                    (pInOut->flags.stencil == FALSE) &&
                    (m_configFlags.disableLinearOpt == FALSE) &&
                    (pInOut->flags.disableLinearOpt == FALSE))
                {
                    tileMode = ADDR_TM_LINEAR_ALIGNED;
                }
                else if (IsMacroTiled(tileMode) && (pInOut->flags.tcCompatible == FALSE))
                {
                    if (DegradeTo1D(width, height, macroWidthAlign, macroHeightAlign))
                    {
                        tileMode = (thickness == 1) ?
                                   ADDR_TM_1D_TILED_THIN1 : ADDR_TM_1D_TILED_THICK;
                    }
                    else if (thickness > 1)
                    {
                        // As in the following HwlComputeSurfaceInfo, thick modes may be degraded to
                        // thinner modes, we should re-evaluate whether the corresponding
                        // thinner modes should be degraded. If so, we choose 1D thick mode instead.
                        tileMode = DegradeLargeThickTile(pInOut->tileMode, pInOut->bpp);

                        if (tileMode != pInOut->tileMode)
                        {
                            // Get thickness again after large thick degrade
                            thickness = Thickness(tileMode);

                            ADDR_COMPUTE_SURFACE_INFO_INPUT input = *pInOut;
                            input.tileMode = tileMode;

                            macroTiledOK = HwlGetAlignmentInfoMacroTiled(&input,
                                                                         &macroWidthAlign,
                                                                         &macroHeightAlign,
                                                                         &macroSizeAlign);

                            if (macroTiledOK &&
                                DegradeTo1D(width, height, macroWidthAlign, macroHeightAlign))
                            {
                                tileMode = ADDR_TM_1D_TILED_THICK;
                            }
                        }
                    }
                }
            }

            if (macroTiledOK)
            {
                if ((pInOut->flags.minimizeAlignment == TRUE) &&
                    (pInOut->numSamples <= 1) &&
                    (IsMacroTiled(tileMode) == TRUE))
                {
                    UINT_32 macroSize = PowTwoAlign(width, macroWidthAlign) *
                                        PowTwoAlign(height, macroHeightAlign);
                    UINT_32 microSize = PowTwoAlign(width, MicroTileWidth) *
                                        PowTwoAlign(height, MicroTileHeight);

                    if (macroSize > microSize)
                    {
                        tileMode = (thickness == 1) ?
                                   ADDR_TM_1D_TILED_THIN1 : ADDR_TM_1D_TILED_THICK;
                    }
                }

                if ((pInOut->maxBaseAlign != 0) &&
                    (IsMacroTiled(tileMode) == TRUE))
                {
                    if (macroSizeAlign > pInOut->maxBaseAlign)
                    {
                        if (pInOut->numSamples > 1)
                        {
                            ADDR_ASSERT(pInOut->maxBaseAlign >= Block64K);

                            convertToPrt = TRUE;
                        }
                        else if (pInOut->maxBaseAlign < Block64K)
                        {
                            tileMode = (thickness == 1) ?
                                       ADDR_TM_1D_TILED_THIN1 : ADDR_TM_1D_TILED_THICK;
                        }
                        else
                        {
                            convertToPrt = TRUE;
                        }
                    }
                }
            }
        }
    }

    if (convertToPrt)
    {
        if ((pInOut->flags.matchStencilTileCfg == TRUE) && (pInOut->numSamples <= 1))
        {
            pInOut->tileMode = ADDR_TM_1D_TILED_THIN1;
        }
        else
        {
            HwlSetPrtTileMode(pInOut);
        }
    }
    else if (tileMode != pInOut->tileMode)
    {
        pInOut->tileMode = tileMode;
    }

    HwlOptimizeTileMode(pInOut);
}

/**
****************************************************************************************************
*   Lib::DegradeLargeThickTile
*
*   @brief
*       Check if the thickness needs to be reduced if a tile is too large
*   @return
*       The degraded tile mode (unchanged if not degraded)
****************************************************************************************************
*/
AddrTileMode Lib::DegradeLargeThickTile(
    AddrTileMode tileMode,
    UINT_32 bpp) const
{
    // Override tilemode
    // When tile_width (8) * tile_height (8) * thickness * element_bytes is > row_size,
    // it is better to just use THIN mode in this case
    UINT_32 thickness = Thickness(tileMode);

    if (thickness > 1 && m_configFlags.allowLargeThickTile == 0)
    {
        UINT_32 tileSize = MicroTilePixels * thickness * (bpp >> 3);

        if (tileSize > m_rowSize)
        {
            switch (tileMode)
            {
                case ADDR_TM_2D_TILED_XTHICK:
                    if ((tileSize >> 1) <= m_rowSize)
                    {
                        tileMode = ADDR_TM_2D_TILED_THICK;
                        break;
                    }
                    // else fall through
                case ADDR_TM_2D_TILED_THICK:
                    tileMode    = ADDR_TM_2D_TILED_THIN1;
                    break;

                case ADDR_TM_3D_TILED_XTHICK:
                    if ((tileSize >> 1) <= m_rowSize)
                    {
                        tileMode = ADDR_TM_3D_TILED_THICK;
                        break;
                    }
                    // else fall through
                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;
            }
        }
    }

    return tileMode;
}

/**
****************************************************************************************************
*   Lib::PostComputeMipLevel
*   @brief
*       Compute MipLevel info (including level 0) after surface adjustment
*   @return
*       ADDR_E_RETURNCODE
****************************************************************************************************
*/
ADDR_E_RETURNCODE Lib::PostComputeMipLevel(
    ADDR_COMPUTE_SURFACE_INFO_INPUT*    pIn,   ///< [in,out] Input structure
    ADDR_COMPUTE_SURFACE_INFO_OUTPUT*   pOut   ///< [out] Output structure
    ) const
{
    // Mipmap including level 0 must be pow2 padded since either SI hw expects so or it is
    // required by CFX  for Hw Compatibility between NI and SI. Otherwise it is only needed for
    // mipLevel > 0. Any h/w has different requirement should implement its own virtual function

    if (pIn->flags.pow2Pad)
    {
        pIn->width      = NextPow2(pIn->width);
        pIn->height     = NextPow2(pIn->height);
        pIn->numSlices  = NextPow2(pIn->numSlices);
    }
    else if (pIn->mipLevel > 0)
    {
        pIn->width      = NextPow2(pIn->width);
        pIn->height     = NextPow2(pIn->height);

        if (!pIn->flags.cube)
        {
            pIn->numSlices = NextPow2(pIn->numSlices);
        }

        // for cubemap, we keep its value at first
    }

    return ADDR_OK;
}

/**
****************************************************************************************************
*   Lib::HwlSetupTileCfg
*
*   @brief
*       Map tile index to tile setting.
*   @return
*       ADDR_E_RETURNCODE
****************************************************************************************************
*/
ADDR_E_RETURNCODE Lib::HwlSetupTileCfg(
    UINT_32         bpp,              ///< Bits per pixel
    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
{
    return ADDR_NOTSUPPORTED;
}

/**
****************************************************************************************************
*   Lib::HwlGetPipes
*
*   @brief
*       Get number pipes
*   @return
*       num pipes
****************************************************************************************************
*/
UINT_32 Lib::HwlGetPipes(
    const ADDR_TILEINFO* pTileInfo    ///< [in] Tile info
    ) const
{
    //pTileInfo can be NULL when asic is 6xx and 8xx.
    return m_pipes;
}

/**
****************************************************************************************************
*   Lib::ComputeQbStereoInfo
*
*   @brief
*       Get quad buffer stereo information
*   @return
*       N/A
****************************************************************************************************
*/
VOID Lib::ComputeQbStereoInfo(
    ADDR_COMPUTE_SURFACE_INFO_OUTPUT*       pOut    ///< [in,out] updated pOut+pStereoInfo
    ) const
{
    ADDR_ASSERT(pOut->bpp >= 8);
    ADDR_ASSERT((pOut->surfSize % pOut->baseAlign) == 0);

    // Save original height
    pOut->pStereoInfo->eyeHeight = pOut->height;

    // Right offset
    pOut->pStereoInfo->rightOffset = static_cast<UINT_32>(pOut->surfSize);

    pOut->pStereoInfo->rightSwizzle = HwlComputeQbStereoRightSwizzle(pOut);
    // Double height
    pOut->height <<= 1;
    pOut->pixelHeight <<= 1;

    // Double size
    pOut->surfSize <<= 1;

    // Right start address meets the base align since it is guaranteed by AddrLib1

    // 1D surface on SI may break this rule, but we can force it to meet by checking .qbStereo.
}


/**
****************************************************************************************************
*   Lib::ComputePrtInfo
*
*   @brief
*       Compute prt surface related info
*
*   @return
*       ADDR_E_RETURNCODE
****************************************************************************************************
*/
ADDR_E_RETURNCODE Lib::ComputePrtInfo(
    const ADDR_PRT_INFO_INPUT*  pIn,
    ADDR_PRT_INFO_OUTPUT*       pOut) const
{
    ADDR_ASSERT(pOut != NULL);

    ADDR_E_RETURNCODE returnCode = ADDR_OK;

    UINT_32     expandX = 1;
    UINT_32     expandY = 1;
    ElemMode    elemMode;

    UINT_32     bpp = GetElemLib()->GetBitsPerPixel(pIn->format,
                                                    &elemMode,
                                                    &expandX,
                                                    &expandY);

    if (bpp <8 || bpp == 24 || bpp == 48 || bpp == 96)
    {
        returnCode = ADDR_INVALIDPARAMS;
    }

    UINT_32     numFrags = pIn->numFrags;
    ADDR_ASSERT(numFrags <= 8);

    UINT_32     tileWidth = 0;
    UINT_32     tileHeight = 0;
    if (returnCode == ADDR_OK)
    {
        // 3D texture without depth or 2d texture
        if (pIn->baseMipDepth > 1 || pIn->baseMipHeight > 1)
        {
            if (bpp == 8)
            {
                tileWidth = 256;
                tileHeight = 256;
            }
            else if (bpp == 16)
            {
                tileWidth = 256;
                tileHeight = 128;
            }
            else if (bpp == 32)
            {
                tileWidth = 128;
                tileHeight = 128;
            }
            else if (bpp == 64)
            {
                // assume it is BC1/4
                tileWidth = 512;
                tileHeight = 256;

                if (elemMode == ADDR_UNCOMPRESSED)
                {
                    tileWidth = 128;
                    tileHeight = 64;
                }
            }
            else if (bpp == 128)
            {
                // assume it is BC2/3/5/6H/7
                tileWidth = 256;
                tileHeight = 256;

                if (elemMode == ADDR_UNCOMPRESSED)
                {
                    tileWidth = 64;
                    tileHeight = 64;
                }
            }

            if (numFrags == 2)
            {
                tileWidth = tileWidth / 2;
            }
            else if (numFrags == 4)
            {
                tileWidth = tileWidth / 2;
                tileHeight = tileHeight / 2;
            }
            else if (numFrags == 8)
            {
                tileWidth = tileWidth / 4;
                tileHeight = tileHeight / 2;
            }
        }
        else    // 1d
        {
            tileHeight = 1;
            if (bpp == 8)
            {
                tileWidth = 65536;
            }
            else if (bpp == 16)
            {
                tileWidth = 32768;
            }
            else if (bpp == 32)
            {
                tileWidth = 16384;
            }
            else if (bpp == 64)
            {
                tileWidth = 8192;
            }
            else if (bpp == 128)
            {
                tileWidth = 4096;
            }
        }
    }

    pOut->prtTileWidth = tileWidth;
    pOut->prtTileHeight = tileHeight;

    return returnCode;
}

} // V1
} // Addr
