/*****************************************************************************/
/* 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_gfxdriver.c
    @brief  Driver init/deinit routines for DirectFB.

@b  Component:  phStbDFB.

*//*

Rev Date        Author      Comments
--------------------------------------------------------------------------------
  1 20080103    laird       New version for DirectFB 1.1.1
  2 20080716    laird       Rework.
  3 20080716    laird       Adding support for video layers.
  5 20080902    laird       Update Copyright headers.
  5.1.1 20080905 neill      Fix display mode change issues
  6 20080905    laird       Code tidy up.
  7 20080905    laird       Merged from 'phStbDFB_gfxdriver.c-6:csrc:SOUsb225#1' and 'phStbDFB_gfxdriver.c-5.1.1:csrc:SOUsb225#1' by laird.
  8 20080905    neill       Fix problems with single framebuffers
  9 20080908    neill       Reduce startup prints
 10 20080919    laird       Tidy up debug messages.
 11 20081111    steel       add stretch blit.
 12 20081119    neill       improve stretch blit initialisation.
 13 20090318    laird       Update default constructor
--------------------------------------------------------------------------------
    For consistency and standardisation retain the Section Separators.
*/

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

/*Must be included after the internal header file otherwise
  we get some lovely C Warnings.....*/
#include <core/graphics_driver.h>
/***********************************************
* LOCAL MACROS                                 *
* recommendation only: <MODULE><_WORD>+        *
************************************************/

/******************************************************************
* LOCAL TYPEDEFS                                                  *
* recommendation only:                [p]phStbTemplate_<Word>+_t  *
*******************************************************************/
DFB_GRAPHICS_DRIVER( STB225 )

D_DEBUG_DOMAIN( phStbDFB_Gfx_Driver, "NXP/STB22X/GDrv", "GFX Driver" );

/******************************************************************
* 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 DFBResult
open_fb( phStbDFB_DriverData_t *pdrv,
         phStbDFB_DeviceData_t *pdev,
         uint32_t              fbnum )
{
     int32_t                   ret;
     char_t                    buf1[16];
     char_t                    buf2[16];
     phStbDFB_FBinfo_t         *fb;

     D_ASSERT( pdrv != NULL );
     D_ASSERT( pdev != NULL );
     D_ASSERT( fbnum < D_ARRAY_SIZE(pdrv->fb) );
     D_ASSERT( fbnum < D_ARRAY_SIZE(pdev->fix) );

     fb = &pdrv->fb[fbnum];
     fb->num = fbnum;

     snprintf( buf1, sizeof(buf1), "/dev/fb%u", fbnum );
     snprintf( buf2, sizeof(buf2), "/dev/fb/%u", fbnum );

     fb->fd = direct_try_open( buf1, buf2, O_RDWR, true );
     if (fb->fd < 0)
          return DFB_INIT;

     ret = ioctl( fb->fd, FBIOGET_FSCREENINFO, &pdev->fix[fbnum] );
     if (ret) {
          D_PERROR( "NXP/STB22X/Driver: FBIOGET_FSCREENINFO (fb%d) failed!\n", fbnum );
          close( fb->fd );
          return DFB_INIT;
     }

     fb->size = pdev->fix[fbnum].smem_len;
     fb->mem = mmap( NULL, fb->size, PROT_READ | PROT_WRITE, MAP_SHARED, fb->fd, 0 );
     if (fb->mem == MAP_FAILED) {
          D_PERROR( "NXP/STB22X/Driver: mmap (fb%d, length %d) failed!\n", fbnum, fb->size );
          close( fb->fd );
          return DFB_INIT;
     }

     pdrv->num_gfx_layers++;

     D_DEBUG_AT( phStbDFB_Gfx_Driver, " -> Mapped fb%d with length %u at %p to %p\n",
                 fbnum, fb->size, (void*)pdev->fix[fbnum].smem_start, fb->mem );

     return DFB_OK;
}

/**********************************************************************************************************************/
static void
close_fb( phStbDFB_FBinfo_t *fb )
{
    if (fb->fd)
    {
        munmap( fb->mem, fb->size );
        close( fb->fd );
    }
}

/**********************************************************************************************************************/
static int
driver_probe( CoreGraphicsDevice *device )
{
     /* Check we have the correct system available.*/
     if (dfb_system_type() != CORE_DEVMEM)
     {
         return 0;
     }

     return 1;
}

