/*****************************************************************************/
/* Copyright (c) 2008 NXP B.V.                                               */
/*                                                                           */
/* This library is free software; you can redistribute it and/or             */
/* modify it under the terms of the GNU Lesser General Public                */
/* License as published by the Free Software Foundation, using               */
/* version 2.1 of the License.                                               */
/*                                                                           */
/* This library is distributed in the hope that it will be useful,           */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of            */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU          */
/* Lesser General Public License for more details.                           */
/*                                                                           */
/* You should have received a copy of the GNU Lesser General Public          */
/* License along with this library; if not, write to the Free Software       */
/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA   */
/*                                                                           */
/*****************************************************************************/
/*
   NXP STB22X Driver

   Code is derived from TI Davinci driver.

   TI Davinci driver

   (c) Copyright 2007  Telio AG

   Written by Denis Oliver Kropp <dok@directfb.org>

   Code is derived from VMWare driver.

   (c) Copyright 2001-2007  The DirectFB Organization (directfb.org)
   (c) Copyright 2000-2004  Convergence (integrated media) GmbH

   All rights reserved.

   Written by Denis Oliver Kropp <dok@directfb.org>,
              Andreas Hundt <andi@fischlustig.de>,
              Sven Neumann <neo@directfb.org>,
              Ville Syrjälä <syrjala@sci.fi> and
              Claudio Ciccani <klan@users.sf.net>.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the
   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.
*//**
    @file   phStbDFB_video_pool.c
    @brief  Implemetation of Surface Pool for Video Layers.

@b  Component:  phStbDFB.

*//*

Rev Date        Author      Comments
--------------------------------------------------------------------------------
  1 20080125    laird       New version for DirectFB 1.2
  2 20080728    laird       Adding virtual surface support.
  3 20080902    laird       Update Copyright headers.
  4 20080904    laird       Use GPU memory for Video Surfaces.
  5 20080905    laird       Making it work by using dummy video line.
  6 20080905    neill       Fix problems with single framebuffers
  7 20080917    laird       Improve debug messages.
  8 20080919    laird       Must use CSPP_DEFAULT not CSPP_PREFERED
 10 20080924    laird       For DirectFB 1.2.4 must use pitch=1 and now using CSPP_PREFERED
 11 20081014    batelaan    Use ansi structure initialiser syntax
--------------------------------------------------------------------------------
    For consistency and standardisation retain the Section Separators.
*/

/***********************************************
* INCLUDE FILES                                *
************************************************/
#include "phStbDFB_internal.h"

/***********************************************
* LOCAL MACROS                                 *
* recommendation only: <MODULE><_WORD>+        *
************************************************/

/******************************************************************
* LOCAL TYPEDEFS                                                  *
* recommendation only:                [p]phStbTemplate_<Word>+_t  *
*******************************************************************/
D_DEBUG_DOMAIN( phStbDFB_Video_Surfaces, "NXP/STB22X/VSurf", "Video Surface Pool" );
D_DEBUG_DOMAIN( phStbDFB_Video_SurfLock, "NXP/STB22X/VLock", "Video Surface Lock" );

typedef struct {
     int32_t            magic;
} StbDFB_VideoPoolData_t;

typedef struct {
     int32_t            magic;

     CoreDFB        *core;
     void           *mem;
     ulong           phys;
     void           *dummy;
} StbDFB_VideoPoolLocalData_t;

typedef struct {
     int32_t   magic;
     int32_t   addr;
     int32_t   offset;
     int32_t   pitch;
     int32_t   size;
} StbDFB_VideoAllocationData_t;

/******************************************************************
* STATIC FUNCTION PROTOTYPES                  <Module>_<Word>+    *
*******************************************************************/

/******************************************************************
* STATIC DATA                  g[k|p|kp|pk|kpk]<Module>_<Word>+   *
*******************************************************************/

/**************************************************************************
* EXPORTED DATA                     g[k|p|kp|pk|kpk]phStbTemplate_<Word>+ *
***************************************************************************/

/*******************************************************************************
* FUNCTION IMPLEMENTATION  <Module>[_<Word>+] for static functions             *
*                          tm[<screen>]<Module>[_<Word>+] for exported functions*
********************************************************************************/

/**********************************************************************************************************************/
static int
phStbDFB_VideoPoolDataSize()
{
     return sizeof(StbDFB_VideoPoolData_t);
}

/**********************************************************************************************************************/
static int
phStbDFB_VideoPoolLocalDataSize()
{
     return sizeof(StbDFB_VideoPoolLocalData_t);
}

