/*
 * 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_Setup.c
    @brief  Kernel Buffer Manager component

    Setup code for the Kernel Buffer Manager.
    Defines the memory configuration.
    This file is compiled and run at build time to generate
    $BUILDROOT/src/phStbKBufMgr_Setup.c
    which contains the exact memory definition for all the buffer types.

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

Rev Date       Author      Comments
--------------------------------------------------------------------------------
  1 20071008   batelaan    Initial from phStbKBufMgr_Module.c
  2 20071011   batelaan    Remove non-static feature.
  3 20071018   barnish     Remove wmv switches in buffer table
3.1.1 20071121 barnish     remove AIO and add ALIN
  4 20071123   barnish     rename audio buffers
  5 20071127   barnish     use 64k audio bufs instead of 16k
  4 20071127   iwillis     Made buffers more configurable
  6 20071128   barnish     merge
  7 20080108   F.Raiyat    Fix the Audio out buffer alignment to 4k.
  8 20080116   barnish     Provide adequate buffers for ASF demux
  9 20080117   F.Raiyat    Fix the Audio out buffer size
 10 20080121   steel       add extra buffers for pvr recording
 11 20080205   neill       add support for HD to SD downscale
 11.2.1 20080221 neill     increase the size of the downscale buffers
 11.2.2 20080228 hutchinson added EB's 20080227 snapshot changes.
 12 20080310   myers       move msvd fw lower in the map and introduce
                           new entries for AVS framestores
 13 20080414   burningh    Add support for dual decode
 12.1.1 20080416 neill     Limit memory used for HD to SD downscale
 14 20080418   burningh    Merge 13 & 12.1.1
 15 20080627   mowle       Add mpeg2ps diversity
 15 20080509   griffiths   Add extra buffer for auxiliary demuxer
 16 20080619   griffiths   Add extra buffer for alternate directfb allocation
 17 20080702   laird       Adding SWDemux support
 18 20080702   laird       Merge.
 19 20080805   laird       Add a debug buffer region.
 20 20080829   awest       Add extra lin buffer and align them
 21 20080905   ashetty     Increase audio buffer size to support multichannel
 21 20080912   batelaan    Compile and run this file at build time to generate
                           $BUILDROOT/src/phStbKBufMgr_Setup.c
 22 20080915   batelaan    New version for CM purposes
 23 20080916   batelaan    Add check for CONFIG_MULTICHANNEL_AUDIO.
 24 20080916   batelaan    Improve debug output.
 25 20080917   batelaan    Improve debug output even more. Correct vmsp buffer placement.
 26 20080929   batelaan    Change L1_CACHE_BYTES from 32 to the correct 16
26.1.1 20081006 neill      Remove CONFIG_SMALLRAM option
 27 20080930   batelaan    Print out mem available to MIPS1 and MIPS2
 27.1.1 20081014 neill     Add video input buffer
 27.2.1 20081112 laird     Update APTS_BUFSIZE to 64K
 28 20081014   griffiths   Add check for CONFIG_NUM_FRAMESTORES
 29 20081015   neill       Merge
 30 20081020   neill       Add header file output
 31 20081021   neill       Fix invalid pointer access
 32 20081030   burningh    Generalise CONFIG_NUM_FRAMESTORES
 33 20081113   laird       Goodbye MIPS2 PS Demux.
 34 20081113   laird       Merged from 'phStbKBufMgr_Setup.c-33:csrc:SOUsb225#1' and 'phStbKBufMgr_Setup.c-27.2.1:csrc:SOUsb225#1' by laird.
34.1.1 20090120 batelaan   Update file copyright/license text
 35 20081209   amccurdy    Create new priority for MSVD framebuffers to move them out of VMSP window
                           Remove direct reference to a specific customer from 27.2.1 comment
                           Fix DEMUXSW_BUFSIZE following removal of MIPS2 PS Demux
                           Cleanup debug output when VMSP window checks fail.
 36 20090105   amccurdy    Update width and height of SW decoder framestores to support h263flv
 37 20090123   batelaan    Merge v36 from amccurdy
 38 20090217   amccurdy    Update width and height of SW decoder framestores to support VP6
                           Tweak stride for VP6 SW decoder to fix performance regression
 39 20090305   burningh    PiP: Always use 4M MSVD firmware
 38 20090123   myers       Increase allocation for AudioOut linear buffer
 40 20090317   myers       Merge 38 & 39
 41 20090325   burningh    PR 2111: Use AudioES for both PES & ES buffers
#42 20090401   kulkarni    Increase mem for MSVD upscaled image
 42 20090402   batelaan    Add alignment field for runtime info
#43 20090402   frq06751    Merge parallel 42's
 44 20090406   amccurdy    Fix config definition tracking
 44 20090407   neill       Correct the memory allocated to the MSVD firmware for upscaling
 45 20090408   laird       Merged from 'phStbKBufMgr_Setup.c-44:csrc:SOUsb225#1' and 'phStbKBufMgr_Setup.c-ds14#44:csrc:SOUsb225#1' by laird.
 46 20090408   burningh    Added RM support. Merged from 'phStbKBufMgr_Setup.c-45:csrc:SOUsb225#1' and 'phStbKBufMgr_Setup.c-BJ_STB2#40:csrc:SOUsb225#1' by burningh.
 47 20090507   burningh    Use 2M firmware when possible
 48 20090526   amccurdy    Output summary after allocation (when accurate overlay information is available)
 xx 20090604   amccurdy    Allocate 5M for VP6 firmware
 xx 20090909   amccurdy    Merge changes for 720p Real decoder
 xx 20091020   mpage       Make NUM_FILTERS configurable
 xx 20091020   mpage       Make VSPTS buffer size configurable
--------------------------------------------------------------------------------
    For consistency and standardisation retain the Section Separators.
*/

/**************************************************************
* INCLUDE FILES                                               *
***************************************************************/
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <limits.h>
#include <assert.h>

#include "prjconfig.h"
#include "phStbKBufMgr.h"
#include "phStbKBufMgr_Int.h"

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

/* Uncomment for debug output, or set environment variable STBKBUFMGR_DEBUG=1 */
//#define DEBUG

#ifdef DEBUG
#  define IFDEBUG(x) do {x;} while (0)
#  define IFNDEBUG(x) do {} while (0)
#else
#  define IFDEBUG(x) do {} while(0)
#  define IFNDEBUG(x) do {x;} while(0)
#endif

#define MIN(a,b)    (((a) < (b)) ? (a) : (b))
#define MAX(a,b)    (((a) < (b)) ? (b) : (a))

/** Flag indicating buffers of this type should be allocated without gaps
 * between them, and not inside gaps. This means that they will be allocated
 * at the bottom of the freespace at the time. */
#define FL_NOGAPS (1U << 0)

#define L1_CACHE_BYTES 16
#define PAGE_SIZE (4 * 1024)




/* WARNING:
   For every CONFIG_* parameter used to define a #define
   or some static data, a GENERATE_PARAM_VALUE_* macro
   should exist in doSetup(). */



#define PES_BUFSIZE             (   4 * 1024)
#define VSYS_BUFSIZE            ( 192 * 1024)
#define VSECT_BUFSIZE           ( 256 * 1024)
#define VFILT_BUFSIZE           (  64 * 1024)

#if defined(CONFIG_MULTICHANNEL_AUDIO) || defined(CONFIG_ENABLE_AACMC)

#if defined(CONFIG_ENABLE_AUDIO_MIXER)
#define AUDIO_BUFSIZE         ( 4 * 1024 * 8 * 2)
#else
#define AUDIO_BUFSIZE         ( 144 * 1024)
#endif

#elif defined (CONFIG_REAL_ENABLE)
    /*
       Ra8lbr decoder (cook) may output 20k bytes per block, so increase the buffer to 64K
       We also increase other 4 audio related buffers (AuPP input linear, AudioDec output
       linear, etc.) at the same time
    */
#  define AUDIO_BUFSIZE         (  64 * 1024)
#else
#  define AUDIO_BUFSIZE         (  64 * 1024)
#endif

#define DOWNSCALE_BUFSIZE       (1024 * 1024)

/* AuPP max output pkt size=64K (before 'enlarging'). 5 pkts are hoarded by the AudioIO and 2 are needed for the Dsp Client & Server. */
//#define AUDIO_OUT_BUFSIZE       ((5+2) * 64 * 1024)
//for AC3, TMDL_AUDIOIO_INPUT_HOARD_AC3_COUNT and other type of stream TMDL_AUDIOIO_INPUT_HOARD_NON_AC3_COUNT
//Biggest buffer rewuirement is AC3.
//#define AUDIO_OUT_BUFSIZE       (4 * 64 * 1024)//((8+2) * ((256*6*8) + (6*1024)))
#if defined(CONFIG_ENABLE_AUDIO_MIXER)
// With the mixer enabled, we're going for lots of buffering so that when a pin reaches end of stream and times out we have plenty of audio
// waiting that so that we don't glitch
#define AUDIO_OUT_BUFSIZE       ((4+2) * 64 * 1024)
#else
#define AUDIO_OUT_BUFSIZE       (4 * 64 * 1024)//((8+2) * ((256*6*8) + (6*1024)))
#endif

/* TODO: Should not need 128k for the asf demux audio outputs - see PR912 */
#define WM_BUFSIZE              ( 128 * 1024)
#define SSRC_BUFSIZE            (  16 * 1024)
#define VPTS_BUFSIZE            (  64 * 1024)
#define UD_BUFSIZE              (  16 * 1024)
#define AHED_BUFSIZE            (  16 * 1024)   /* audio PES header buffers */
#define APTS_BUFSIZE            (  64 * 1024)   /* audio PTS buffers (made bigger than 2*AAC internal buffer of 8K)
                                                   to solve an issue with very small AAC packets emptying input buffer
                                                   filling internal buffer and yet PTS Q is full.*/

/* This value has to be the largest input buffer size of any SW demux component */
/* Fixme: Better to tag all SW demuxes and determine the largest one automatically... */
/* Fixme: SW demux requirements need checking... we allocate too much memory in many cases... */
#define DEMUXSW_BUFSIZE         (WM_BUFSIZE)
#if defined (CONFIG_REAL_ENABLE)
#  define DEMUXSW_VID_BUFSIZE   ( 512 * 1024)
#else
#  define DEMUXSW_VID_BUFSIZE   (DEMUXSW_BUFSIZE)
#endif