/**********************************************************************************************************************/
static void
driver_get_info( CoreGraphicsDevice *device,
                 GraphicsDriverInfo *info )
{
    /* fill driver info structure */
    snprintf( info->name,
              DFB_GRAPHICS_DRIVER_INFO_NAME_LENGTH,
              "NXP Semiconductors STB22X Driver" );

    snprintf( info->vendor,
              DFB_GRAPHICS_DRIVER_INFO_VENDOR_LENGTH,
              "NXP Semiconductors" );

    info->version.major = 0;
    info->version.minor = 1;

    info->driver_data_size = sizeof(phStbDFB_DriverData_t);
    info->device_data_size = sizeof(phStbDFB_DeviceData_t);
}

/**********************************************************************************************************************/
/**
 * This is called for every instance of DirectFB that is created.
 */
static DFBResult
driver_init_driver( CoreGraphicsDevice  *device,
                    GraphicsDeviceFuncs *funcs,
                    void                *driver_data,
                    void                *device_data,
                    CoreDFB             *core )
{
     DFBResult              ret   = DFB_OK;
     phStbDFB_DriverData_t *pdrv  = driver_data;
     phStbDFB_DeviceData_t *pdev  = device_data;
     bool                   master= dfb_core_is_master( core );
     bool                   hw_accel = false;

     pdrv->pdev = pdev;
     pdrv->core = core;

     /* Initialise the number of video and graphics layers */
     pdrv->num_gfx_layers = 0;
     pdrv->num_video_layers = 2;

     /* Setup hardware initialisation. */
     hw_accel = phStbDFB_2DInit(funcs, &pdrv->pnxdraw);
     if (hw_accel)
     {
         D_INFO("DirectFB/Graphics/STB22x: Acceleration enabled!\n");
         /* Store the fact that 2D acceleration is available.*/
         pdrv->pnxdraw_present = true;
     }
     else
     {
         D_INFO("DirectFB/Graphics/STB22x: Acceleration disabled!\n");
         /* Store the fact that 2D acceleration is not available.*/
         pdrv->pnxdraw_present = false;
     }

     /* We must have at least one framebuffer.*/
     ret = open_fb( pdrv, pdev, PH_STBDFB_SCREEN_MAIN_GRAPHICS1 );
     if (ret)
     {
         D_ERROR("DirectFB/Graphics/STB22x: Failed to open /dev/fb0! error=>%s\n",
                 DirectFBErrorString( ret ) );
         return ret;
     }

     /* Register screen here by passing in all the relevent data.*/
     D_INFO("DirectFB/Graphics/STB22x: Registering Primary screen\n");
     pdrv->screens[DSCID_PRIMARY] = dfb_screens_register( device, driver_data, &phStbDFB_ScreenFuncs );
     D_INFO("DirectFB/Graphics/STB22x: Registering GFX layer 0 on Primary Screen\n");
     pdrv->layers[PH_STBDFB_SCREEN_MAIN_GRAPHICS1] = dfb_layers_register( pdrv->screens[DSCID_PRIMARY], driver_data, &phstbDFB_Gfx_LayerFuncs );

     ret = open_fb( pdrv, pdev, PH_STBDFB_SCREEN_MAIN_GRAPHICS2 );
     if (!ret)
     {
         D_INFO("DirectFB/Graphics/STB22x: Registering GFX layer 1 on Primary Screen\n");
         pdrv->layers[PH_STBDFB_SCREEN_MAIN_GRAPHICS2] = dfb_layers_register( pdrv->screens[DSCID_PRIMARY], driver_data, &phstbDFB_Gfx_LayerFuncs );
     }

     if (pdrv->num_gfx_layers == 2)
     {
         pdrv->resource_id[PH_STBDFB_SCREEN_MAIN_GRAPHICS1] = 0;
         pdrv->resource_id[PH_STBDFB_SCREEN_MAIN_GRAPHICS2] = 1;
         pdrv->resource_id[PH_STBDFB_SCREEN_MAIN_DISPLAY1]  = 2;
         pdrv->resource_id[PH_STBDFB_SCREEN_MAIN_DISPLAY2]  = 3;
         pdrv->resource_id[PH_STBDFB_SCREEN_MAIN_LCD]       = 4;
     }
     else
     {
         pdrv->resource_id[PH_STBDFB_SCREEN_MAIN_GRAPHICS1] = 0;
         /* Not present - remove from resource ID list */
         pdrv->resource_id[PH_STBDFB_SCREEN_MAIN_GRAPHICS2] = PH_STBDFB_MAX_LAYERS;
         pdrv->resource_id[PH_STBDFB_SCREEN_MAIN_DISPLAY1]  = 1;
         pdrv->resource_id[PH_STBDFB_SCREEN_MAIN_DISPLAY2]  = 2;
         pdrv->resource_id[PH_STBDFB_SCREEN_MAIN_LCD]       = 3;
     }

     D_INFO("DirectFB/Graphics/STB22x: Registering VIDEO layer 1 on Primary Screen\n");
     pdrv->layers[PH_STBDFB_SCREEN_MAIN_DISPLAY1] = dfb_layers_register( pdrv->screens[DSCID_PRIMARY], driver_data, &phStbDFB_Video_LayerFuncs );

     D_INFO("DirectFB/Graphics/STB22x: Registering VIDEO layer 2 on Primary Screen\n");
     pdrv->layers[PH_STBDFB_SCREEN_MAIN_DISPLAY2] = dfb_layers_register( pdrv->screens[DSCID_PRIMARY], driver_data, &phStbDFB_Video_LayerFuncs );

     /* If we are a second instance in a multi instance application we join not initialise.*/
     if (!master)
     {
          dfb_surface_pool_join( core, pdev->gfx_pool, &phStbDFB_Gfx_SurfacePoolFuncs );
          dfb_surface_pool_join( core, pdev->video_pool, &phStbDFB_Video_SurfacePoolFuncs );
     }

     /* Return OK otherwise we have an error.*/
     return DFB_OK;
}