/**********************************************************************************************************************/
static int
phStbDFB_VideoAllocationDataSize()
{
     return sizeof(StbDFB_VideoAllocationData_t);
}

/**********************************************************************************************************************/
static DFBResult
phStbDFB_VideoInitPool( CoreDFB                    *core,
                        CoreSurfacePool            *pool,
                        void                       *pool_data,
                        void                       *pool_local,
                        void                       *system_data,
                        CoreSurfacePoolDescription *ret_desc )
{
     StbDFB_VideoPoolData_t       *data  = pool_data;
     StbDFB_VideoPoolLocalData_t  *local = pool_local;

     D_DEBUG_ENTER( phStbDFB_Video_Surfaces, "" );

     D_ASSERT( core != NULL );
     D_MAGIC_ASSERT( pool, CoreSurfacePool );
     D_ASSERT( data != NULL );
     D_ASSERT( local != NULL );
     D_ASSERT( ret_desc != NULL );

     ret_desc->caps     = CSPCAPS_NONE;
     ret_desc->access   = CSAF_CPU_READ | CSAF_CPU_WRITE | CSAF_GPU_READ | CSAF_GPU_WRITE | CSAF_SHARED;
     ret_desc->types    = CSTF_LAYER | CSTF_SHARED | CSTF_EXTERNAL;
     ret_desc->priority = CSPP_PREFERED;

     snprintf( ret_desc->name, DFB_SURFACE_POOL_DESC_NAME_LENGTH, "Video Layers Pool" );

     D_MAGIC_SET( data, StbDFB_VideoPoolData_t );
     D_MAGIC_SET( local, StbDFB_VideoPoolLocalData_t );

     /* The following allows for the worst case of 1920 at 32 bit.  This dummy
        allocation allows for the system to specify EXCLUSIVE access on the layer
        which allows SetOpacity to work.
        This suggestion of a dummy line came from Dennis Kropp.*/
     local->dummy = malloc(8*1024);

     D_DEBUG_EXIT( phStbDFB_Video_Surfaces, "" );
     return DFB_OK;
}

/**********************************************************************************************************************/
static DFBResult
phStbDFB_VideoJoinPool( CoreDFB         *core,
                        CoreSurfacePool *pool,
                        void            *pool_data,
                        void            *pool_local,
                        void            *system_data )
{
     StbDFB_VideoPoolData_t       *data  = pool_data;
     StbDFB_VideoPoolLocalData_t  *local = pool_local;

     D_DEBUG_ENTER( phStbDFB_Video_Surfaces, "" );

     D_ASSERT( core != NULL );
     D_MAGIC_ASSERT( pool, CoreSurfacePool );
     D_MAGIC_ASSERT( data, StbDFB_VideoPoolData_t );
     D_ASSERT( local != NULL );

     (void) data;

     D_MAGIC_SET( local, StbDFB_VideoPoolLocalData_t );

     D_DEBUG_EXIT( phStbDFB_Video_Surfaces, "" );
     return DFB_OK;
}

/**********************************************************************************************************************/
static DFBResult
phStbDFB_VideoDestroyPool( CoreSurfacePool *pool,
                           void            *pool_data,
                           void            *pool_local )
{
     StbDFB_VideoPoolData_t      *data  = pool_data;
     StbDFB_VideoPoolLocalData_t *local = pool_local;

     D_DEBUG_ENTER( phStbDFB_Video_Surfaces, "" );

     D_MAGIC_ASSERT( pool, CoreSurfacePool );
     D_MAGIC_ASSERT( data, StbDFB_VideoPoolData_t );
     D_MAGIC_ASSERT( local, StbDFB_VideoPoolLocalData_t );

     D_MAGIC_CLEAR( data );
     D_MAGIC_CLEAR( local );

     /* Free our dummy video data.*/
     free(local->dummy);

     D_DEBUG_EXIT( phStbDFB_Video_Surfaces, "" );
     return DFB_OK;
}

/**********************************************************************************************************************/
static DFBResult
phStbDFB_VideoLeavePool( CoreSurfacePool *pool,
                         void            *pool_data,
                         void            *pool_local )
{
     StbDFB_VideoPoolData_t      *data  = pool_data;
     StbDFB_VideoPoolLocalData_t *local = pool_local;

     D_DEBUG_ENTER( phStbDFB_Video_Surfaces, "" );

     D_MAGIC_ASSERT( pool, CoreSurfacePool );
     D_MAGIC_ASSERT( data, StbDFB_VideoPoolData_t );
     D_MAGIC_ASSERT( local, StbDFB_VideoPoolLocalData_t );

     (void) data;

     D_MAGIC_CLEAR( local );

     D_DEBUG_EXIT( phStbDFB_Video_Surfaces, "" );
     return DFB_OK;
}