/** NUM_FILTER is used to allocate a pool of 64K VFILT buffers.  2 of these buffers are used for
physical scratch space (PCR queue, other info) and PDMA input. The remaining buffers are allocated
for PES Payload output (e.g. subtitles). */
#define NUM_FILTER              (2 + CONFIG_NUM_HW_DEMUX_PAYLOAD_FILTERS)
#define NUM_SSRC                (4)     /* number of sbm source buffers */

/** The stride of frame stores (number of bytes between the start of 2 consecutive
    display lines) is maximum frame width, plus a decoder dependent amount of
    additional padding, rounded up to a multiple of "granularity".

    HW based MSVD decoders (both HD and SD) are hardcoded to use a stride of 2048.
    SW AVS decoder uses a frame width of 720 and no additional line padding.
    The SW H263 decoder has a frame width of 720 with an additional 64 bytes of
    padding per line (as an optimisation, it decodes directly into the framebuffer
    and so needs some additional workspace). VP6 does the same, but with 96 bytes.

    To improve MSVD2 data cache performance, stride should be an odd multiple of
    STRIDE_GRANULARITY. Set FRAMESTORE_WIDTH_PAD_VP6 to 128 (instead of 96) to
    achieve this. ie

    AVS  : STRIDE_ROUNDUP(720 + 0)   = 736 (ie 23 * 32)
    H263 : STRIDE_ROUNDUP(720 + 64)  = 800 (ie 25 * 32)
    VP6  : STRIDE_ROUNDUP(720 + 128) = 864 (ie 27 * 32)
*/
#define FRAMESTORE_STRIDE           (2048)

#define FRAMESTORE_WIDTH_SW         (720)
#define FRAMESTORE_WIDTH_PAD_AVS    (0)
#define FRAMESTORE_WIDTH_PAD_H263   (64)
#define FRAMESTORE_WIDTH_PAD_VP6    (128)

#define STRIDE_GRANULARITY          (32)
#define STRIDE_ROUNDUP(w)           ((((w) + STRIDE_GRANULARITY - 1) / STRIDE_GRANULARITY) * STRIDE_GRANULARITY)

/** The height of SW frame stores.
    All SW decoders are SD resolution (ie height == 576 lines). However, as an
    optimisation, H263 SW decoder reuses the framebuffer as a decoder working
    buffer and therefore requires an additional 64 lines of padding. VP6 does
    the same, but requires 96 lines.
*/
#define FRAMESTORE_HEIGHT_SW        (576)
#define FRAMESTORE_HEIGHT_PAD_AVS   (0)
#define FRAMESTORE_HEIGHT_PAD_H263  (64)
#define FRAMESTORE_HEIGHT_PAD_VP6   (96)

/** NetDev packet buffer size (bytes) */
#define NETDEV_BUFSIZE              (64 * 1024)

/** The maximum number of a particular buffer supported. */
#define MAX_ALLOWED_BUFFERS         (64)

/** The maximum amount of bytes we allow to be used for buffer alignment purposes,
    before we fail. */
#define MAX_ALIGNMENT_SPACE_ALLOWED (512 * 1024)

/** The top of available RAM.
    @note On 64MB and 128MB boards, this will echo down to the top of physical RAM.
    @note This would NOT work with a 192MB configuration. */
#define TOP_OF_MEMORY               (256 * 1024 * 1024)

/** The mask used to make an address available to the MIPS via KSEG1. */
#define KSEG1_MASK                  (0x80000000)

/** 64MB memory region.
    @see #PRIORITY_VMSP_MIN_PLACEMENT */
#define VMSP_REGION_SIZE            (64u * 1024u * 1024u)

/** Lowest priority of buffers that require 64MB region constraint for VMSP.
    We will check that all buffer types at this priority and higher are within the same
    #VMSP_REGION_SIZE region.
*/
#define PRIORITY_HIGHEST            (1)
#define PRIORITY_VMSP_MIN_PLACEMENT (2)
#define PRIORITY_LOWEST             (UINT_MAX)

#define PH_STBKBUFMGR_ALIGN_UP(addr,alignment) (((addr) + (alignment)) & ~(alignment))

#define TO_KB(x) (((float)(x)) / 1024)
#define TO_MB(x) (((float)(x)) / (1024 * 1024))

#define GENERATE_PARAM_VALUE_CHECK(paramName, oldValue) \
    do {                                                                \
        fprintf(gpOutputFP,                                             \
                "\n/* Check that value of %s, which was %u (0x%x) when this file was generated,\n" \
                "   has not changed... */\n"                            \
                "#if %s != %u\n"                                        \
                "#  error %s from prjconfig.h changed since this file was generated!\n" \
                "#endif\n",                                             \
                paramName,                                              \
                oldValue,                                               \
                oldValue,                                               \
                paramName,                                              \
                oldValue,                                               \
                paramName                                               \
                );                                                      \
        fprintf(gpSummaryFP,                                            \
                "%-40s = %u (0x%x)\n",                                   \
                paramName,                                              \
                oldValue,                                               \
                oldValue                                                \
                );                                                      \
    } while (0)

