/*
* Copyright (c) 2017, Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
//!
//! \file     heap.cpp
//! \brief    Implements functionalities pertaining to heaps.
//!

#include "heap.h"

Heap::Heap()
{
    HEAP_FUNCTION_ENTER;
    m_id = m_invalidId;
}

Heap::Heap(uint32_t id)
{
    HEAP_FUNCTION_ENTER;
    m_id = id;
}

Heap::~Heap()
{
    HEAP_FUNCTION_ENTER;
    if (m_osInterface != nullptr)
    {
        if (m_lockedHeap != nullptr)
        {
            m_osInterface->pfnUnlockResource(m_osInterface, m_resource);
        }
        if (!Mos_ResourceIsNull(m_resource))
        {
            m_osInterface->pfnFreeResource(m_osInterface, m_resource);
            MOS_FreeMemory(m_resource);
        }
    }
}

MOS_STATUS Heap::RegisterOsInterface(PMOS_INTERFACE osInterface)
{
    HEAP_FUNCTION_ENTER;
    HEAP_CHK_NULL(osInterface);
    m_osInterface = osInterface;
    return MOS_STATUS_SUCCESS;
}

MOS_STATUS Heap::Allocate(uint32_t heapSize, bool keepLocked)
{
    HEAP_FUNCTION_ENTER;

    if (heapSize == 0)
    {
        HEAP_ASSERTMESSAGE("No size requested for heap allocation!");
        return MOS_STATUS_INVALID_PARAMETER;
    }

    if (m_resource != nullptr)
    {
        HEAP_ASSERTMESSAGE("A heap has already been allocated!");
        return MOS_STATUS_INVALID_PARAMETER;
    }

    m_resource = (PMOS_RESOURCE)MOS_AllocAndZeroMemory(sizeof(MOS_RESOURCE));
    HEAP_CHK_NULL(m_resource);
    HEAP_CHK_NULL(m_osInterface);

    MOS_ALLOC_GFXRES_PARAMS allocParams;
    memset(&allocParams, 0, sizeof(allocParams));
    allocParams.Type = MOS_GFXRES_BUFFER;
    allocParams.TileType = MOS_TILE_LINEAR;
    allocParams.Format = Format_Buffer;
    allocParams.dwBytes = heapSize;
    allocParams.pBufName = "Heap";

    if (m_hwWriteOnly && !keepLocked)
    {
        allocParams.Flags.bNotLockable = true;
        allocParams.dwMemType = MOS_MEMPOOL_DEVICEMEMORY;
    }

    HEAP_CHK_STATUS(m_osInterface->pfnAllocateResource(
        m_osInterface,
        &allocParams,
        m_resource));

    // explicit set to skip resource sync, heap resource should be used block by block
    // driver should ensure non concurrent access the same block
    HEAP_CHK_STATUS(m_osInterface->pfnSkipResourceSync(m_resource));

    if (keepLocked)
    {
        m_lockedHeap = Lock();
        HEAP_CHK_NULL(m_lockedHeap);
        m_keepLocked = keepLocked;
    }

    m_size = heapSize;
    m_freeSpace = m_size;

    return MOS_STATUS_SUCCESS;
}

uint8_t* Heap::Lock()
{
    HEAP_FUNCTION_ENTER_VERBOSE;
    if (m_keepLocked)
    {
        return m_lockedHeap;
    }

    if (m_osInterface == nullptr)
    {
        HEAP_ASSERTMESSAGE("Invalid m_osInterface(nullptr)");
        return nullptr;
    }

    MOS_LOCK_PARAMS lockParams;
    memset(&lockParams, 0, sizeof(lockParams));
    lockParams.WriteOnly = 1;
    lockParams.NoOverWrite = 1;
    lockParams.Uncached = 1;
    uint8_t* pLockedResource =
        (uint8_t*)m_osInterface->pfnLockResource(m_osInterface, m_resource, &lockParams);
    return pLockedResource;
}

MOS_STATUS Heap::Dump()
{
    return MOS_STATUS_UNIMPLEMENTED;
}

MOS_STATUS Heap::AdjustFreeSpace(uint32_t addedSpace)
{
    HEAP_FUNCTION_ENTER_VERBOSE;

    if (addedSpace + m_freeSpace > m_size ||
        m_usedSpace < addedSpace)
    {
        HEAP_ASSERTMESSAGE("Provided space will not fit in the heap");
        return MOS_STATUS_INVALID_PARAMETER;
    }
    m_freeSpace += addedSpace;
    m_usedSpace -= addedSpace;

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS Heap::AdjustUsedSpace(uint32_t addedSpace)
{
    HEAP_FUNCTION_ENTER_VERBOSE;

    if (addedSpace + m_usedSpace > m_size ||
        m_freeSpace < addedSpace)
    {
        HEAP_ASSERTMESSAGE("Provided space will not fit in the heap");
        return MOS_STATUS_INVALID_PARAMETER;
    }

    if (m_freeInProgress)
    {
        HEAP_ASSERTMESSAGE("Heap is in the process of being freed, cannot add space as used");
        return MOS_STATUS_INVALID_PARAMETER;
    }

    m_freeSpace -= addedSpace;
    m_usedSpace += addedSpace;

    return MOS_STATUS_SUCCESS;
}