/**********************************************************************************************************************/
static DFBResult
phStbDFB_VideoTestConfig( CoreSurfacePool         *pool,
                          void                    *pool_data,
                          void                    *pool_local,
                          CoreSurfaceBuffer       *buffer,
                          const CoreSurfaceConfig *config )
{
     CoreSurface      *surface;
     StbDFB_VideoPoolData_t      *data  = pool_data;
     StbDFB_VideoPoolLocalData_t *local = pool_local;
     phStbDFB_DriverData_t *pdrv  = dfb_gfxcard_get_driver_data();

     D_DEBUG_ENTER( phStbDFB_Video_Surfaces, " buffer ( %p ) resource_id (%d)", buffer, buffer->surface->resource_id);

     D_MAGIC_ASSERT( pool, CoreSurfacePool );
     D_MAGIC_ASSERT( data, StbDFB_VideoPoolData_t );
     D_MAGIC_ASSERT( local, StbDFB_VideoPoolLocalData_t );
     D_MAGIC_ASSERT( buffer, CoreSurfaceBuffer );

     (void) data;
     (void) local;

     surface = buffer->surface;
     D_MAGIC_ASSERT( surface, CoreSurface );

     D_DEBUG_AT( phStbDFB_Video_Surfaces, " -> surface->type=0x%08x and surface->resource_id=%d\n",
                 surface->type,
                 surface->resource_id );

     if  ((surface->type & CSTF_LAYER) &&
         ((surface->resource_id == pdrv->resource_id[PH_STBDFB_SCREEN_MAIN_DISPLAY1]) ||
          (surface->resource_id == pdrv->resource_id[PH_STBDFB_SCREEN_MAIN_DISPLAY2])))
     {
         D_DEBUG_EXIT( phStbDFB_Video_Surfaces, " Surface can be allocated" );
         return DFB_OK;
     }

     D_DEBUG_EXIT( phStbDFB_Video_Surfaces, " Surface cannnot be allocated" );
     return DFB_UNSUPPORTED;
}

/**********************************************************************************************************************/
static DFBResult
phStbDFB_VideoAllocateBuffer( CoreSurfacePool       *pool,
                              void                  *pool_data,
                              void                  *pool_local,
                              CoreSurfaceBuffer     *buffer,
                              CoreSurfaceAllocation *allocation,
                              void                  *alloc_data )
{
     CoreSurface       *surface;
     StbDFB_VideoPoolData_t       *data  = pool_data;
     StbDFB_VideoPoolLocalData_t  *local = pool_local;
     StbDFB_VideoAllocationData_t *alloc = alloc_data;
     phStbDFB_DriverData_t *pdrv  = dfb_gfxcard_get_driver_data();

     D_DEBUG_ENTER( phStbDFB_Video_Surfaces, " buffer ( %p )", buffer );

     D_MAGIC_ASSERT( pool, CoreSurfacePool );
     D_MAGIC_ASSERT( data, StbDFB_VideoPoolData_t );
     D_MAGIC_ASSERT( local, StbDFB_VideoPoolLocalData_t );
     D_MAGIC_ASSERT( buffer, CoreSurfaceBuffer );

     (void) data;
     (void) local;

     surface = buffer->surface;
     D_MAGIC_ASSERT( surface, CoreSurface );

     if ((surface->type & CSTF_LAYER) &&
         ((surface->resource_id == pdrv->resource_id[PH_STBDFB_SCREEN_MAIN_DISPLAY1]) ||
          (surface->resource_id == pdrv->resource_id[PH_STBDFB_SCREEN_MAIN_DISPLAY2])))
     {
          D_DEBUG_AT( phStbDFB_Video_Surfaces, " -> pitch %d, addr %d\n", alloc->pitch, alloc->addr );

          D_MAGIC_SET( alloc, StbDFB_VideoAllocationData_t );

          allocation->size   = 0;
          allocation->offset = 0;

          D_DEBUG_EXIT( phStbDFB_Video_Surfaces, "" );
          return DFB_OK;
     }

     D_DEBUG_EXIT( phStbDFB_Video_Surfaces, "" );
     return DFB_BUG;
}

