/******************************************************************************  
 | 
 *  
 | 
 * 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  
 | 
 *  
 | 
 * <pre>  
 | 
 *  
 | 
 *   Principal Author: Joerg Detert  
 | 
 *   Creation date:    Feb 28, 2008  
 | 
 *  
 | 
 * </pre>  
 | 
 *  
 | 
 *****************************************************************************/  
 | 
  
 | 
#include <string.h>  
 | 
  
 | 
#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);  
 | 
    }  
 | 
}  
 | 
  
 |