/**********************************************************************************************************************/
/**
 * This is called after driver_init_driver but called only for the master instance of
 * DirectFB.
 */
static DFBResult
driver_init_device( CoreGraphicsDevice *device,
                    GraphicsDeviceInfo *device_info,
                    void               *driver_data,
                    void               *device_data )
{
     phStbDFB_DriverData_t *pdrv = driver_data;
     phStbDFB_DeviceData_t *pdev = device_data;

     /* fill device info */
     snprintf( device_info->vendor, DFB_GRAPHICS_DEVICE_INFO_VENDOR_LENGTH, "NXP Semiconductors" );
     snprintf( device_info->name,   DFB_GRAPHICS_DEVICE_INFO_NAME_LENGTH,   "STB22X" );

     phStbDFB_screen_idx = DSCID_PRIMARY;
     phStbDFB_gfx_layer_idx = PH_STBDFB_SCREEN_MAIN_GRAPHICS1;
     phStbDFB_video_layer_idx = PH_STBDFB_SCREEN_MAIN_DISPLAY1;

     /* device limitations */
     device_info->limits.surface_byteoffset_alignment = 4; //32Bit aligned
     device_info->limits.surface_bytepitch_alignment  = 8; //32Bit aligned

     /* Do we have 2D hardware available or not? */
     if (pdrv->pnxdraw_present)
     {
          device_info->caps.flags    = 0;
          device_info->caps.accel    = PH_STBDFB_SUPPORTED_DRAWINGFUNCTIONS |
                                       PH_STBDFB_SUPPORTED_BLITTINGFUNCTIONS;
          if (gphStbDFB_stretchSupported)
          {
              device_info->caps.accel |= PH_STBDFB_SUPPORTED_STRETCHFUNCTIONS;
          }
          device_info->caps.drawing  = PH_STBDFB_SUPPORTED_DRAWINGFLAGS;
          device_info->caps.blitting = PH_STBDFB_SUPPORTED_BLITTINGFLAGS;
     }
     else
     {
          device_info->caps.accel    = DFXL_NONE;
          device_info->caps.drawing  = DSDRAW_NOFX;
          device_info->caps.blitting = DSBLIT_NOFX;
     }

     /* We initialise the surface pools just once. hence it lives here.....*/
     dfb_surface_pool_initialize( pdrv->core, &phStbDFB_Gfx_SurfacePoolFuncs, &pdev->gfx_pool );
     dfb_surface_pool_initialize( pdrv->core, &phStbDFB_Video_SurfacePoolFuncs, &pdev->video_pool );

     return DFB_OK;
}

/**********************************************************************************************************************/
static void
driver_close_device( CoreGraphicsDevice *device,
                     void               *driver_data,
                     void               *device_data )
{
    phStbDFB_DeviceData_t *pdev  = device_data;

    dfb_surface_pool_destroy( pdev->video_pool );
    dfb_surface_pool_destroy( pdev->gfx_pool );
}

/**********************************************************************************************************************/
static void
driver_close_driver( CoreGraphicsDevice *device,
                     void               *driver_data )
{
     phStbDFB_DriverData_t *pdrv = driver_data;

     /* Close the 2D HW acceleration if it was used.*/
     if (pdrv->pnxdraw_present)
     {
         phStbDFB_2DDeInit( &pdrv->pnxdraw );
     }

     /* Clean up time.......*/
     close_fb( &pdrv->fb[PH_STBDFB_SCREEN_MAIN_GRAPHICS2] );
     close_fb( &pdrv->fb[PH_STBDFB_SCREEN_MAIN_GRAPHICS1] );
}

/**********************************************************************************************************************/
void phStbDFB(void)
{
    directfb_STB225_ctor();
}