#define GENERATE_PARAM_IS_DEFINED_CHECK(param)                          \
    do {                                                                \
        fprintf(gpOutputFP,                                             \
                "\n/* Check that %s is defined, which was the case when this file was generated. */\n" \
                "#ifndef %s\n"                                          \
                "#  error %s from prjconfig.h changed since this file was generated!\n" \
                "#endif\n",                                             \
                #param,                                                 \
                #param,                                                 \
                #param                                                  \
                );                                                      \
        GENERATE_PARAM_VALUE_CHECK(#param, param); \
    } while (0)

#define GENERATE_PARAM_IS_UNDEFINED_CHECK(param)                        \
    do {                                                                \
        fprintf(gpOutputFP,                                             \
                "\n/* Check that %s is not defined, which was the case when this file was generated. */\n" \
                "#ifdef %s\n"                                           \
                "#  error %s from prjconfig.h changed since this file was generated!\n" \
                "#endif\n",                                             \
                #param,                                                 \
                #param,                                                 \
                #param                                                  \
                );                                                      \
        fprintf(gpSummaryFP,                                            \
                "%-40s = <undefined>\n",                                \
                #param                                                  \
                );                                                      \
    } while (0)

/* Used to indicate that we need to print something inside a C comment. */
#define INSIDE_C_COMMENT true

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

/** The description of an individual buffer type. */
typedef struct phStbKBufMgr_BufferDesc
{
    uint32_t size;
    uint32_t numberRequired;
    uint32_t alignment;
    const char *name;
    /** Lower number means place first (nearer top of memory). */
    uint32_t priority;
    uint32_t flags;
} phStbKBufMgr_BufferDesc_t;

/** Option definitions for the buffer types. */
typedef struct phStbKBufMgr_Options
{
    unsigned int frameStoreWidth;
    unsigned int frameStoreStride;

    phStbKBufMgr_BufferDesc_t bufferDescs[phStbKBufMgr_Buffer_Number];
} phStbKBufMgr_Options_t;

/** Information about an instance of a buffer.
    Represents exactly one memory block.
    Each ::phStbKBufMgr_BufferData_t can have up to #MAX_ALLOWED_BUFFERS of these.
*/
typedef struct phStbKBufMgr_Block
{
    /** Back pointer. */
    const struct phStbKBufMgr_BufferData *pBufferData;

    /** Physical address. 0 if unallocated. */
    uint32_t addr;

    /** Size in bytes of area just above this memory block that is free.
        Not used if isOverlay is true. */
    uint32_t aboveFreeSize;

    /** Points to next memory block allocated above this one,
        or NULL if it is the top block.
        Not used if isOverlay is true. */
    struct phStbKBufMgr_Block *pUp;

    /** Points to next memory block allocated below this one,
        or NULL if it is the bottom block.
        Not used if isOverlay is true. */
    struct phStbKBufMgr_Block *pDown;

    /** If this is true, then the block was not allocated, but overlayed over other blocks.
        It means pUp and pDown are not valid,
        and one can/should not use the aboveFreeSize field. */
    bool isOverlay;
} phStbKBufMgr_Block_t;

/** Holds all data about a buffer type. */
typedef struct phStbKBufMgr_BufferData
{
    /** Buffer type ID. */
    phStbKBufMgr_Buffer_t buf;

    /** Definition of this type. */
    const phStbKBufMgr_BufferDesc_t *pBufferDesc;

    /** Initially set to pBufferDesc->numberRequired, and decremented
        when we allocate memory for an instance. */
    uint32_t numberStillToAllocate;

    /** Instances of this buffer type. */
    phStbKBufMgr_Block_t block[MAX_ALLOWED_BUFFERS];
} phStbKBufMgr_BufferData_t;

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

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

/** Default options for the memory setup.

    "0" entries in the array are usually overridden by calling doSetup().

    The priorities for allocating buffers nearer the top of memory are
    (smaller number means earlier placement, and thus more likely to being near the top):
    - 1: very large ES buffers, which have large alignments
    (alignmentForNextPowerOfTwo(size), and thus are given
    the first opportunity to sit at their highest aligned address
    - 2: the rest of the VMSP related buffers
    (others are already at higher priority levels).
    See #PRIORITY_VMSP_MIN_PLACEMENT.
    - 3: MSVD framebuffers
    - 4: the rest.
    This includes the MSVD firmware, because all buffers that need to be above
    it are already at higher priority levels.

    @note The alignment of audio output related buffers (AUDIO_OUT_BUFSIZE AUDIO_BUFSIZE)
    is 4KB since the tmdlAudioIO expects buffers to be 4KB aligned.

    @note The order of elements in this array must match ::phStbKBufMgr_Buffer_t !!!!
*/
phStbKBufMgr_Options_t gphStbKBufMgr_Options =
{
    0,  /* frameStoreWidth */
    0,  /* frameStoreStride */

    {  /* size              numberRequired  alignment         name           prio flags */
        { 0,                0,              0,                "VIDEO_ES",    1,   0},
        { 0,                0,              0,                "AUDIO_ES",    1,   0},
        { 0,                0,              0,                "VSPTS",       2,   0},
        { VSECT_BUFSIZE,    0,              VSECT_BUFSIZE-1,  "VSECT",       2,   0},
        { VFILT_BUFSIZE,    0,              VFILT_BUFSIZE-1,  "VFILT",       2,   0},
        { 0,                0,              0x3ff,            "FSY",         3,   FL_NOGAPS},
        { 0,                0,              0x3ff,            "FSUV",        3,   FL_NOGAPS},
        { 0,                1,              0x3ff,            "FSY_STOP",    3,   FL_NOGAPS},
        { 0,                1,              0x3ff,            "FSUV_STOP",   3,   FL_NOGAPS},
        { 0,                1,              0x7ffff,          "FW",          4,   FL_NOGAPS},
        { 0,                0,              3,                "DWNSCALE_Y",  4,   0},
        { 0,                0,              3,                "DWNSCALE_UV", 4,   0},
        { AUDIO_OUT_BUFSIZE,0,              0xfff,            "AUDIO_OUT",   4,   0},
        { VSYS_BUFSIZE,     0,              3,                "VSYS",        2,   0},
        { AUDIO_BUFSIZE,    0,              0xfff,            "AUDIO",       4,   0},
        { NETDEV_BUFSIZE,   1,              L1_CACHE_BYTES-1, "NETDEV",      4,   0},
        { DEMUXSW_BUFSIZE,  0,              0xfff,            "DMXSW",       4,   0},
        { SSRC_BUFSIZE,     NUM_SSRC,       3,                "SSRC",        4,   0},
        { DEMUXSW_VID_BUFSIZE, 0,           0xfff,            "DMXSW_VID",   4,   0},
        { DEMUXSW_BUFSIZE,  0,              0xfff,            "DMXSW_AUD",   4,   0},
        { DEMUXSW_BUFSIZE,  0,              0xfff,            "DMXSW_AUD2",  4,   0},
        { PES_BUFSIZE,      0,              0,                "VID_PES_HDR", 2,   0},
        { VPTS_BUFSIZE,     0,              3,                "VID_PTS",     2,   0},
        { UD_BUFSIZE,       0,              3,                "USERDATASBM", 4,   0},
        { AHED_BUFSIZE,     0,              3,                "AUD_PES_HDR", 2,   0},
        { APTS_BUFSIZE,     0,              3,                "AUD_PTS",     2,   0},
        { 0,                1,              0,                "AUX_DEMUX",   4,   0},
        { 0,                1,              0,                "ALT_DFB",     4,   0},
        { 0,                1,              0x3ff,            "VID_INPUT",   4,   0},
        { 0,                1,              3,                "DBG_REGION",  4,   0},
        /* Below this are the un-allocated buffers, they overlay
         * themselves over a buffer specified above. */
        { 0,                0,              0x3ff,            "FSY_AVS",     0,   0},
        { 0,                0,              0x3ff,            "FSUV_AVS",    0,   0}
    }
};

/** The top end of reserved RAM. */
static const uint32_t topOfSpace = (TOP_OF_MEMORY - CONFIG_DSP_MIPS_MEM_SIZE);

/** The top end of unallocated RAM. Initially set to ::topOfSpace. */
static uint32_t topOfFreeSpace;

/** The result of our calculation. Will be aligned to #PAGE_SIZE. */
static uint32_t gReservedMemSize;

/** Pointer to the top most memory block allocated. */
static phStbKBufMgr_Block_t *gpTopBlock;

/** Pointer to the bottom most memory block allocated. */
static phStbKBufMgr_Block_t *gpBottomBlock;

/** Internal data about all known buffers. */
static phStbKBufMgr_BufferData_t gBufferData[phStbKBufMgr_Buffer_Number];

/** Total memory at minimum required, ignoring alignment/gaps. */
static uint32_t gNumBytesOptimallyNeeded;

/** Total memory allocated.
    Note this may be less than ::gReservedMemSize since that is aligned to #PAGE_SIZE. */
static uint32_t gNumBytesAllocated;

uint32_t gAlignmentTotal = 0;

/** Output file. */
static FILE *gpOutputFP;

/** Output file name */
static char outputFileName[PATH_MAX];

/** Temporary output file name */
static char tmpOutputFileName[PATH_MAX];

/** Output header file. */
static FILE *gpOutputHeaderFP;

/** Output header file name */
static char outputHeaderFileName[PATH_MAX];

/** Temporary output header file name */
static char tmpOutputHeaderFileName[PATH_MAX];

/** Summary file. */
static FILE *gpSummaryFP;

/** Summary file name */
static char summaryFileName[PATH_MAX];

/** Value of BUILDROOT environment variable. */
static char *gBuildRoot;

/** Indicates if this is the first time we generate the output files.
    If not, then we will compare the new file with the old file to
    prevent updating timestamp of the file. We thus prevent unnecessary
    recompilations. */
static bool gFirstTime = false;
static bool gFirstTimeHeader = false;

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

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

/* Calculate an alignment mask so that it is aligned to
 * the next power of 2 of the size.
 *
 * e.g. size = 768 KB, alignment = 1024 KB
 *                      alignment = 0x00100000 B, mask = 0x000fffff
 * @param size @b IN Size of the buffer
 * @return alignment mask, e.g. 0xFFF.
 */
static uint32_t alignmentForNextPowerOfTwo(uint size)
{
    uint alignment = 0;
    uint alignmentTemp = size - 1;
    while (alignmentTemp != 0)
    {
        alignment <<= 1;
        alignment |= 1;
        alignmentTemp >>= 1;
    }
    return alignment;
}

/**************************************************************/
/** Open the output file and write some prelude to it. */
static void openGeneratedFile(void)
{
    struct stat statbuf;
    char cmd[PATH_MAX + 20];

    sprintf(cmd, "mkdir -p %s/src", gBuildRoot);
    (void)system(cmd); /* error will be picked up when we try to create a file later on */

    sprintf(outputFileName, "%s/src/phStbKBufMgr_Generated.c", gBuildRoot);
    IFDEBUG(fprintf(stderr, "Generating %s\n", outputFileName));
    gFirstTime = (stat(outputFileName, &statbuf) != 0);
    if (gFirstTime) {
        gpOutputFP = fopen(outputFileName, "w");
    } else {
        strcpy(tmpOutputFileName, outputFileName);
        strcat(tmpOutputFileName, ".tmp");
        gpOutputFP = fopen(tmpOutputFileName, "w");
    }
    if (gpOutputFP == NULL)
    {
        perror(gFirstTime ? outputFileName : tmpOutputFileName);
        fprintf(stderr, "phStbKBufMgr_Setup: cannot open output file!\n");
        exit(1);
    }
    fprintf(gpOutputFP, "/* Auto-generated! Do not edit. */\n");
    fputs("#include \"phStbKBufMgr_Int.h\"\n", gpOutputFP);
    fputs("#include \"asm/cache.h\"\n", gpOutputFP);
    fputs("#include \"asm/page.h\"\n", gpOutputFP);
    fprintf(gpOutputFP,
            "\n#if L1_CACHE_BYTES != %u\n"
            "#  error Assumption in phStbKBufMgr_Setup that L1_CACHE_BYTES==%u is incorrect!\n"
            "#endif\n",
            L1_CACHE_BYTES,
            L1_CACHE_BYTES);
    fprintf(gpOutputFP,
            "\n#if PAGE_SIZE != %u\n"
            "#  error Assumption in phStbKBufMgr_Setup that PAGE_SIZE==%u is incorrect!\n"
            "#endif\n",
            PAGE_SIZE,
            PAGE_SIZE);

    sprintf(outputHeaderFileName, "%s/src/phStbKBufMgr_Generated.h", gBuildRoot);
    IFDEBUG(fprintf(stderr, "Generating %s\n", outputHeaderFileName));
    gFirstTimeHeader = (stat(outputHeaderFileName, &statbuf) != 0);
    if (gFirstTimeHeader) {
        gpOutputHeaderFP = fopen(outputHeaderFileName, "w");
    } else {
        strcpy(tmpOutputHeaderFileName, outputHeaderFileName);
        strcat(tmpOutputHeaderFileName, ".tmp");
        gpOutputHeaderFP = fopen(tmpOutputHeaderFileName, "w");
    }

    sprintf(summaryFileName, "%s/src/phStbKBufMgr_Summary.txt", gBuildRoot);
    IFDEBUG(fprintf(stderr, "Generating %s\n", summaryFileName));
    gpSummaryFP = fopen(summaryFileName, "w");
    if (gpSummaryFP == NULL)
    {
        perror(summaryFileName);
        fprintf(stderr, "phStbKBufMgr_Setup: cannot open summary file!\n");
        exit(1);
    }
    fprintf(gpSummaryFP,
            "Summary report for phStbKBufMgr_Setup.\n\nConfiguration parameters used:\n\n");
}

/**************************************************************/
/** Initialises buffer date, mostly to 0 and false.
    Also sets up the numberStillToAllocate fields.
*/
static void initBufferPlacingData(void)
{
    uint buf;
    uint bufMin;
    uint bufMax;
    uint errors = 0;

    memset(gBufferData, 0, sizeof(gBufferData));

    for (buf = 0; (buf < phStbKBufMgr_Buffer_Number); buf++)
    {
        phStbKBufMgr_BufferData_t *pBufferData = &gBufferData[buf];
        const phStbKBufMgr_BufferDesc_t *pBufferDesc = &gphStbKBufMgr_Options.bufferDescs[buf];

        pBufferData->buf = buf;
        pBufferData->pBufferDesc = pBufferDesc;
        pBufferData->numberStillToAllocate = pBufferDesc->numberRequired;
        if (pBufferDesc->numberRequired > MAX_ALLOWED_BUFFERS)
        {
            errors++;
            fprintf(stderr, "**ERROR: phStbKBufMgr: Too many buffers (%u > %u): type %s (%d)\nAborting.\n",
                    pBufferDesc->numberRequired, MAX_ALLOWED_BUFFERS, pBufferDesc->name, buf);
        }
    }

    /* The following buffers must be allocated without gaps between them,
       because of the way overlayAVSFrameStores() uses them.
       We assume also that they are next to eachother in the enum list! */
    bufMin = MIN(phStbKBufMgr_Buffer_FSY, phStbKBufMgr_Buffer_FSUV);
    bufMax = MAX(phStbKBufMgr_Buffer_FSY, phStbKBufMgr_Buffer_FSUV);
    for (buf = bufMin; buf < bufMax; buf++)
    {
        const phStbKBufMgr_BufferDesc_t *pBufferDesc = &gphStbKBufMgr_Options.bufferDescs[buf];
        if ((pBufferDesc->size & pBufferDesc->alignment) != 0)
        {
            errors++;
            fprintf(stderr, "**WARNING: phStbKBufMgr: Size (%u) is not aligned on 0x%x: type %s (%d)\nAborting.\n",
                    pBufferDesc->size, pBufferDesc->alignment, pBufferDesc->name, buf);
        }
    }

    if (errors > 0)
    {
        exit(1);
    }
}

/**************************************************************/
/** Checks if a buffer type fits in an address region.
    @param @b IN pBufferDesc For debug purposes.
    @param @b IN addr Address of region that is free.
    @param @b IN size Size of region that is free.
    @param @b IN pBufferDesc Buffer type to check if it fits into addr-addr+size.
    @return true if it fits.
*/
static bool fits(
    const phStbKBufMgr_BufferDesc_t *pBufferDescOfGap,
    uint32_t addr,
    uint32_t size,
    const phStbKBufMgr_BufferDesc_t *pBufferDesc)
{
    uint32_t requiredSize = pBufferDesc->size;

    IFNDEBUG((void)pBufferDescOfGap);

    if (requiredSize == 0)
    {
        return true;
    }

    if (size == 0)
    {
        return false;
    }
    else
    {
        uint32_t alignment = pBufferDesc->alignment;
        uint32_t addrAligned = PH_STBKBUFMGR_ALIGN_UP(addr, alignment);
        bool result = (addrAligned + requiredSize <= addr + size);
        IFDEBUG(
            fprintf(stderr,
                    "    fits(%12s size %06x in 0x%08x-0x%08x above %12s)?: aligned 0x%08x-0x%08x => %s\n",
                    pBufferDesc->name,
                    requiredSize,
                    addr, addr + size,
                    pBufferDescOfGap->name,
                    addrAligned, addrAligned+requiredSize,
                    result ? "FITS" : "DOES NOT FIT");
                );
        return result;
    }
}

/**************************************************************/
static void overlayAVSFrameStores(void)
{
    phStbKBufMgr_Buffer_t buf;

    /* Now overlay the AVS framestores on the normal framestores */
    /* Check that the non AVS framestores were allocated OK      */
    if (gBufferData[phStbKBufMgr_Buffer_FSY].block[0].addr && gBufferData[phStbKBufMgr_Buffer_FSUV].block[0].addr)
    {
        uint32_t i;
        uint32_t numFSBuffers;
        uint32_t sizeFSBuffers;
        uint32_t sizeAlignedAvsFSBuffers;
        uint32_t memoryTop;
        uint32_t memoryBottom;
        uint32_t memoryAvailable;
        uint32_t buffAddress;

        /* Allocation above of the non-AVS    .-----------.   */
        /* framestores is from the top down   |           |   */
        /* so the memory available to us      |  FSY[0]   |   */
        /* starts from the bottom of the last |-----------|   */
        /* framestore to the end of the first |           |   */
        /*                                    |  FSY[1]   |   */
        /*                                    |-----------|   */
        /*                                    |           |   */
        /*                                    |  FSY[n]   |   */
        /*                                    `-----------'   */

        /* First do the FSY buffers */
        buf = phStbKBufMgr_Buffer_FSY;
        numFSBuffers = gphStbKBufMgr_Options.bufferDescs[buf].numberRequired;
        sizeFSBuffers = gphStbKBufMgr_Options.bufferDescs[buf].size;

        memoryTop = gBufferData[buf].block[0].addr + sizeFSBuffers;
        memoryBottom = gBufferData[buf].block[numFSBuffers-1].addr;
        memoryAvailable = memoryTop - memoryBottom;

        sizeAlignedAvsFSBuffers =
            PH_STBKBUFMGR_ALIGN_UP(gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_FSY_AVS].size,
                                   gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_FSY_AVS].alignment);

        IFDEBUG(fprintf(stderr,
                        "numFSBuffers %2d\nsizeFSBuffers %02d\nmemoryTop 0x%08x\nmemoryBottom 0x%08x\nsizeAlignedAvsFSBuffers 0x%6x\nsizeAlignedAvsFSBuffers 0x%6x\n",
                        numFSBuffers, sizeFSBuffers, memoryTop, memoryBottom, sizeAlignedAvsFSBuffers, sizeAlignedAvsFSBuffers));

        i = 0;
        /* Do not create more than 16 framestores else bad things can happen */
        for (buffAddress = memoryBottom; (i < 16) && (buffAddress < (memoryTop - sizeAlignedAvsFSBuffers)); buffAddress += sizeAlignedAvsFSBuffers)
        {
            phStbKBufMgr_Block_t *pBlock = &gBufferData[phStbKBufMgr_Buffer_FSY_AVS].block[i];
            pBlock->isOverlay = true;
            pBlock->addr = buffAddress;

            IFDEBUG(fprintf(stderr,
                            "%2d/%02d 0x%08x 0x%06x 0x%06x\n",
                            phStbKBufMgr_Buffer_FSY_AVS,
                            i,
                            pBlock->addr,
                            gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_FSY_AVS].size,
                            gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_FSY_AVS].alignment));
            i++;
        }
        gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_FSY_AVS].numberRequired = i;

        /* Now do the FSUV buffers */
        buf = phStbKBufMgr_Buffer_FSUV;
        numFSBuffers = gphStbKBufMgr_Options.bufferDescs[buf].numberRequired;
        sizeFSBuffers = gphStbKBufMgr_Options.bufferDescs[buf].size;

        memoryTop = gBufferData[buf].block[0].addr + sizeFSBuffers;
        memoryBottom = gBufferData[buf].block[numFSBuffers-1].addr;
        memoryAvailable = memoryTop - memoryBottom;

        sizeAlignedAvsFSBuffers =
            PH_STBKBUFMGR_ALIGN_UP(gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_FSUV_AVS].size,
                                   gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_FSUV_AVS].alignment);

        IFDEBUG(fprintf(stderr,
                        "numFSBuffers %2d\nsizeFSBuffers %02d\nmemoryTop 0x%08x\nmemoryBottom 0x%08x\nsizeAlignedAvsFSBuffers 0x%6x\nsizeAlignedAvsFSBuffers 0x%6x\n",
                        numFSBuffers, sizeFSBuffers, memoryTop, memoryBottom, sizeAlignedAvsFSBuffers, sizeAlignedAvsFSBuffers));

        i = 0;
        /* Do not create more than 16 framestores else bad things can happen */
        for (buffAddress = memoryBottom; (i < 16) && (buffAddress < (memoryTop - sizeAlignedAvsFSBuffers)); buffAddress += sizeAlignedAvsFSBuffers)
        {
            phStbKBufMgr_Block_t *pBlock = &gBufferData[phStbKBufMgr_Buffer_FSUV_AVS].block[i];
            pBlock->isOverlay = true;
            pBlock->addr = buffAddress;
            IFDEBUG(fprintf(stderr,
                            "%2d/%02d 0x%08x 0x%06x 0x%06x\n",
                            phStbKBufMgr_Buffer_FSUV_AVS,
                            i,
                            pBlock->addr,
                            gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_FSUV_AVS].size,
                            gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_FSUV_AVS].alignment));
            i++;
        }
        gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_FSUV_AVS].numberRequired = i;
    }
}

