/*
 * Copyright 2010 Trident Microsystems (Far East) Ltd. All rights reserved.
 * All prior copyright rights in this work and the accompanying software
 * products transferred to Trident Microsystems (Far East) Ltd. by written
 * agreement.
 */
/*****************************************************************************/
/* Copyright (c) 2010 NXP B.V.  All rights are reserved.                     */
/*                                                                           */
/* Redistribution and use in source and binary forms, with or without        */
/* modification, are permitted provided that the following conditions        */
/* are met:                                                                  */
/*                                                                           */
/* Redistributions of source code must retain the above copyright            */
/* notice, this list of conditions and the following disclaimer.             */
/*                                                                           */
/* Redistributions in binary form must reproduce the above copyright         */
/* notice, this list of conditions and the following disclaimer in the       */
/* documentation and/or other materials provided with the distribution.      */
/*                                                                           */
/* Neither the name of NXP nor the names of its                              */
/* contributors may be used to endorse or promote products derived from      */
/* this software without specific prior written permission.                  */
/*                                                                           */
/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS       */
/* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT         */
/* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR     */
/* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT      */
/* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,     */
/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT          */
/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,     */
/* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY     */
/* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT       */
/* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE         */
/* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH          */
/* DAMAGE.                                                                   */
/*                                                                           */
/*****************************************************************************/
/**
    @file   phStbKBufMgr.c
    @brief  Kernel Buffer Manager component

    Implementation of the top level API for the Kernel Buffer Manager.

    Set your editor for 4 space indentation.
*//*

Rev Date        Author      Comments
--------------------------------------------------------------------------------
  1 20061129    burningh    Original
  2 20061129    burningh    Add further implementation
  3 20061205    steel       Add static allocation for hw buffers
  4 20070213    burningh    Add error detection for static allocation
  5 20070214    burningh    Add KSEG1 to addresses so they're directly useable
5.1.1 20070608  iwillis     Put 2 SD framestores into 1 2048 stride framestore
                            for SD memory saving.
  6 20071008    batelaan    Portability enhancements
  7 20071011    batelaan    Remove non-static feature.
  8 20071127    iwillis     Display message if more than 1 MiB free space
                            remaining after initialisation
  9 20080124    burningh    Determine how much RAM we're using for alignment
 10 20080131    burningh    Change wording of alignment prints
 11 20080227    hutchinson  merge in 20080227 EB snapshot
 12 20080314    myers       Set up the AVS framestore pointers
 13 20080415    burningh    Fix GetBuffer() and ReleaseBuffer() buglet
 14 20080915    batelaan    Major overhaul: build time memory buffer configuration.
 15 20080916    batelaan    Don't issue warning if GetBuffer fails.
 16 20081104    batelaan    Add phStbKBufMgr_LookupBuffer and phStbKBufMgr_BufferStats
 17 20090120    batelaan    Update file copyright/license text
 18 20090402    batelaan    Add phStbKBufMgr_PrintSummaryTo
--------------------------------------------------------------------------------
    For consistency and standardisation retain the Section Separators.
*/

/**************************************************************
* INCLUDE FILES                                               *
***************************************************************/

#include <tmNxCompId.h>
#include "phStbKBufMgr_Int.h"
#include <tmNxTypes.h>
#include <phStbDbg.h>

#include <string.h>
#include <prjconfig.h>

/**************************************************************
* LOCAL MACROS                                                *
***************************************************************/

/**************************************************************
* LOCAL TYPEDEFS                                              *
***************************************************************/

/**************************************************************
* STATIC FUNCTION PROTOTYPES                                  *
***************************************************************/

/**************************************************************
* STATIC DATA                                                 *
***************************************************************/

/**************************************************************
* EXPORTED DATA                                               *
***************************************************************/

/** Debug unit for this component. */
DBG_UNIT_STATIC(gStbKBufMgr_DbgUnit);

/**************************************************************
* FUNCTION IMPLEMENTATION                                     *
***************************************************************/