/**********************************************************************************************************************/
static DFBResult
phStbDFB_VideoDeallocateBuffer( CoreSurfacePool       *pool,
                                void                  *pool_data,
                                void                  *pool_local,
                                CoreSurfaceBuffer     *buffer,
                                CoreSurfaceAllocation *allocation,
                                void                  *alloc_data )
{
     StbDFB_VideoPoolData_t       *data  = pool_data;
     StbDFB_VideoAllocationData_t *alloc = alloc_data;

     D_DEBUG_ENTER( phStbDFB_Video_Surfaces, " buffer ( %p )", buffer );

     D_MAGIC_ASSERT( pool, CoreSurfacePool );
     D_MAGIC_ASSERT( data, StbDFB_VideoPoolData_t );
     D_MAGIC_ASSERT( buffer, CoreSurfaceBuffer );
     D_MAGIC_ASSERT( alloc, StbDFB_VideoAllocationData_t );

     (void) data;

     D_MAGIC_CLEAR( alloc );

     D_DEBUG_EXIT( phStbDFB_Video_Surfaces, "" );
     return DFB_OK;
}

/**********************************************************************************************************************/
static DFBResult
phStbDFB_VideoLock( CoreSurfacePool       *pool,
                    void                  *pool_data,
                    void                  *pool_local,
                    CoreSurfaceAllocation *allocation,
                    void                  *alloc_data,
                    CoreSurfaceBufferLock *lock )
{
     StbDFB_VideoPoolLocalData_t  *local = pool_local;
     StbDFB_VideoAllocationData_t *alloc = alloc_data;

     D_MAGIC_ASSERT( pool, CoreSurfacePool );
     D_MAGIC_ASSERT( allocation, CoreSurfaceAllocation );
     D_MAGIC_ASSERT( alloc, StbDFB_VideoAllocationData_t );
     D_MAGIC_ASSERT( lock, CoreSurfaceBufferLock );

     D_DEBUG_ENTER( phStbDFB_Video_SurfLock, " lock->buffer ( %p )", lock->buffer );

     /* Setting pitch to 1 to get around an assertion in CORE_SURFACE_BUFFER_LOCK_ASSERT().*/
     lock->pitch  = 1;
     /* Set the dummy pointer so that it can use SetOpacity.*/
     lock->addr   = local->dummy;
     /* Because we have indicated that we have GPU access we need to set phys address
        to something to stop it asserting in CORE_SURFACE_BUFFER_LOCK_ASSERT().
        We could have an actual phys address that is safe or use a magic value.
        I haev opted for magic value for now.*/
     lock->phys   = 0x0DFBDEAD;

     D_DEBUG_AT( phStbDFB_Video_SurfLock, "  -> offset %lu, pitch %d, phys 0x%08lx\n",
                 lock->offset, lock->pitch, lock->phys );

     D_DEBUG_EXIT( phStbDFB_Video_SurfLock, "" );
     return DFB_OK;
}

/**********************************************************************************************************************/
static DFBResult
phStbDFB_VideoUnlock( CoreSurfacePool       *pool,
                      void                  *pool_data,
                      void                  *pool_local,
                      CoreSurfaceAllocation *allocation,
                      void                  *alloc_data,
                      CoreSurfaceBufferLock *lock )
{
     StbDFB_VideoAllocationData_t *alloc = alloc_data;

     D_MAGIC_ASSERT( pool, CoreSurfacePool );
     D_MAGIC_ASSERT( allocation, CoreSurfaceAllocation );
     D_MAGIC_ASSERT( alloc, StbDFB_VideoAllocationData_t );
     D_MAGIC_ASSERT( lock, CoreSurfaceBufferLock );

     D_DEBUG_ENTER( phStbDFB_Video_SurfLock, " lock->buffer ( %p )", lock->buffer );

     (void) alloc;

     D_DEBUG_EXIT( phStbDFB_Video_SurfLock, "" );
     return DFB_OK;
}

/**********************************************************************************************************************/
const SurfacePoolFuncs phStbDFB_Video_SurfacePoolFuncs = {
     .PoolDataSize =       phStbDFB_VideoPoolDataSize,
     .PoolLocalDataSize =  phStbDFB_VideoPoolLocalDataSize,
     .AllocationDataSize = phStbDFB_VideoAllocationDataSize,

     .InitPool =           phStbDFB_VideoInitPool,
     .JoinPool =           phStbDFB_VideoJoinPool,
     .DestroyPool =        phStbDFB_VideoDestroyPool,
     .LeavePool =          phStbDFB_VideoLeavePool,

     .TestConfig =         phStbDFB_VideoTestConfig,
     .AllocateBuffer =     phStbDFB_VideoAllocateBuffer,
     .DeallocateBuffer =   phStbDFB_VideoDeallocateBuffer,

     .Lock =               phStbDFB_VideoLock,
     .Unlock =             phStbDFB_VideoUnlock
};