/**************************************************************/
static void fitExtraSDFrameStore(
    phStbKBufMgr_BufferData_t *pBufferData)
{
    uint32_t bufAddr2;
    const phStbKBufMgr_BufferDesc_t *pBufferDesc = pBufferData->pBufferDesc;
    uint32_t bufAddr;
    int i;

    if (pBufferData->numberStillToAllocate == 0)
    {
        return;
    }

    assert(pBufferDesc->numberRequired > 0);

    /* Index of next block to allocate: */
    i = pBufferDesc->numberRequired - pBufferData->numberStillToAllocate;

    /* Address of last buffer block allocated: */
    bufAddr = pBufferData->block[i - 1].addr;

    IFDEBUG(fprintf(stderr,
                    "fitExtraSDFrameStore(%s 0x%08x)???\n",
                    pBufferData->pBufferDesc->name, bufAddr));

    /* Due to a restriction in the MSVD2 integration, the framestore
     * stride for hw assisted decoders has to be 2048 bytes.
     * We can fit 2 SD framestores into this stride by adding
     * an offset to give the address of the second one.
     *
     * Check whether we can fit another framestore within this
     * one. First calculate the address we would need to
     * use for the second framestore. */
    bufAddr2 = bufAddr + gphStbKBufMgr_Options.frameStoreWidth;
    if (bufAddr2 & pBufferDesc->alignment)
    {
        bufAddr2 += pBufferDesc->alignment + 1 - (bufAddr2 & pBufferDesc->alignment);
    }

    if ((bufAddr2 - bufAddr + gphStbKBufMgr_Options.frameStoreWidth) <= gphStbKBufMgr_Options.frameStoreStride)
    {
        phStbKBufMgr_Block_t *pBlock;
        /* It fits! */
        pBlock = &pBufferData->block[i];
        pBlock->isOverlay = true;
        pBlock->addr = bufAddr2;
        pBufferData->numberStillToAllocate--;

        IFDEBUG(fprintf(stderr,
                        "    framestore %s inside larger one buf %d/%d @ %08x\n",
                        pBufferDesc->name,
                        pBufferData->buf, i, bufAddr2));
        IFDEBUG(fprintf(stderr,
                        "    %02d/%02d 0x%08x 0x%06x 0x%06x\n",
                        pBufferData->buf, i, bufAddr2, pBufferDesc->size, pBufferDesc->alignment));
    }
}

/**************************************************************/
/** Place one instance of a buffer type into the available free memory.
    @param @b IN useGaps If true, then try to fit the buffer type into gaps first,
    before expanding the amount of allocated memory downwards.
    Gaps always sit above already-allocated buffers.
    @param @b IN pBufferData Buffer type to allocate.
 */