tmErrorCode_t phStbKBufMgr_Init(void)
{
	uint32_t buf;
	tmErrorCode_t status = TM_OK;

	DBG_REGISTER_MODULE(gStbKBufMgr_DbgUnit, "phStbKBufMgr");

	DBG_PRINT0(gStbKBufMgr_DbgUnit, DBG_INTERFACE_LEAVE,
			   "phStbKBufMgr_Init");

	// When playing back interlaced content, some filter or other goes off the end of the video frame 
	// ends up with uninitialized video being filtered into the last line of the video output.
	// Ideally we'd blank all the frame buffers each time we use them but that's really costly, so just
	// do it when intializing and do 'targetted' blanking each time we use them i.e. only blank the line 
	// after  the last line of PAL/NTSC.
    for(buf = 0; buf < phStbKBufMgr_Buffer_Number; ++buf)
    {
        phStbKBufMgr_RtBufferData_t *pBufferData = &gphStbKBufMgr_RtBuffers[buf];
        uint32_t index;
        uint32_t numberRequired = pBufferData->numberRequired;

        for (index = 0; index < numberRequired; index++)
        {
            phStbKBufMgr_RtBlock_t *pBlock = &pBufferData->blocks[index];

			if((buf == phStbKBufMgr_Buffer_FSY) || 
			   (buf == phStbKBufMgr_Buffer_FSY_Stop))
			{
				// Initialize frame store Y buffers to black
				memset(pBlock->addr, 0, pBufferData->size);
			}
			else if((buf == phStbKBufMgr_Buffer_FSUV) ||
					(buf == phStbKBufMgr_Buffer_FSUV_Stop))
			{
				// Initialize frame store UV buffers to black
				memset(pBlock->addr, 0x80, pBufferData->size);
			}
		}
	}

    return status;
}

/*****************************************************************************/
tmErrorCode_t phStbKBufMgr_GetBuffer(
    phStbKBufMgr_Buffer_t buf,
    uint32_t *pBufAddr,
    uint32_t *pSize)
{
    tmErrorCode_t status;

    if ((buf >= 0) && (buf < phStbKBufMgr_Buffer_Number))
    {
        phStbKBufMgr_RtBufferData_t *pBufferData = &gphStbKBufMgr_RtBuffers[buf];
        uint32_t index;
        uint32_t numberRequired = pBufferData->numberRequired;

        for (index = 0; index < numberRequired; index++)
        {
            phStbKBufMgr_RtBlock_t *pBlock = &pBufferData->blocks[index];
			// The stop buffers are never release, always hand out the same ones even if they're already in use.
            if ((!pBlock->inUse) || (buf == phStbKBufMgr_Buffer_FSY_Stop) || (buf == phStbKBufMgr_Buffer_FSUV_Stop))
            {
                *pBufAddr = pBlock->addr;
                *pSize = pBufferData->size;

                pBlock->inUse = True;
                status = TM_OK;

				// Frame store Y and UV buffers to black for PAL/NTSC last few lines
				// Don't touch the 'stop' buffers as those are currently on screen and what
				// is more shouldn't have the bad line as they are copies of one of the other
				// frame stores.
				if(buf == phStbKBufMgr_Buffer_FSY)
				{
					memset(pBlock->addr + (574 * 2048), 0, 2048*5);
					memset(pBlock->addr + (478 * 2048), 0, 2048*5);
				}
				else if(buf == phStbKBufMgr_Buffer_FSUV)
				{
					memset(pBlock->addr + (574 * 1024), 0x80, 1024*5);
					memset(pBlock->addr + (478 * 1024), 0x80, 1024*5);
				}

             	DBG_PRINT4(gStbKBufMgr_DbgUnit, DBG_INTERFACE_LEAVE,
                           "phStbKBufMgr_GetBuffer: type %d-%s, *pBufAddr %x, *pSize %x\n",
                           buf, pBufferData->name, *pBufAddr, *pSize);
                return status;
            }
        }
        DBG_PRINT2(gStbKBufMgr_DbgUnit, DBG_LEVEL_1,
                   "phStbKBufMgr_GetBuffer: no buffers left: type %d-%s",
                   (int)buf, pBufferData->name);
        status = PH_STBKBUFMGR_ERR_BUFFER_NOT_AVAILABLE;
    }
    else
    {
        DBG_PRINT1(gStbKBufMgr_DbgUnit, DBG_WARNING,
                   "phStbKBufMgr_GetBuffer: Illegal buf type %d",
                   (int)buf);
        status = PH_STBKBUFMGR_ERR_BAD_BUFFER_TYPE;
    }

    return status;
}

