/******************************************************************************
 *
 * Copyright 2007, Silicon Image, Inc.  All rights reserved.
 * No part of this work may be reproduced, modified, distributed, transmitted,
 * transcribed, or translated into any language or computer format, in any form
 * or by any means without written permission of: Silicon Image, Inc., 1060
 * East Arques Avenue, Sunnyvale, California 94085
 *
 *****************************************************************************/
/**
 * @file media_buffer_pool.c
 *
 * @brief
 *          Buffer Pool implementation
 *
 * 
 *
 *   Principal Author: Joerg Detert
 *   Creation date:    Feb 28, 2008
 *
 * 
 *
 *****************************************************************************/
#include 
#include "media_buffer_pool.h"
#define MEDIA_BUF_ALIGN(buf, align) (((buf)+((align)-1U)) & ~((align)-1U))
/******************************************************************************
 * MediaBufPoolGetSize
 *****************************************************************************/
RESULT MediaBufPoolGetSize(MediaBufPoolConfig_t* pPoolConfig)
{
    RESULT  ret = RET_SUCCESS;
    if(pPoolConfig == NULL)
    {
        return RET_WRONG_HANDLE;
    }
    if(pPoolConfig->bufNum > pPoolConfig->maxBufNum)
    {
        return RET_WRONG_CONFIG;
    }
    /* size of extra metadata */
    pPoolConfig->metaDataMemSize = pPoolConfig->maxBufNum * pPoolConfig->metaDataSizeMediaBuf;
    /* size of media buffer array */
    pPoolConfig->metaDataMemSize += (uint32_t)(pPoolConfig->maxBufNum * sizeof(MediaBuffer_t));
    /* calculate size of buffer array*/
    if(pPoolConfig->flags & (uint32_t)BUFPOOL_RINGBUFFER)
    {
        /* results in no gaps between buffers but an offset depending on the alignment */
        pPoolConfig->bufMemSize = (pPoolConfig->maxBufNum * pPoolConfig->bufSize) +
                                   pPoolConfig->bufAlign;
        /* the buffer size must be correct to the alignment otherwise we would have
         * unaligned buffers after the first which is always aligned */
        if((pPoolConfig->bufSize & ((uint32_t)pPoolConfig->bufAlign - 1U)) != 0U)
        {
            return RET_WRONG_CONFIG;
        }
    }
    else
    {
        /* this leads to gaps between the buffers dependend on the alignment */
        pPoolConfig->bufMemSize = pPoolConfig->maxBufNum * (pPoolConfig->bufSize +
                                  pPoolConfig->bufAlign);
    }
    return ret;
}
/******************************************************************************
 * MediaBufPoolCreate
 *****************************************************************************/