static void placeOneBuffer(
    phStbKBufMgr_BufferData_t *pBufferData)
{
    phStbKBufMgr_Block_t *pExistingBlock;
    const phStbKBufMgr_BufferDesc_t *pBufferDesc = pBufferData->pBufferDesc;
    uint index = pBufferDesc->numberRequired - pBufferData->numberStillToAllocate;
    phStbKBufMgr_Block_t *pBlock = &pBufferData->block[index];
    bool useGaps = (pBufferDesc->flags & FL_NOGAPS) ? false : true;

    /* assure it's not allocated yet: */
    assert(pBlock->addr == 0);
    assert(pBlock->aboveFreeSize == 0);
    assert(pBlock->pUp == NULL);
    pBlock->pBufferData = pBufferData;

    IFDEBUG(fprintf(stderr, "PlaceBuffer: %12s size %06x align %06x ...\n",
                    pBufferDesc->name, pBufferDesc->size, pBufferDesc->alignment));
    if (useGaps)
    {
        for (pExistingBlock = gpBottomBlock; pExistingBlock != NULL; pExistingBlock = pExistingBlock->pUp)
        {
            /* top of existing block: */
            uint32_t existingBlockTop = pExistingBlock->addr + pExistingBlock->pBufferData->pBufferDesc->size;
            if (fits(pExistingBlock->pBufferData->pBufferDesc,
                     existingBlockTop,
                     pExistingBlock->aboveFreeSize,
                     pBufferDesc))
            {
                uint32_t blockTop; /* top of block we are placing */
                pBlock->addr = PH_STBKBUFMGR_ALIGN_UP(existingBlockTop, pBufferDesc->alignment);
                gNumBytesOptimallyNeeded += pBufferDesc->size;
                /* calculate new free spaces */
                blockTop = pBlock->addr + pBufferDesc->size;
                pBlock->aboveFreeSize = existingBlockTop + pExistingBlock->aboveFreeSize - blockTop;
                pExistingBlock->aboveFreeSize = pBlock->addr - existingBlockTop;
                /* Insert in list: */
                pBlock->pUp = pExistingBlock->pUp;
                pExistingBlock->pUp = pBlock;
                break;
            }
        }
    }

    /* Not yet allocated? */
    if (pBlock->addr == 0)
    {
        uint32_t alignmentAmount;
        /* Block not yet placed (in a gap), so insert it below the bottom block (gpBottomBlock). */
        if (gpBottomBlock != NULL)
        {
            assert(topOfFreeSpace == gpBottomBlock->addr);
            pBlock->pUp = gpBottomBlock;
        }
        pBlock->addr = topOfFreeSpace - pBufferDesc->size;
        alignmentAmount = pBlock->addr & pBufferDesc->alignment;
        pBlock->addr -= alignmentAmount;
        pBlock->aboveFreeSize = alignmentAmount;
        gpBottomBlock = pBlock;
        topOfFreeSpace = gpBottomBlock->addr;
        gNumBytesOptimallyNeeded += pBufferDesc->size;
        IFDEBUG(fprintf(stderr, "    Put at the bottom (space above = %u)\n", alignmentAmount));
    }

    /* Update the list of blocks. */
    if (gpTopBlock == NULL)
    {
        gpTopBlock = pBlock;
        gpBottomBlock = pBlock;
        topOfFreeSpace = gpBottomBlock->addr;
    }
    else
    {
        if (gpTopBlock->pUp != NULL)
        {
            gpTopBlock = gpTopBlock->pUp;
        }
    }

    pBufferData->numberStillToAllocate--;
}

/**************************************************************/
/** Find the best buffer type candidate to allocate next.
    Just find the biggest one.
    @todo Improve findNextBufToAlloc to consider size AND/OR alignments.
 */
static bool findNextBufToAlloc(
    phStbKBufMgr_Buffer_t first,
    phStbKBufMgr_Buffer_t last,
    phStbKBufMgr_BufferData_t **pNextBufToAlloc)
{
    uint buf;
    uint32_t nextBufToAllocPriority = PRIORITY_LOWEST;
    uint32_t nextBufToAllocSize = 0;
    uint32_t nextBufToAllocAlignment = 0;

    *pNextBufToAlloc = NULL;

    for (buf = first; buf <= last; buf++)
    {
        phStbKBufMgr_BufferData_t *pBufferData = &gBufferData[buf];
        const phStbKBufMgr_BufferDesc_t *pBufferDesc = pBufferData->pBufferDesc;
        uint32_t priority = pBufferDesc->priority;
        uint32_t size = pBufferDesc->size;
        uint32_t alignment = pBufferDesc->alignment;

        if (pBufferData->numberStillToAllocate != 0)
        {
            if ((*pNextBufToAlloc == NULL)
                || (priority < nextBufToAllocPriority)
                || ((priority == nextBufToAllocPriority)
                    && (
                        // TODO: perhaps try all combination???
                        //|| (alignment > nextBufToAllocAlignment)
                        //|| (size > nextBufToAllocSize)
                        (size + alignment > nextBufToAllocSize + nextBufToAllocAlignment)
                        )
                    )
                )
            {
                nextBufToAllocSize = size;
                nextBufToAllocAlignment = alignment;
                nextBufToAllocPriority = priority;
                *pNextBufToAlloc = pBufferData;
            }
        }
    }
    if (*pNextBufToAlloc == NULL)
    {
        IFDEBUG(fprintf(stderr,
                        "findNextBufToAlloc => NOTHING to allocate!\n"));
        return false;
    }
    else
    {
        IFDEBUG(fprintf(stderr,
                        "findNextBufToAlloc => %s size 0x%x align 0x%x\n",
                        (*pNextBufToAlloc)->pBufferDesc->name,
                        (*pNextBufToAlloc)->pBufferDesc->size,
                        (*pNextBufToAlloc)->pBufferDesc->alignment
                        ));
        return true;
    }
}

/**************************************************************/
/** Set-up down pointers between consequtive blocks of memory.
 */
static void setupDownPointers(void)
{
    phStbKBufMgr_Block_t *pBlock;

    for (pBlock = gpBottomBlock; pBlock != NULL; pBlock = pBlock->pUp)
    {
        if (pBlock->isOverlay)
        {
            assert(pBlock->pUp == NULL);
            assert(pBlock->aboveFreeSize == 0);
            continue;
        }

        if (pBlock->pUp == NULL)
        {
            assert(pBlock == gpTopBlock);
        }
        else
        {
            assert(pBlock->pUp->pDown == NULL);
            pBlock->pUp->pDown = pBlock;
        }
    }
    assert(gpBottomBlock->pDown == NULL);
}

/**************************************************************/
/** Place all buffers into free memory. */
static void placeAllBuffers(void)
{
    phStbKBufMgr_BufferData_t *pNextBufToAlloc;
    initBufferPlacingData();

    topOfFreeSpace = topOfSpace;

    while (findNextBufToAlloc(0,
                              phStbKBufMgr_Buffer_AllocatedGuard - 1,
                              &pNextBufToAlloc)) {
        placeOneBuffer(pNextBufToAlloc);

        if (pNextBufToAlloc->buf == phStbKBufMgr_Buffer_FSUV ||
            pNextBufToAlloc->buf == phStbKBufMgr_Buffer_FSY)
        {
            fitExtraSDFrameStore(pNextBufToAlloc);
        }
    }

    gNumBytesAllocated = topOfSpace - topOfFreeSpace;
    gAlignmentTotal = gNumBytesAllocated - gNumBytesOptimallyNeeded;
    gReservedMemSize = PH_STBKBUFMGR_ALIGN_UP(gNumBytesAllocated, PAGE_SIZE);

    overlayAVSFrameStores();

    setupDownPointers();
}

/**************************************************************/
/** Check that a buffer type don't straddle a region.
    @param buf @b IN buffer type to check
    @param mask @b IN mask value indicating which bits in an address
    contribute to the region address.
    @param pRegion @b INOUT If points to a UINT_MAX value,
    update it with the region value.
    @return 0 if OK, otherwise -1.
*/
static int checkWithinRegion(
    phStbKBufMgr_Buffer_t buf,
    uint32_t region,
    bool verbose,
    uint32_t *pMask)
{
    int ret = 0;
    uint32_t mask = *pMask;
    uint i;
    phStbKBufMgr_BufferData_t *pBufferData = &gBufferData[buf];
    const phStbKBufMgr_BufferDesc_t *pBufferDesc = pBufferData->pBufferDesc;
    for (i = 0; i < pBufferDesc->numberRequired; i++)
    {
        uint32_t addr;
        const phStbKBufMgr_Block_t *pBlock = &pBufferData->block[i];
        char nameBuffer[128];

        if (snprintf (nameBuffer, sizeof(nameBuffer), "%s[%u]", pBufferDesc->name, i) == sizeof(nameBuffer))
        {
            nameBuffer[sizeof(nameBuffer) - 1] = 0;
        }

        addr = pBlock->addr;
        /* Mask not yet used, in that case our first address defines the mask indicating which region we use. */
        if (mask == UINT_MAX)
        {
            mask = addr & (~(region - 1));
        }

        if (verbose)
        {
            fprintf(stderr, "phStbKBufMgr_Setup: buffer %-13s: start address 0x%08x: in %d * 0x%08x region\n",
                    nameBuffer, addr, addr / region, region);
        }
        if ((addr & mask) != mask)
        {
            ret = -1;
            if (verbose)
            {
                fprintf(stderr, "**ERROR: phStbKBufMgr_Setup: buffer %-13s: start address 0x%08x: not in same multiple of 0x%08x as related buffers\n",
                        nameBuffer, addr, region);
            }
        }

        addr = (pBlock->addr + pBufferDesc->size - 1);
        if (verbose)
        {
            fprintf(stderr, "phStbKBufMgr_Setup: buffer %-13s: end   address 0x%08x: in %d * 0x%08x region\n",
                    nameBuffer, addr, addr / region, region);
        }
        if ((addr & mask) != mask)
        {
            ret = -1;
            if (verbose)
            {
                fprintf(stderr, "**ERROR: phStbKBufMgr_Setup: buffer %-13s: end   address 0x%08x: not in same multiple of 0x%08x as related buffers\n",
                        nameBuffer, addr, region);
            }
        }
    }
    *pMask = mask;
    return ret;
}

/**************************************************************/
/** Check that all vmsp related buffers don't straddle a 64MB region.
*/
static void validateVmspSetup(bool firstTime)
{
    int ret = 0;
    uint buf;
    bool verbose = !firstTime;
    uint32_t mask = UINT_MAX;
    uint32_t priority;

    for (priority = PRIORITY_HIGHEST; priority <= PRIORITY_VMSP_MIN_PLACEMENT; priority++)
    {
        for (buf = 0; buf < phStbKBufMgr_Buffer_Number; buf++)
        {
            if (gBufferData[buf].pBufferDesc->priority == priority)
            {
                ret |= checkWithinRegion(buf, VMSP_REGION_SIZE, verbose, &mask);
            }
        }
    }

    /* If not first time, re-run checks with the verbose flag */
    if (ret != 0)
    {
        if (firstTime)
        {
            validateVmspSetup(!firstTime);
        }
        fprintf(stderr, "Buffer placement algorithm needs to be improved! :-(\n");
        exit(1);
    }
}