/*****************************************************************************/
tmErrorCode_t phStbKBufMgr_ReleaseBuffer(
    phStbKBufMgr_Buffer_t buf,
    uint32_t bufAddr)
{
    tmErrorCode_t status;

    if ((buf >= 0) && (buf < phStbKBufMgr_Buffer_Number))
    {
        phStbKBufMgr_RtBufferData_t *pBufferData = &gphStbKBufMgr_RtBuffers[buf];
        uint32_t index;
        uint32_t numberRequired = pBufferData->numberRequired;
        for (index = 0; index < numberRequired; index++)
        {
            phStbKBufMgr_RtBlock_t *pBlock = &pBufferData->blocks[index];
            if (pBlock->addr == bufAddr)
            {
                pBlock->inUse = False;
                status = TM_OK;
                DBG_PRINT3(gStbKBufMgr_DbgUnit, DBG_INTERFACE_LEAVE,
                           "phStbKBufMgr_ReleaseBuffer: type %d-%s, bufAddr %x\n",
                           buf, pBufferData->name, bufAddr);
                return status;
            }
        }
        status = PH_STBKBUFMGR_ERR_BUFFER_NOT_AVAILABLE;
        DBG_PRINT3(gStbKBufMgr_DbgUnit, DBG_WARNING,
                   "phStbKBufMgr_ReleaseBuffer: type %d-%s, illegal bufAddr %x\n",
                   buf, pBufferData->name, bufAddr);
    }
    else
    {
        DBG_PRINT1(gStbKBufMgr_DbgUnit, DBG_WARNING,
                   "phStbKBufMgr_ReleaseBuffer: Illegal type %d",
                   (int)buf);
        status = PH_STBKBUFMGR_ERR_BAD_BUFFER_TYPE;
    }

    return status;
}

/*****************************************************************************/
tmErrorCode_t phStbKBufMgr_SwapStopBuffer(
	phStbKBufMgr_Buffer_t buf,
	phStbKBufMgr_Buffer_t stop_buf,
	uint32_t bufAddr)
{
    tmErrorCode_t status;

    if ((buf >= 0) && (buf < phStbKBufMgr_Buffer_Number))
    {
		// Check that we're not just using the old stop buffer first
		if(gphStbKBufMgr_RtBuffers[stop_buf].blocks[0].addr == bufAddr)
		{
			// nothing more to do, no stopping has occurred
			return TM_OK;
		}	

        phStbKBufMgr_RtBufferData_t *pBufferData = &gphStbKBufMgr_RtBuffers[buf];
        uint32_t index;
        uint32_t numberRequired = pBufferData->numberRequired;
        for (index = 0; index < numberRequired; index++)
        {
            phStbKBufMgr_RtBlock_t *pBlock = &pBufferData->blocks[index];
            if (pBlock->addr == bufAddr)
            {
				void *tmp;
				
				// Get the matching stop buffer
		        phStbKBufMgr_RtBufferData_t *pStopBufferData = &gphStbKBufMgr_RtBuffers[stop_buf];
		        
		        // Do the swap
		        tmp = pStopBufferData->blocks[0].addr;
		        pStopBufferData->blocks[0].addr = pBlock->addr;
		        pBlock->addr = tmp;

                status = TM_OK;
                return status;
            }
        }
        status = PH_STBKBUFMGR_ERR_BUFFER_NOT_AVAILABLE;
        DBG_PRINT3(gStbKBufMgr_DbgUnit, DBG_WARNING,
                   "phStbKBufMgr_ReleaseBuffer: type %d-%s, illegal bufAddr %x\n",
                   buf, pBufferData->name, bufAddr);
    }
    else
    {
        DBG_PRINT1(gStbKBufMgr_DbgUnit, DBG_WARNING,
                   "phStbKBufMgr_ReleaseBuffer: Illegal type %d",
                   (int)buf);
        status = PH_STBKBUFMGR_ERR_BAD_BUFFER_TYPE;
    }

    return status;
}

/*****************************************************************************/
tmErrorCode_t phStbKBufMgr_LookupBuffer(
    phStbKBufMgr_Buffer_t buf,
    uint32_t bufferIndex,
    uint32_t *pBufAddr,
    uint32_t *pSize)
{
    tmErrorCode_t status;

    if ((buf >= 0) && (buf < phStbKBufMgr_Buffer_Number))
    {
        phStbKBufMgr_RtBufferData_t *pBufferData = &gphStbKBufMgr_RtBuffers[buf];
        uint32_t numberRequired = pBufferData->numberRequired;

        if (bufferIndex >= numberRequired)
        {
            DBG_PRINT2(gStbKBufMgr_DbgUnit, DBG_WARNING,
                       "phStbKBufMgr_LookupBuffer: type %d; bad index %u",
                       (int)buf, bufferIndex);
            status = PH_STBKBUFMGR_ERR_BAD_BUFFER_INDEX;
        }
        else
        {
            phStbKBufMgr_RtBlock_t *pBlock = &pBufferData->blocks[bufferIndex];
            if (pBlock->inUse)
            {
                if (pBufAddr != NULL)
                {
                    *pBufAddr = pBlock->addr;
                }
                if (pSize != NULL)
                {
                    *pSize = pBufferData->size;
                }
                status = TM_OK;
                DBG_PRINT4(gStbKBufMgr_DbgUnit, DBG_INTERFACE_LEAVE,
                           "phStbKBufMgr_LookupBuffer: type %d; index: %u; addr=0x%x size=%u",
                           (int)buf, bufferIndex, pBlock->addr, pBufferData->size);
            }
            else
            {
                DBG_PRINT2(gStbKBufMgr_DbgUnit, DBG_WARNING,
                           "phStbKBufMgr_LookupBuffer: type %d; index: %u; not allocated",
                           (int)buf, bufferIndex);
                status = PH_STBKBUFMGR_ERR_NOT_ALLOCATED;
            }
        }
    }
    else
    {
        DBG_PRINT1(gStbKBufMgr_DbgUnit, DBG_WARNING,
                   "phStbKBufMgr_LookupBuffer: Illegal buf type %d",
                   (int)buf);
        status = PH_STBKBUFMGR_ERR_BAD_BUFFER_TYPE;
    }

    return status;
}

