/*!
******************************************************************************

 @Function              VDECDDUTILS_CreateStrUnit

 @Description           this function allocate a structure for a complete data unit

******************************************************************************/
IMG_RESULT VDECDDUTILS_CreateStrUnit(
    VDECDD_sStrUnit    ** ppsStrUnit,
    LST_T               * sBSList
)
{
    VDECDD_sStrUnit    * psStrUnit;
    BSPP_sBitStrSeg  * psBitStrSeg;

    VDEC_MALLOC(psStrUnit);
    IMG_ASSERT(psStrUnit != IMG_NULL);
    if (psStrUnit == IMG_NULL)
    {
        return IMG_ERROR_OUT_OF_MEMORY;
    }
    VDEC_BZERO(psStrUnit);
    if (sBSList != IMG_NULL)
    {
        // copy BS list to this list
        LST_init(&psStrUnit->sBitStrSegList);
        for ( psBitStrSeg = LST_first( sBSList); psBitStrSeg != NULL;   psBitStrSeg = LST_first(sBSList) )
        {
            psBitStrSeg = LST_removeHead(sBSList);
            LST_add(&psStrUnit->sBitStrSegList,psBitStrSeg);
        }
    }

    *ppsStrUnit = psStrUnit;

    return IMG_SUCCESS;
}
/*!
******************************************************************************

 @Function              VDECDDUTILS_CreateStrUnitOld

 @Description           this function allocate a structure for a complete data unit


******************************************************************************/
IMG_RESULT VDECDDUTILS_CreateStrUnitOld(
    VDECDD_sStrUnit    ** ppsStrUnit,
    BSPP_sSequHdrInfo   * psSeqInfo,
    BSPP_sPictHdrInfo   * psPicInfo,
    LST_T               * sBSList
)
{
    VDECDD_sStrUnit    * psStrUnit;
    BSPP_sBitStrSeg  * psBitStrSeg;

    VDEC_MALLOC(psStrUnit);
    IMG_ASSERT(psStrUnit != IMG_NULL);
    if (psStrUnit == IMG_NULL)
    {
        return IMG_ERROR_OUT_OF_MEMORY;
    }
    VDEC_BZERO(psStrUnit);
    if (sBSList != IMG_NULL)
    {
        // copy BS list to this list
        LST_init(&psStrUnit->sBitStrSegList);
        for ( psBitStrSeg = LST_first( sBSList); psBitStrSeg != NULL;   psBitStrSeg = LST_first(sBSList) )
        {
            psBitStrSeg = LST_removeHead(sBSList);
            LST_add(&psStrUnit->sBitStrSegList,psBitStrSeg);
        }

    }
    if(psSeqInfo !=IMG_NULL)
    {
        psStrUnit->psSequHdrInfo = psSeqInfo;
        psStrUnit->psSequHdrInfo->ui32RefCount = 1;
    }
    psStrUnit->psPictHdrInfo = psPicInfo;
    *ppsStrUnit = psStrUnit;

    return IMG_SUCCESS;
}
/*!
******************************************************************************

 @Function              RESOURCE_ListAdd

******************************************************************************/
IMG_RESULT
RESOURCE_ListAdd(
    LST_T *       psList,
    IMG_VOID *    pvItem,
    IMG_UINT32    ui32Id,
    IMG_UINT32 *  pui32RefCount
)
{
    RESOURCE_sListElem *  psListElem = IMG_NULL;
    IMG_BOOL bFound = IMG_FALSE;
    IMG_UINT32  ui32Result;

    /* Check input params. */
    IMG_ASSERT(IMG_NULL != psList);
    IMG_ASSERT(IMG_NULL != pvItem);

    if (psList == IMG_NULL ||
        pvItem == IMG_NULL)
    {
        ui32Result = IMG_ERROR_INVALID_PARAMETERS;
        goto error;
    }

    /* Decrement the reference count on the item
       to signal that the owner has relinquished it. */
    ui32Result = RESOURCE_ItemReturn(pui32RefCount);
    if (ui32Result != IMG_SUCCESS)
    {
        ui32Result = IMG_ERROR_UNEXPECTED_STATE;
        goto error;
    }

    /* Determine whether this buffer is already in the list. */
    psListElem = LST_first(psList);
    while (psListElem)
    {
        if (psListElem->pvItem == pvItem)
        {
            bFound = IMG_TRUE;
            break;
        }

        psListElem = LST_next(psListElem);
    }

    if (!bFound)
    {
        /* Allocate the image buffer list element structure. */
        VDEC_MALLOC(psListElem);
        if (IMG_NULL == psListElem)
        {
            REPORT(REPORT_MODULE_RESOURCE, REPORT_ERR,
                "Failed to allocate memory for RESOURCE list element");
            ui32Result = IMG_ERROR_OUT_OF_MEMORY;
            goto error;
        }
        VDEC_BZERO(psListElem);

        /* Setup the list element. */
        psListElem->pvItem = pvItem;
        psListElem->ui32Id = ui32Id;
        psListElem->pui32RefCount = pui32RefCount;

        /* Add the element to the list. */
        LST_add(psList, (IMG_VOID *)psListElem);
    }

    return IMG_SUCCESS;

error:
    return ui32Result;
}
/*!
******************************************************************************

 @Function              RESOURCE_ListReplace

******************************************************************************/
IMG_RESULT
RESOURCE_ListReplace(
    LST_T                 * psList,
    IMG_VOID              * pvItem,
    IMG_UINT32              ui32Id,
    IMG_UINT32            * pui32RefCount,
    RESOURCE_pfnFreeItem    pfnFreeItem,
    IMG_VOID              * pvFreeCbParam
)
{
    RESOURCE_sListElem    * psListElem = IMG_NULL;
    IMG_UINT32              ui32Result;

    /* Check input params. */
    IMG_ASSERT(IMG_NULL != psList);
    IMG_ASSERT(IMG_NULL != pvItem);
    if (psList == IMG_NULL ||
        pvItem == IMG_NULL)
    {
        ui32Result = IMG_ERROR_INVALID_PARAMETERS;
        goto error;
    }

    /* Determine whether this sequence header is already in the list. */
    psListElem = LST_first(psList);
    while (psListElem)
    {
        if (psListElem->ui32Id == ui32Id)
        {
            /* Free old version. */
            RESOURCE_ItemReturn(psListElem->pui32RefCount);
            if (*psListElem->pui32RefCount == 0)
            {
                if (pfnFreeItem)
                {
                    pfnFreeItem(psListElem->pvItem, pvFreeCbParam);
                }
                else
                {
                    IMG_FREE(psListElem->pvItem);
                }
                psListElem->pvItem = IMG_NULL;
            }

            LST_remove(psList, psListElem);
            break;
        }

        psListElem = LST_next(psListElem);
    }

    if (psListElem == IMG_NULL)
    {
        /* Allocate the sequence header list element structure. */
        VDEC_MALLOC(psListElem);
        if (IMG_NULL == psListElem)
        {
            REPORT(REPORT_MODULE_RESOURCE, REPORT_ERR,
                "Failed to allocate memory for RESOURCE list element");
            return IMG_ERROR_OUT_OF_MEMORY;
        }
    }
    VDEC_BZERO(psListElem);

    /* Setup the sequence header list element. */
    RESOURCE_ItemUse(pui32RefCount);

    psListElem->pvItem = pvItem;
    psListElem->ui32Id = ui32Id;
    psListElem->pui32RefCount = pui32RefCount;

    /* Add the sequence header list element to the sequence header list. */
    LST_add(psList, (IMG_VOID *)psListElem);

    return IMG_SUCCESS;

error:
    return ui32Result;
}
/*!
******************************************************************************

 @Function              MMU_DeviceCreate

******************************************************************************/
IMG_RESULT  MMU_DeviceCreate(
    MMU_eMmuType                    eMmuType,
    VDEC_eTileScheme                eTileScheme,
    VDECDDMMU_pfnDeviceCallback     pfnDeviceCallback,
    MSVDXIO_sMemPool                sMemPool,
    IMG_VOID                      * pvCallbackParameter,
    IMG_UINT32                      ui32PtdAlignment,
    IMG_HANDLE                    * phMmuDevHandle
)
{
    IMG_RESULT          ui32Result = IMG_SUCCESS;
    IMG_BOOL            bTiling = IMG_TRUE;
    TALMMU_eMMUType     eTalMmuType = TALMMU_MMUTYPE_4K_PAGES_32BIT_ADDR;
    IMG_UINT32          i;
    MMU_sDevContext *   psDevContext;
    TALMMU_sDevMemInfo  sDevMemInfo;

    // Set the TAL MMU type.
    switch (eMmuType)
    {
    case MMU_TYPE_32BIT:
        eTalMmuType = TALMMU_MMUTYPE_4K_PAGES_32BIT_ADDR;
        break;

    case MMU_TYPE_36BIT:
        eTalMmuType = TALMMU_MMUTYPE_4K_PAGES_36BIT_ADDR;
        break;

    case MMU_TYPE_40BIT:
        eTalMmuType = TALMMU_MMUTYPE_4K_PAGES_40BIT_ADDR;
        break;

    default:
        return IMG_ERROR_INVALID_PARAMETERS;
    }

    /* Allocate a device context structure...*/
    VDEC_MALLOC(psDevContext);
    IMG_ASSERT(psDevContext != IMG_NULL);
    if (psDevContext == IMG_NULL)
    {
        REPORT(REPORT_MODULE_MMU, REPORT_ERR,
               "Failed to allocate memory for MMU device context");
        return IMG_ERROR_OUT_OF_MEMORY;
    }
    VDEC_BZERO(psDevContext);

    /* Initialise stream list. */
    LST_init(&psDevContext->sStrList);

    psDevContext->pfnDeviceCallback = pfnDeviceCallback;
    psDevContext->pvCallbackParameter = pvCallbackParameter;

    /* Initialise TALMMU. */
    ui32Result = TALMMU_Initialise();
    IMG_ASSERT(ui32Result == IMG_SUCCESS);
    if (ui32Result != IMG_SUCCESS)
    {
        goto error_tal_init;
    }

    /* Create an MMU template */
    sDevMemInfo.pszDeviceName = "VDEC";
    sDevMemInfo.ui32DeviceId = 0;
    sDevMemInfo.eMMUType = eTalMmuType;
    sDevMemInfo.eDevFlags = TALMMU_DEVFLAGS_NONE;
    sDevMemInfo.pszPageDirMemSpaceName = "MEM";
    sDevMemInfo.pszPageTableMemSpaceName = IMG_NULL;
    sDevMemInfo.ui32PageSize = DEV_MMU_PAGE_SIZE;
    sDevMemInfo.ui32PageTableDirAlignment = ui32PtdAlignment;
    sDevMemInfo.eMemAttrib = (SYS_MEMATTRIB_UNCACHED | SYS_MEMATTRIB_WRITECOMBINE);
    sDevMemInfo.eMemPool = sMemPool.eMemPoolId;
    sDevMemInfo.eTilingScheme = (eTileScheme == VDEC_TS1_512x8) ? TALMMU_MMUTILING_SCHEME_1 : TALMMU_MMUTILING_SCHEME_0;

    ui32Result = TALMMU_DevMemTemplateCreate(&sDevMemInfo, &psDevContext->hDevMemTemplate);
    IMG_ASSERT(ui32Result == IMG_SUCCESS);
    if (ui32Result != IMG_SUCCESS)
    {
        goto error_tal_template;
    }

    TALMMU_AddCallback(psDevContext->hDevMemTemplate,
                       mmu_Callback,
                       (IMG_PVOID)psDevContext);

    /* Add heaps to template */
    for (i = 0; i < MMU_HEAP_MAX; i++)
    {
        sHeapInfo.ui32HeapId = asMmuHeaps[i].eHeapId;
        sHeapInfo.eHeapType = asMmuHeaps[i].eHeapType;
        sHeapInfo.pszMemSpaceName = asMmuHeaps[i].pszMemSpace;
        sHeapInfo.bTiled = (bTiling && asMmuHeaps[i].ui32TileStride) ? IMG_TRUE : IMG_FALSE;
        sHeapInfo.ui32BaseDevVirtAddr = asMmuHeaps[i].ui32StartOffset;
        sHeapInfo.ui32Size = asMmuHeaps[i].ui32Size;
        sHeapInfo.ui32XTileStride = 0;

        if (asMmuHeaps[i].ui32TileStride)
        {
            IMG_UINT32  ui32HwTileStride;
            IMG_UINT32  ui32Log2HwTileStride = 0;

            ui32HwTileStride = asMmuHeaps[i].ui32TileStride;
            // Calculate HW tile stride.
            // HW tile stride = log2((tile_stride / tile_width)/ 2)
            ui32HwTileStride >>= 1;
            switch (eTileScheme)
            {
            case VDEC_TS0_256x16:
                ui32HwTileStride >>= 8;
                break;
            case VDEC_TS1_512x8:
                ui32HwTileStride >>= 9;
                break;
            default:
                IMG_ASSERT(0);
                break;
            }

            ui32HwTileStride >>= 1;
            while (ui32HwTileStride)
            {
                ui32HwTileStride >>= 1;
                ui32Log2HwTileStride++;
            }
            // Calculated hardware coded stride value.
            ui32HwTileStride = ui32Log2HwTileStride;

            sHeapInfo.ui32XTileStride = ui32HwTileStride;
        }

        ui32Result = TALMMU_DevMemHeapAdd(psDevContext->hDevMemTemplate, &sHeapInfo);
        IMG_ASSERT(ui32Result == IMG_SUCCESS);
        if (ui32Result != IMG_SUCCESS)
        {
            goto error_tal_heap;
        }
    }