/**************************************************************/
/** Check the allocation calculation. Checks:
    - amount of memory used for alignment is correct (compare with free space between blocks).
    - up/down pointers are correct.
    - no overlap between blocks.
*/
static void validateCalculation(void)
{
    phStbKBufMgr_Block_t *pBlock;
    uint32_t totalFreeSpace = 0;

    /* Check that gAlignmentTotal is correct: */
    for (pBlock = gpBottomBlock; pBlock != NULL; pBlock = pBlock->pUp)
    {
        const phStbKBufMgr_BufferDesc_t *pBufferDesc = pBlock->pBufferData->pBufferDesc;
        assert(!pBlock->isOverlay);

        totalFreeSpace += pBlock->aboveFreeSize;

        if (pBlock->pUp != NULL)
        {
            assert(pBlock->addr + pBufferDesc->size + pBlock->aboveFreeSize == pBlock->pUp->addr);
        }
    }
    assert(gpTopBlock->addr + gpTopBlock->pBufferData->pBufferDesc->size + gpTopBlock->aboveFreeSize == topOfSpace);
    assert(gpBottomBlock->addr == topOfFreeSpace);

    IFDEBUG(fprintf(stderr, "gNumBytesAllocated       = %u\n", gNumBytesAllocated));
    IFDEBUG(fprintf(stderr, "gNumBytesOptimallyNeeded = %u\n", gNumBytesOptimallyNeeded));
    IFDEBUG(fprintf(stderr, "gAlignmentTotal          = %u\n", gAlignmentTotal));
    IFDEBUG(fprintf(stderr, "totalFreeSpace           = %u\n", totalFreeSpace));
    assert(totalFreeSpace == gAlignmentTotal);

#if CONFIG_NUM_HW_DEMUXES > 0
    validateVmspSetup(true /* firstTime */);
#endif

    if (gAlignmentTotal > MAX_ALIGNMENT_SPACE_ALLOWED)
    {
        fprintf(stderr, "phStbKBufMgr_Setup: too much space used for alignment (%u KB).\nLimit was set to %d KB. Please improve placement algorithm.\n",
                gAlignmentTotal / 1024, MAX_ALIGNMENT_SPACE_ALLOWED / 1024);
        fprintf(stderr, "Buffer placement algorithm needs to be improved! :-(\n");
        exit(1);
    }
}

/**************************************************************/
/** Print summary of memory size. */
static void printSummary(void)
{
    uint buf;
    int mips1Size;
    int totalSize_calculation_ignore_overlays = 0;
    const char *prefix = "### ";

    fprintf(gpSummaryFP, "\nBuffer specifications:\n\n");
//                        10      AUD_OUT 0x030000   2 0x000FFF 0x00060000    384.0 KB   0.375 MB    3
    fprintf(gpSummaryFP, "Id   Name       Each Size  Num  Align   Total Bytes.....KB....MB...   Prio\n");

    for (buf = 0; buf < phStbKBufMgr_Buffer_Number; ++buf)
    {
        uint i;
        uint totalSize;
        uint totalSize_ignore_overlays;
        phStbKBufMgr_BufferDesc_t *pBufferDesc = &gphStbKBufMgr_Options.bufferDescs[buf];

        if (buf == phStbKBufMgr_Buffer_AllocatedGuard)
        {
            totalSize_calculation_ignore_overlays = 1;
            fprintf(gpSummaryFP,
                    "\nThe following buffers are overlaid on top of one or more of the buffers above:\n\n");
        }

        totalSize_ignore_overlays = pBufferDesc->size * pBufferDesc->numberRequired;

        if (totalSize_calculation_ignore_overlays)
            totalSize = totalSize_ignore_overlays;
        else {
            totalSize = 0;
            for (i = 0; i < pBufferDesc->numberRequired; i++)
                if (gBufferData[buf].block[i].isOverlay != true)
                    totalSize += pBufferDesc->size;
        }

        fprintf(gpSummaryFP,
                "%2d %12s 0x%08x %3d 0x%06X 0x%08x %8.1f %7.3f %4d"
                , buf
                , pBufferDesc->name
                , pBufferDesc->size
                , pBufferDesc->numberRequired
                , pBufferDesc->alignment
                , totalSize
                , TO_KB(totalSize)
                , TO_MB(totalSize)
                , pBufferDesc->priority);

        if (totalSize != totalSize_ignore_overlays)
            fprintf(gpSummaryFP,
                    "   <-- Overlayed (total size without overlay: %8.1f k", TO_KB(totalSize_ignore_overlays));

        fprintf(gpSummaryFP,
                "\n");
    }

    fprintf(gpSummaryFP,
            "\n%sTotal buffers size:  0x%08x bytes, %8.1f KB, %7.3f MB\n",
            prefix,
            gNumBytesOptimallyNeeded,
            TO_KB(gNumBytesOptimallyNeeded),
            TO_MB(gNumBytesOptimallyNeeded));

    fprintf(gpSummaryFP,
            "%sAlignment space:     0x%08x bytes, %8.1f KB, %7.3f MB\n",
            prefix,
            gAlignmentTotal,
            TO_KB(gAlignmentTotal),
            TO_MB(gAlignmentTotal));

    fprintf(gpSummaryFP,
            "%sIncluding alignment: 0x%08x bytes, %8.1f KB, %7.3f MB\n",
            prefix,
            gNumBytesAllocated,
            TO_KB(gNumBytesAllocated),
            TO_MB(gNumBytesAllocated));

    fprintf(gpSummaryFP,
            "%sTotal reserved:      0x%08x bytes, %8.1f KB, %7.3f MB\n",
            prefix,
            gReservedMemSize,
            TO_KB(gReservedMemSize),
            TO_MB(gReservedMemSize));

    fprintf(gpSummaryFP,
            "%sMIPS2 memory:        0x%08x bytes, %8.1f KB, %7.3f MB\n",
            prefix,
            CONFIG_DSP_MIPS_MEM_SIZE,
            TO_KB(CONFIG_DSP_MIPS_MEM_SIZE),
            TO_MB(CONFIG_DSP_MIPS_MEM_SIZE));

    if ((CONFIG_DSP_MIPS_MEM_SIZE + gReservedMemSize) < (64*1024*1024))
    {
        mips1Size = (64*1024*1024) - (CONFIG_DSP_MIPS_MEM_SIZE + gReservedMemSize);
        fprintf(gpSummaryFP,
                "%sMIPS1 memory:        0x%08x bytes, %8.1f KB, %7.3f MB ( 64MB board)\n",
                prefix,
                mips1Size,
                TO_KB(mips1Size),
                TO_MB(mips1Size));
    }

    if ((CONFIG_DSP_MIPS_MEM_SIZE + gReservedMemSize) < (128*1024*1024))
    {
        mips1Size = (128*1024*1024) - (CONFIG_DSP_MIPS_MEM_SIZE + gReservedMemSize);
        fprintf(gpSummaryFP,
                "%sMIPS1 memory:        0x%08x bytes, %8.1f KB, %7.3f MB (128MB board)\n",
                prefix,
                mips1Size,
                TO_KB(mips1Size),
                TO_MB(mips1Size));
    }

    mips1Size = (256*1024*1024) - (CONFIG_DSP_MIPS_MEM_SIZE + gReservedMemSize);
    fprintf(gpSummaryFP,
            "%sMIPS1 memory:        0x%08x bytes, %8.1f KB, %7.3f MB (256MB board)\n",
            prefix,
            mips1Size,
            TO_KB(mips1Size),
            TO_MB(mips1Size));
}

/**************************************************************/
/** Print detailed report of all blocks. */
static void printReport(void)
{
    int buf;
    phStbKBufMgr_Block_t *pBlock;

    fprintf(gpOutputHeaderFP, "/* Auto-generated! Do not edit. */\n\n");
    for (buf = 0; (buf < phStbKBufMgr_Buffer_Number); buf++)
    {
        if (gBufferData[buf].pBufferDesc->numberRequired > 0)
        {
            int bufferOffset;
            if (gBufferData[buf].block[0].addr <
                gBufferData[buf].block[gBufferData[buf].pBufferDesc->numberRequired - 1].addr)
            {
                bufferOffset = 0;
            }
            else
            {
                bufferOffset = gBufferData[buf].pBufferDesc->numberRequired - 1;
            }
            fprintf(gpOutputHeaderFP, "#define phStbKBufMgr_start_%-20s (0x%08X)\n",
                    gBufferData[buf].pBufferDesc->name,
                    gBufferData[buf].block[bufferOffset].addr);
        }
    }

    fputs("\n/* Buffers ordered by type\n", gpOutputFP);
    fprintf(gpOutputFP, "%2s/%2s-%12s: %8s %6s %6s\n",
            "Ty", "# ", "Type Name", "address", "size", "align");
    for (buf = 0; buf < phStbKBufMgr_Buffer_Number; ++buf)
    {
        uint n;
        phStbKBufMgr_BufferData_t *pBufferData = &gBufferData[buf];
        const phStbKBufMgr_BufferDesc_t *pBufferDesc = pBufferData->pBufferDesc;
        for (n = 0; n < pBufferDesc->numberRequired; n++)
        {
            pBlock = &pBufferData->block[n];
            fprintf(gpOutputFP, "%2d/%02d-%12s: %08X %06X %06X\n"
                    , buf
                    , n
                    , pBufferDesc->name
                    , pBlock->addr
                    , pBufferDesc->size
                    , pBufferDesc->alignment);
        }
    }

    /* Print list of blocks from top to bottom. */
    fprintf(gpOutputFP, "\nBuffers ordered from top to bottom\n");
    fprintf(gpOutputFP, "%12s: %8s %6s %6s\n",
            "Name", "Addr", "Size", "Align");
    for (pBlock = gpTopBlock; pBlock != NULL; pBlock = pBlock->pDown)
    {
        const phStbKBufMgr_BufferDesc_t *pBufferDesc = pBlock->pBufferData->pBufferDesc;
        if (pBlock->aboveFreeSize != 0)
        {
            fprintf(gpOutputFP, "%12s: %08X %06x\n",
                    "--GAP--",
                    pBlock->addr + pBufferDesc->size,
                    pBlock->aboveFreeSize);
        }
        fprintf(gpOutputFP, "%12s: %08X %06X %06X\n"
                , pBufferDesc->name
                , pBlock->addr
                , pBufferDesc->size
                , pBufferDesc->alignment);
    }
    fputs("*/\n\n", gpOutputFP);
}