RESULT MediaBufPoolCreate(MediaBufPool_t*       pBufPool,
                          MediaBufPoolConfig_t* pPoolConfig,
                          MediaBufPoolMemory_t  poolMemory)
{
    uint32_t        i;
    uint32_t        offset;
    void*           pBuf;
    RESULT  ret = RET_SUCCESS;
    if(pBufPool == NULL)
    {
        return RET_WRONG_HANDLE;
    }
    if((poolMemory.pMetaDataMemory  == NULL) ||
       (poolMemory.pBufferMemory    == NULL))
    {
        return RET_INVALID_PARM;
    }
    if((pPoolConfig->bufNum == 0U) || (pPoolConfig->bufSize == 0U) ||
       (pPoolConfig->maxBufNum < pPoolConfig->bufNum))
    {
        return RET_WRONG_CONFIG;
    }
    /* If buffer pool is switched to ringbuffer mode, there must not be any gap*/
    /* between individual buffers inside static buffer pool, introduced by data*/
    /* alignement issues (e.g. rouding up of internal buffer sizes).*/
    /* That is, the buffer size must be a multiple of the data alignement.*/
    if((pPoolConfig->flags & BUFPOOL_RINGBUFFER) &&
       (pPoolConfig->bufSize % pPoolConfig->bufAlign))
    {
        return RET_WRONG_CONFIG;
    }
    /* initialize buffer pool object*/
    (void) memset(pBufPool, 0, sizeof(MediaBufPool_t));
    pBufPool->bufSize              = pPoolConfig->bufSize;
    pBufPool->metaDataSizeMediaBuf = pPoolConfig->metaDataSizeMediaBuf;
    pBufPool->bufNum               = pPoolConfig->bufNum;
    pBufPool->freeBufNum           = pPoolConfig->bufNum;
    pBufPool->maxBufNum            = pPoolConfig->maxBufNum;
    pBufPool->poolSize             = pPoolConfig->bufNum * pPoolConfig->bufSize;
    pBufPool->flags                = pPoolConfig->flags;
    /* The memory given by the caller is assigned to the buffer pool */
    pBufPool->pBaseAddress = poolMemory.pMetaDataMemory;
    /* Make sure that sizes in pPoolConfig are written */
    (void) MediaBufPoolGetSize(pPoolConfig);
    /* initialize buffer memory to zero */
    (void) memset(poolMemory.pMetaDataMemory, 0, (size_t) pPoolConfig->metaDataMemSize);
    /* We use the first partition of memory for the media buffer array */
    pBufPool->pBufArray = (MediaBuffer_t*) pBufPool->pBaseAddress;
    /* The second partition of memory is used for the buffer meta data array for Media Buffers */
    offset = (pPoolConfig->maxBufNum * sizeof(MediaBuffer_t));
    pBufPool->pMetaDataMediaBufBase = (void*) (((ulong_t) pBufPool->pBaseAddress) + offset);
    /* The memory pointed to by pBufferMemory is used for
     * the real buffer memory. This memory is assigned to the pointers stored in the corresponding
     * MediaBuffer_t[]. Due to the alignment restrictions given by the user it must be kept in mind
     * that the alignment is added. */
    pBuf = poolMemory.pBufferMemory;
    for(i = 0U; i < pBufPool->maxBufNum; i++)
    {
        /* set base address of buffer
           (will be changed in case that buffer pool is resized) */
        pBufPool->pBufArray[i].pBaseAddress = (uint8_t*) MEDIA_BUF_ALIGN((ulong_t)pBuf +
                        (i * pPoolConfig->bufSize), (uint32_t)pPoolConfig->bufAlign);
        /* set base size of buffer
           (will be changed in case that buffer pool is resized) */
        pBufPool->pBufArray[i].baseSize = pBufPool->bufSize;
        if ( pPoolConfig->metaDataSizeMediaBuf != 0 )
        {
            /* set Media Buffer meta data structure address for
               (stays the same during lifetime of buffer pool) */
            pBufPool->pBufArray[i].pMetaData = (void *)((ulong_t) pBufPool->pMetaDataMediaBufBase +
                                               (i * pPoolConfig->metaDataSizeMediaBuf));
        }
        else
        {
            pBufPool->pBufArray[i].pMetaData = NULL;
        }
        MediaBufInit(&pBufPool->pBufArray[i]);
    }
    AtomicMutexInit();
    return ret;
}
/******************************************************************************
 * MediaBufPoolDestroy
 *****************************************************************************/
RESULT MediaBufPoolDestroy(MediaBufPool_t *pBufPool)
{
    DCT_ASSERT(pBufPool != NULL);
    (void) memset(pBufPool, 0, sizeof(MediaBufPool_t));
    AtomicMutexDestory();
    return RET_SUCCESS;
}
/******************************************************************************
 * MediaBufPoolReset
 *****************************************************************************/
RESULT MediaBufPoolReset(MediaBufPool_t* pBufPool)
{
    uint32_t i;
    DCT_ASSERT(pBufPool != NULL);
    /* reset state variables */
    pBufPool->freeBufNum = pBufPool->bufNum;
    pBufPool->fillLevel  = 0;
    pBufPool->index      = 0;
    /* re-init media buffers */
    for(i = 0U; i < pBufPool->maxBufNum; i++)
    {
        MediaBufInit(&pBufPool->pBufArray[i]);
    }
    return RET_SUCCESS;
}
/******************************************************************************
 * MediaBufPoolRegisterCb
 *****************************************************************************/