/*****************************************************************************/
tmErrorCode_t phStbKBufMgr_BufferStats(
    phStbKBufMgr_Buffer_t buf,
    uint32_t *pSize,
    uint32_t *pNumTotal,
    uint32_t *pNumFree)
{
    tmErrorCode_t status;

    if ((buf >= 0) && (buf < phStbKBufMgr_Buffer_Number))
    {
        phStbKBufMgr_RtBufferData_t *pBufferData = &gphStbKBufMgr_RtBuffers[buf];
        uint32_t nFree = 0;
        uint32_t index;
        uint32_t numberRequired = pBufferData->numberRequired;

        for (index = 0; index < numberRequired; index++)
        {
            phStbKBufMgr_RtBlock_t *pBlock = &pBufferData->blocks[index];
            if (!pBlock->inUse)
            {
                nFree++;
            }
        }
        if (pSize != NULL)
        {
            *pSize = pBufferData->size;
        }
        if (pNumTotal != NULL)
        {
            *pNumTotal = numberRequired;
        }
        if (pNumFree != NULL)
        {
            *pNumFree = nFree;
        }
        status = TM_OK;
        DBG_PRINT4(gStbKBufMgr_DbgUnit, DBG_INTERFACE_LEAVE,
                   "phStbKBufMgr_BufferStats: type %d; size=%u nTotal=%u nFree=%u",
                   (int)buf, pBufferData->size, numberRequired, nFree);
    }
    else
    {
        DBG_PRINT1(gStbKBufMgr_DbgUnit, DBG_WARNING,
                   "phStbKBufMgr_BufferStats: Illegal buf type %d",
                   (int)buf);
        status = PH_STBKBUFMGR_ERR_BAD_BUFFER_TYPE;
    }

    return status;
}

/*****************************************************************************/
size_t phStbKBufMgr_PrintSummaryTo(char *buf, size_t size)
{
    size_t len;
    phStbKBufMgr_Buffer_t bufIndex;
    len = snprintf(buf, size,
                   "size        alignment   count   free    name\n");

    for (bufIndex = 0; bufIndex < phStbKBufMgr_Buffer_Number; bufIndex++)
    {
        phStbKBufMgr_RtBufferData_t *pBufferData = &gphStbKBufMgr_RtBuffers[bufIndex];
        uint32_t nFree = 0;
        uint32_t index;
        uint32_t numberRequired = pBufferData->numberRequired;

        for (index = 0; index < numberRequired; index++)
        {
            phStbKBufMgr_RtBlock_t *pBlock = &pBufferData->blocks[index];
            if (!pBlock->inUse)
            {
                nFree++;
            }
        }
        len += snprintf(buf + len, size - len,
                        "0x%08x  0x%08x  %u\t%u\t%s\n",
                        pBufferData->size,
                        pBufferData->alignment,
                        pBufferData->numberRequired,
                        nFree,
                        pBufferData->name);

		if((bufIndex == phStbKBufMgr_Buffer_FSY) ||
		   (bufIndex == phStbKBufMgr_Buffer_FSUV) ||
		   (bufIndex == phStbKBufMgr_Buffer_FSY_Stop) ||
		   (bufIndex == phStbKBufMgr_Buffer_FSUV_Stop))
		{
			for (index = 0; index < numberRequired; index++)
			{
				phStbKBufMgr_RtBlock_t *pBlock = &pBufferData->blocks[index];
				len += snprintf(buf + len, size - len, "\t0x%08x\n", pBlock->addr);

			}
		}
    }
    return len;
}