/**************************************************************/
/*
   Print requirements of the buffer types for debug purposes.
   Warning: Setup information before allocation is confusing for two reasons:

    - For SD only configs it doesn't take account of fitting 2 SD framestores
      within the 2048 byte HW stride, so framestore memory usage will appear
      to be much more than it actually is.

    - The number of SW decoder framestores which will be available is not known
      (it will typically be more than the number of HW decoder framestores).

   Therefore the setup summary is disabled unless building with DEBUG enabled.
   More accurate information is included in the printSummary output after
   allocation is complete.
*/
static void printSetup(void)
{
#if defined (DEBUG)

    uint buf;
    fprintf(gpSummaryFP, "\nBuffer specifications (pre-allocation):\n\n");
//                        10      AUD_OUT 0x030000   2 0x000FFF 0x00060000    384.0 KB   0.375 MB    3
    fprintf(gpSummaryFP, "Id   Name       Each Size  Num  Align   Total Bytes.....KB....MB...   Prio\n");
    for (buf = 0; buf < phStbKBufMgr_Buffer_Number; ++buf)
    {
        phStbKBufMgr_BufferDesc_t *pBufferDesc = &gphStbKBufMgr_Options.bufferDescs[buf];
        uint totalSize = pBufferDesc->size * pBufferDesc->numberRequired;
        if (buf == phStbKBufMgr_Buffer_AllocatedGuard)
        {
            fprintf(gpSummaryFP,
                    "\nThe following buffers are overlaid on top of one or more of the buffers above:\n\n");
        }
        fprintf(gpSummaryFP,
                "%2d %12s 0x%08x %3d 0x%06X 0x%08x %8.1f %7.3f %4d\n"
                , buf
                , pBufferDesc->name
                , pBufferDesc->size
                , pBufferDesc->numberRequired
                , pBufferDesc->alignment
                , totalSize
                , TO_KB(totalSize)
                , TO_MB(totalSize)
                , pBufferDesc->priority);
    }

#endif
}

/**************************************************************/
/** Perform the setup of the memory buffer types. */
static int doSetup(void)
{
    int frameStoreNo;
    int frameStoreWidth;
    int frameStoreHeight;
    int frameStoreStride;
    int frameStoreHeightSw;
    int frameStoreStrideSw;
    int fwBufSizeMB = 0;

    /* Add here a list of param check statements for those CONFIG_*
       parameters that are used at file scope.
       CONFIG_* parameters that are used further on should be
       handled at the location where they are first used. */
    GENERATE_PARAM_IS_DEFINED_CHECK(CONFIG_DSP_MIPS_MEM_SIZE);
#if defined(CONFIG_MULTICHANNEL_AUDIO)
    GENERATE_PARAM_IS_DEFINED_CHECK(CONFIG_MULTICHANNEL_AUDIO);
#else
    GENERATE_PARAM_IS_UNDEFINED_CHECK(CONFIG_MULTICHANNEL_AUDIO);
#endif

#if defined(CONFIG_REAL_ENABLE)
    GENERATE_PARAM_IS_DEFINED_CHECK(CONFIG_REAL_ENABLE);
#else
    GENERATE_PARAM_IS_UNDEFINED_CHECK(CONFIG_REAL_ENABLE);
#endif

    /* The number of frame stores. */
    GENERATE_PARAM_IS_DEFINED_CHECK(CONFIG_NUM_FRAMESTORES);
    frameStoreNo = CONFIG_NUM_FRAMESTORES;

/* Calculate:
   - The width of frame stores (1920 or 720 are usual values)
   - The height of frame stores (1088, 720, 576 or 480 are usual values)
*/

#if defined (CONFIG_HDDECODE_ENABLE)
    GENERATE_PARAM_IS_DEFINED_CHECK(CONFIG_HDDECODE_ENABLE);
#if defined (CONFIG_MSVD_UPSCALE_ENABLE)
    GENERATE_PARAM_IS_DEFINED_CHECK(CONFIG_MSVD_UPSCALE_ENABLE);
    frameStoreWidth = 1920;
    frameStoreHeight = 1296;
    fwBufSizeMB = MAX(fwBufSizeMB, 5);
#else
    GENERATE_PARAM_IS_UNDEFINED_CHECK(CONFIG_MSVD_UPSCALE_ENABLE);
    frameStoreWidth = 1920;
    frameStoreHeight = 1088;
    fwBufSizeMB = MAX(fwBufSizeMB, 4);
#endif
#else
    GENERATE_PARAM_IS_UNDEFINED_CHECK(CONFIG_HDDECODE_ENABLE);
    frameStoreWidth = 720;
    frameStoreHeight = 576;

    fwBufSizeMB = MAX(fwBufSizeMB, 2);

#if defined (CONFIG_WMV)
    /* Even for SD, 4MiB firmware is required for VC1 decoding */
    GENERATE_PARAM_IS_DEFINED_CHECK(CONFIG_WMV);
    fwBufSizeMB = MAX(fwBufSizeMB, 4);
#else
    GENERATE_PARAM_IS_UNDEFINED_CHECK(CONFIG_WMV);
#endif

    GENERATE_PARAM_IS_DEFINED_CHECK(CONFIG_NUM_VIDEO_DECODERS);
#if (CONFIG_NUM_VIDEO_DECODERS > 1)
    /* Even for SD, 4MiB firmware is required for dual video decoding */
    fwBufSizeMB = MAX(fwBufSizeMB, 4);
#endif

#endif

    frameStoreStride = FRAMESTORE_STRIDE;
    frameStoreHeightSw = FRAMESTORE_HEIGHT_SW;
    frameStoreStrideSw = STRIDE_ROUNDUP(FRAMESTORE_WIDTH_SW);

#if defined (CONFIG_AVS_ENABLE)
    GENERATE_PARAM_IS_DEFINED_CHECK(CONFIG_AVS_ENABLE);
    frameStoreHeightSw = MAX(frameStoreHeightSw, FRAMESTORE_HEIGHT_SW + FRAMESTORE_HEIGHT_PAD_AVS);
    frameStoreStrideSw = MAX(frameStoreStrideSw, STRIDE_ROUNDUP(FRAMESTORE_WIDTH_SW + FRAMESTORE_WIDTH_PAD_AVS));
    fwBufSizeMB = MAX(fwBufSizeMB, 2);
#else
    GENERATE_PARAM_IS_UNDEFINED_CHECK(CONFIG_AVS_ENABLE);
#endif

#if defined (CONFIG_H263FLV_ENABLE)
    GENERATE_PARAM_IS_DEFINED_CHECK(CONFIG_H263FLV_ENABLE);
    frameStoreHeightSw = MAX(frameStoreHeightSw, FRAMESTORE_HEIGHT_SW + FRAMESTORE_HEIGHT_PAD_H263);
    frameStoreStrideSw = MAX(frameStoreStrideSw, STRIDE_ROUNDUP(FRAMESTORE_WIDTH_SW + FRAMESTORE_WIDTH_PAD_H263));
    fwBufSizeMB = MAX(fwBufSizeMB, 4);
#else
    GENERATE_PARAM_IS_UNDEFINED_CHECK(CONFIG_H263FLV_ENABLE);
#endif

#if defined (CONFIG_VP6_ENABLE)
    GENERATE_PARAM_IS_DEFINED_CHECK(CONFIG_VP6_ENABLE);
    frameStoreHeightSw = MAX(frameStoreHeightSw, FRAMESTORE_HEIGHT_SW + FRAMESTORE_HEIGHT_PAD_VP6);
    frameStoreStrideSw = MAX(frameStoreStrideSw, STRIDE_ROUNDUP(FRAMESTORE_WIDTH_SW + FRAMESTORE_WIDTH_PAD_VP6));
    fwBufSizeMB = MAX(fwBufSizeMB, 5);
#else
    GENERATE_PARAM_IS_UNDEFINED_CHECK(CONFIG_VP6_ENABLE);
#endif

#if defined (CONFIG_REAL_720P_ENABLE)
    GENERATE_PARAM_IS_DEFINED_CHECK(CONFIG_REAL_720P_ENABLE);
    frameStoreHeightSw = MAX(frameStoreHeightSw, 720);
    frameStoreStrideSw = MAX(frameStoreStrideSw, STRIDE_ROUNDUP((1312*752)/1088));
    fwBufSizeMB = MAX(fwBufSizeMB, 4);
#else
    GENERATE_PARAM_IS_UNDEFINED_CHECK(CONFIG_REAL_720P_ENABLE);
#endif

    /* Use HW decoder framestore height for SW decoder framestores, if safe to do so.
       Probably not required but has been done historically (and causes no increase
       in memory usage), so keep it in for now...
    */
    frameStoreHeightSw = MAX(frameStoreHeightSw, frameStoreHeight);

    IFDEBUG(fprintf(stderr, "Starting phStbKBufMgr (%d frame stores of %dx%d)\n",
                    frameStoreNo, frameStoreWidth, frameStoreHeight));

    gphStbKBufMgr_Options.frameStoreWidth = frameStoreWidth;
    gphStbKBufMgr_Options.frameStoreStride = frameStoreStride;

    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_FW].size = fwBufSizeMB * 1024 * 1024;

    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_FSY].numberRequired = frameStoreNo;
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_FSY].size = frameStoreHeight * frameStoreStride;
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_FSUV].numberRequired = frameStoreNo;
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_FSUV].size = frameStoreHeight * frameStoreStride / 2;
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_FSY_Stop].size = frameStoreHeight * frameStoreStride;
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_FSUV_Stop].size = frameStoreHeight * frameStoreStride / 2;

    /* Note: the MSVD2 SW decoder framestores will overlay the non-SW framestores (so no increase in memory). */
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_FSY_AVS].numberRequired = frameStoreNo;
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_FSY_AVS].size = frameStoreHeightSw * frameStoreStrideSw;
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_FSUV_AVS].numberRequired = frameStoreNo;
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_FSUV_AVS].size = frameStoreHeightSw * frameStoreStrideSw / 2;

    /* The number of video decoder instances (simultaneous video streams). */
    GENERATE_PARAM_IS_DEFINED_CHECK(CONFIG_NUM_VIDEO_DECODERS);
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_VIDEO_ES].numberRequired = CONFIG_NUM_VIDEO_DECODERS;
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_VIDEO_PES_HDR].numberRequired = CONFIG_NUM_VIDEO_DECODERS;
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_VPTS].numberRequired = CONFIG_NUM_VIDEO_DECODERS;
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_UD].numberRequired = CONFIG_NUM_VIDEO_DECODERS;

    /* The number of audio decoder instances (simultaneous audio streams). */
    GENERATE_PARAM_IS_DEFINED_CHECK(CONFIG_NUM_AUDIO_DECODERS);
    /* See tmdlAudio.h for macros which select kBufMgr buffers and thus how many are required per decoder. */
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_AHeader].numberRequired   = (CONFIG_NUM_AUDIO_DECODERS + 1);
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_APTS].numberRequired      = (CONFIG_NUM_AUDIO_DECODERS + 1);
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_AUDIO_ES].numberRequired  = (CONFIG_NUM_AUDIO_DECODERS + 1) * 2;
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_AUDIO].numberRequired     = (CONFIG_NUM_AUDIO_DECODERS + 1) * 4;
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_AUDIO_OUT].numberRequired = (CONFIG_NUM_AUDIO_DECODERS + 1) * 2;

    /* Enable simultaneous HD and SD outputs? */