RESULT MediaBufPoolRegisterCb(MediaBufPool_t*         pBufPool,
                              MediaBufPoolCbNotify_t  fpCallback,
                              void*                   pUserContext)
{
    RESULT ret = RET_NOTAVAILABLE;
    int32_t i;
    if(pBufPool == NULL)
    {
        return RET_WRONG_HANDLE;
    }
    if(fpCallback == NULL)
    {
        return RET_INVALID_PARM;
    }
    if(pBufPool->notify.fpCallback == NULL)
    {
        pBufPool->notify.fpCallback    = fpCallback;
        pBufPool->notify.pUserContext  = pUserContext;
        ret = RET_SUCCESS;
    }
    return ret;
}
/******************************************************************************
 * MediaBufPoolDeregisterCb
 *****************************************************************************/
RESULT MediaBufPoolDeregisterCb(MediaBufPool_t*         pBufPool,
                                MediaBufPoolCbNotify_t  fpCallback)
{
    RESULT ret = RET_NOTAVAILABLE;
    int32_t i;
    if(pBufPool == NULL)
    {
        return RET_WRONG_HANDLE;
    }
    if(fpCallback == NULL)
    {
        return RET_INVALID_PARM;
    }
    if(pBufPool->notify.fpCallback == fpCallback)
    {
        pBufPool->notify.fpCallback   = NULL;
        pBufPool->notify.pUserContext = NULL;
        ret = RET_SUCCESS;
    }
    return ret;
}
/******************************************************************************
 * MediaBufPoolGetBuffer
 *****************************************************************************/
MediaBuffer_t* MediaBufPoolGetBuffer(MediaBufPool_t* pBufPool)
{
    MediaBuffer_t *pMediaBuffer;
    DCT_ASSERT(pBufPool != NULL);
    for(;;)
    {
        /* if the requested number of buffers is greater than the maximum then wait*/
        while( pBufPool->freeBufNum == 0U )
        {
            return  NULL;
        }
        /* at least one buffer is free*/
        for(;;)
        {
            if(!pBufPool->pBufArray[pBufPool->index].lockCount)
            {
                /* adjust the resources count*/
                pBufPool->freeBufNum--;
                pBufPool->pBufArray[pBufPool->index].lockCount = 1;
                pBufPool->pBufArray[pBufPool->index].pOwner = pBufPool;
                pMediaBuffer = &pBufPool->pBufArray[pBufPool->index];
                if(++(pBufPool->index) >= pBufPool->bufNum)
                {
                    pBufPool->index = 0U;
                }
                return pMediaBuffer;
            }
            else
            {
                /* pBufPool->uiIndex points to the next buffer to be checked for a get empty request.*/
                /* If that buffer is locked, no media buffer is returned, because random access is*/
                /* not allowed in ringbuffer mode.*/
                if(pBufPool->flags & BUFPOOL_RINGBUFFER)
                {
                    return  NULL;
                }
            }
            if(++(pBufPool->index) >= pBufPool->bufNum)
            {
                pBufPool->index = 0U;
            }
        }
    } /* for( ;;)*/
}
/******************************************************************************
 * MediaBufPoolFreeBuffer
 *****************************************************************************/
void MediaBufPoolFreeBuffer(MediaBufPool_t* pBufPool, MediaBuffer_t *pBuf)
{
    int32_t i;
    DCT_ASSERT(pBufPool != NULL);
    DCT_ASSERT(pBuf != NULL);
    pBuf->lockCount = 0;
    pBufPool->freeBufNum++;
    if(pBufPool->notify.fpCallback != NULL)
    {
        (pBufPool->notify.fpCallback)(pBufPool->notify.pUserContext, pBuf);
    }
}