#if defined(CONFIG_HDSD_DOWNSCALE_ENABLE)
    GENERATE_PARAM_IS_DEFINED_CHECK(CONFIG_HDSD_DOWNSCALE_ENABLE);
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_DOWNSCALE_Y].numberRequired = 1;
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_DOWNSCALE_UV].numberRequired = 1;
#  ifdef CONFIG_HDDECODE_ENABLE
    /* Set up the downscale buffer size */
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_DOWNSCALE_Y].size = DOWNSCALE_BUFSIZE;
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_DOWNSCALE_UV].size = DOWNSCALE_BUFSIZE;
#  else
    /* Limit the Downscale / Progressive -> interlaced buffer size */
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_DOWNSCALE_Y].size = DOWNSCALE_BUFSIZE / 2;
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_DOWNSCALE_UV].size = DOWNSCALE_BUFSIZE / 2;
#  endif
#else
    GENERATE_PARAM_IS_UNDEFINED_CHECK(CONFIG_HDSD_DOWNSCALE_ENABLE);
#endif

    /* The number of software demux instances. */
    GENERATE_PARAM_IS_DEFINED_CHECK(CONFIG_NUM_SW_DEMUXES);
    /* We need 2x decoders for the buffers. 1 for main buffer and 1 for lin buffer */
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_DMXSW].numberRequired = CONFIG_NUM_SW_DEMUXES * 2;
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_DMXSW_AUD].numberRequired = CONFIG_NUM_SW_DEMUXES;
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_DMXSW_VID].numberRequired = CONFIG_NUM_SW_DEMUXES;

    /* The number of hardware demux instances which can record an SPTS. */
#if defined(CONFIG_PVR_RECORD)
    GENERATE_PARAM_IS_DEFINED_CHECK(CONFIG_PVR_RECORD);
    GENERATE_PARAM_IS_DEFINED_CHECK(CONFIG_VIDEO_SPTS_BUFSIZE);
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_VSPTS].numberRequired = 2;
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_VSPTS].size = CONFIG_VIDEO_SPTS_BUFSIZE * 1024;
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_VSPTS].alignment = gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_VSPTS].size - 1;
#else
    GENERATE_PARAM_IS_UNDEFINED_CHECK(CONFIG_PVR_RECORD);
#endif

    /* The number of hardware demux instances. */
    GENERATE_PARAM_IS_DEFINED_CHECK(CONFIG_NUM_HW_DEMUXES);
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_VSYS].numberRequired = CONFIG_NUM_HW_DEMUXES;
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_VSECT].numberRequired = CONFIG_NUM_HW_DEMUXES;
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_VFILT].numberRequired = CONFIG_NUM_HW_DEMUXES * NUM_FILTER;

    /* Video elementary stream buffer size. */
    GENERATE_PARAM_IS_DEFINED_CHECK(CONFIG_VIDEO_ES_BUFSIZE);
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_VIDEO_ES].size = CONFIG_VIDEO_ES_BUFSIZE * 1024;

    /* Audio elementary stream buffer size. */
    GENERATE_PARAM_IS_DEFINED_CHECK(CONFIG_AUDIO_ES_BUFSIZE);
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_AUDIO_ES].size = CONFIG_AUDIO_ES_BUFSIZE * 1024;

    /* Calculate the ES buffer alignment masks. The ES buffers must be aligned to
     * the next power of 2 of the size.
     *
     * e.g. size = 768 KB, alignment = 1024 KB
     *                      alignment = 0x00100000 B, mask = 0x000fffff
     */
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_VIDEO_ES].alignment =
        alignmentForNextPowerOfTwo(gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_VIDEO_ES].size);
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_AUDIO_ES].alignment =
        alignmentForNextPowerOfTwo(gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_AUDIO_ES].size);

    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_VIDEO_PES_HDR].alignment =
        gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_VIDEO_PES_HDR].size - 1;

    /*  Auxiliary demultiplexor buffer size. */
    GENERATE_PARAM_IS_DEFINED_CHECK(CONFIG_AUX_DEMUX_BUFSIZE);
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_AUX_MUX].size = CONFIG_AUX_DEMUX_BUFSIZE * 1024;

    /* Alternate DirectFB buffer size. */
    GENERATE_PARAM_IS_DEFINED_CHECK(CONFIG_ALT_DIRECTFB_BUFSIZE);
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_ALT_DIRECTFB].size = CONFIG_ALT_DIRECTFB_BUFSIZE * 1024;

    /* Video Input buffer size. */
    GENERATE_PARAM_IS_DEFINED_CHECK(CONFIG_VIDEO_INPUT_BUFSIZE);
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_VID_INPUT].size = CONFIG_VIDEO_INPUT_BUFSIZE * 1024;

    /* A region that can be used to store debug information. */
#if defined(CONFIG_VIDEO_TEST_BUFFER_SIZE)
    GENERATE_PARAM_IS_DEFINED_CHECK(CONFIG_VIDEO_TEST_BUFFER_SIZE);
    gphStbKBufMgr_Options.bufferDescs[phStbKBufMgr_Buffer_DEBUG_REGION].size = CONFIG_VIDEO_TEST_BUFFER_SIZE * 1024;
#else
    GENERATE_PARAM_IS_UNDEFINED_CHECK(CONFIG_VIDEO_TEST_BUFFER_SIZE);
#endif

    printSetup();

    placeAllBuffers();

    printSummary();
    printReport();

    validateCalculation();

    return 0;
}

/**************************************************************/
/** Print the main contents of the phStbKBufMgr_Generated.c file. */
static void printBufferDefsToGeneratedFile(void)
{
    uint buf;
    for (buf = 0; (buf < phStbKBufMgr_Buffer_Number); buf++)
    {
        uint n;
        phStbKBufMgr_BufferData_t *pBufferData = &gBufferData[buf];
        const phStbKBufMgr_BufferDesc_t *pBufferDesc = &gphStbKBufMgr_Options.bufferDescs[buf];
        const char *name = pBufferDesc->name;
        fprintf(gpOutputFP, "static phStbKBufMgr_RtBlock_t blocks_%s[%u] = {\n",
                name, pBufferDesc->numberRequired);
        for (n = 0; n < pBufferDesc->numberRequired; n++)
        {
            phStbKBufMgr_Block_t *pBlock = &pBufferData->block[n];
            fprintf(gpOutputFP, "  {0x%08x, false}%s\n",
                    pBlock->addr + KSEG1_MASK,
                    (n == pBufferDesc->numberRequired - 1) ? "" : ",");
        }
        fprintf(gpOutputFP, "};\n");
    }

    fprintf(gpOutputFP, "phStbKBufMgr_RtBufferData_t gphStbKBufMgr_RtBuffers[%u] = {\n",
            phStbKBufMgr_Buffer_Number);
    for (buf = 0; (buf < phStbKBufMgr_Buffer_Number); buf++)
    {
        const phStbKBufMgr_BufferDesc_t *pBufferDesc = &gphStbKBufMgr_Options.bufferDescs[buf];
        const char *name = pBufferDesc->name;
        fprintf(gpOutputFP, "  {%2u, 0x%06x, 0x%08x, blocks_%s, \"%s\"}%s\n",
                pBufferDesc->numberRequired,
                pBufferDesc->size,
                pBufferDesc->alignment,
                name,
                name,
                (buf == phStbKBufMgr_Buffer_Number - 1) ? "" : ",");
    }
    fprintf(gpOutputFP, "};\n");
}

/**************************************************************/
int main(int argc, char **argv)
{
    (void)argv;

    if (argc != 1)
    {
        fprintf(stderr,
                "Usage: phStbKBufMgr_Setup\n"
                "Generates phStbKBufMgr_Generated.c which contains the memory buffer map.\n");
        exit(1);
    }
    gBuildRoot = getenv("BUILDROOT");
    if (gBuildRoot == NULL)
    {
        fprintf(stderr, "phStbKBufMgr_Setup: environment variable BUILDROOT should be defined!\n");
        exit(1);
    }

    openGeneratedFile();

    doSetup();

    printBufferDefsToGeneratedFile();
    printf("$RESERVED_HIGH_MEM_SIZE = 0x%08x;\n", gReservedMemSize);
    fclose(gpOutputFP);
    fclose(gpOutputHeaderFP);
    fclose(gpSummaryFP);

    if (gFirstTime)
    {
        fprintf(stderr, "phStbKBufMgr setup; creating %s ...\n", outputFileName);
        fprintf(stderr, "See also %s\n", summaryFileName);
    }
    else
    {
        char cmd[3 * PATH_MAX];
        sprintf(cmd, "cmp -s %s %s", tmpOutputFileName, outputFileName);
        if (system(cmd) != 0)
        {
            int ret;
            fprintf(stderr, "phStbKBufMgr setup changed; updating %s ...\n", outputFileName);
            fprintf(stderr, "See also %s\n", summaryFileName);
            sprintf(cmd, "cp -f %s %s", tmpOutputFileName, outputFileName);
            ret = system(cmd);
            if (ret != 0)
            {
                fprintf(stderr, "**ERROR: command returned %d\n  %s\n", ret, cmd);
                exit(1);
            }
        }
    }

    if (gFirstTimeHeader)
    {
        fprintf(stderr, "phStbKBufMgr setup; creating %s ...\n", outputHeaderFileName);
    }
    else
    {
        char cmd[3 * PATH_MAX];
        sprintf(cmd, "cmp -s %s %s", tmpOutputHeaderFileName, outputHeaderFileName);
        if (system(cmd) != 0)
        {
            int ret;
            fprintf(stderr, "phStbKBufMgr setup changed; updating %s ...\n", outputHeaderFileName);
            sprintf(cmd, "cp -f %s %s", tmpOutputHeaderFileName, outputHeaderFileName);
            ret = system(cmd);
            if (ret != 0)
            {
                fprintf(stderr, "**ERROR: command returned %d\n  %s\n", ret, cmd);
                exit(1);
            }
        }
    }
    return 0;
}
