/*****************************************************************************/
/* 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.c
    @brief  Implementation of Video Layers in DirectFB.

@b  Component:  phStbDFB.

*//*

Rev Date        Author      Comments
--------------------------------------------------------------------------------
  1 20080125    laird       New version for DirectFB 1.2
  2 20080728    laird       Supporting Virtual surfaces on video layers.
  3 20080902    laird       Update Copyright headers.
  4 20080903    laird       Allow for no windowstack.
  5 20080904    laird       More tidying up.
  5.1.1 20080905 neill      Fix display mode change issues
  6 20080904    laird       More tidying up.
  7 20080905    laird       Merged from 'phStbDFB_video.c-6:csrc:SOUsb225#1' and 'phStbDFB_video.c-5.1.1:csrc:SOUsb225#1' by laird.
  8 20080919    laird       Tidy up debug messages.
  9 20081014    batelaan    Use ansi structure initialiser syntax
 10 20081103    neill       Remove assertion check after set src rectangle
--------------------------------------------------------------------------------
    For consistency and standardisation retain the Section Separators.
*/

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

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

/******************************************************************
* LOCAL TYPEDEFS                                                  *
* recommendation only:                [p]phStbTemplate_<Word>+_t  *
*******************************************************************/
D_DEBUG_DOMAIN( phStbDFB_Video, "NXP/STB22X/Video", "NXP 22X Video Layers" );

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

/******************************************************************
* STATIC DATA                  g[k|p|kp|pk|kpk]<Module>_<Word>+   *
*******************************************************************/
/**
 * @brief This is used to indicate what layer is being initialised.
 *        Once InitLayer has been used we can work out the layer number
 *        using dfb_layer_at and then mapping to our layer IDS in our
 *        hardware.  however before this we have to do it by hand.
 */
uint32_t phStbDFB_video_layer_idx = PH_STBDFB_SCREEN_MAIN_DISPLAY1;

/**************************************************************************
* 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_Video_LayerDataSize()
{
     return sizeof(phStbDFB_LayerData_t);
}

/**********************************************************************************************************************/
static DFBResult
phStbDFB_Video_InitLayer( CoreLayer                  *layer,
                          void                       *driver_data,
                          void                       *layer_data,
                          DFBDisplayLayerDescription *description,
                          DFBDisplayLayerConfig      *config,
                          DFBColorAdjustment         *adjustment )
{
     int32_t                 ret = DFB_INIT;
     phStbDFB_LayerData_t  *pgfx = layer_data;
     tmErrorCode_t           err = TM_OK;
     phStbVideoTypes_PixelFormat_t pixelFormat;
     uint32_t  renderer_layer_id = phStbVideoRenderer_Layers_Display1;

     D_DEBUG_ENTER( phStbDFB_Video, "");

     /*  We base what layer we are on the number of times we have been called.
         i.e first time we are called we use phStbDFB_video_layer_idx of PH_STBDFB_SCREEN_MAIN_DISPLAY1(2).
         When we set this layer up we increment the value so next time it will use PH_STBDFB_SCREEN_MAIN_DISPLAY2(3).
         This means all the arrays in side the driver are ordered the same way.
         However the hardware orders them differently, so we map from one to the other here*/
     if(phStbDFB_video_layer_idx == PH_STBDFB_SCREEN_MAIN_DISPLAY1)
     {
         renderer_layer_id = phStbVideoRenderer_Layers_Display1;
         snprintf(description->name, DFB_DISPLAY_LAYER_DESC_NAME_LENGTH, "Primary Vid Layer [tmdlVss1]");
     }
     else if (phStbDFB_video_layer_idx == PH_STBDFB_SCREEN_MAIN_DISPLAY2)
     {
         renderer_layer_id = phStbVideoRenderer_Layers_Display2;
         snprintf(description->name, DFB_DISPLAY_LAYER_DESC_NAME_LENGTH, "Secondary Vid Layer [tmdlVss2]");
     }
     else
     {
         D_BUG("A video layer is being registered and is not a supported video layer.\n");
         ret = DFB_INIT;
         goto RETURN;
     }

     /*Get the layer handle and name and store it in the phStbDFB_LayerData_t.*/
     err = phStbVideoRenderer_GetVideoLayer( renderer_layer_id,
                                             &pgfx->pLayer,
                                             pgfx->name);
     if (err != 0)
     {
         D_DEBUG_AT( phStbDFB_Video, " -> phStbVideoRenderer_GetVideoLayer failed => err=0x%x\n", err );
         D_ASSERT(err == 0);
         ret = DFB_INIT;
         goto RETURN;
     }
     D_DEBUG_AT(phStbDFB_Video, " -> DirectFB LayerId=%u (Renderer Layer ID %u) is called '%s  '\n", phStbDFB_video_layer_idx, renderer_layer_id, pgfx->name );
     D_DEBUG_AT(phStbDFB_Video, " -> Have hardcoded Framebuffer to UYVY\n");
     pixelFormat = phStbVideoTypes_PixelFormatUYVY;

     /*set the pixel format hardcoded for now*/
     config->pixelformat = CONVERT_TO_DFB_PIXELFORMAT(pixelFormat);
     config->buffermode  = DLBM_FRONTONLY;

     /*This surface is the video surface*/
     config->flags        = DLCONF_WIDTH   |
                            DLCONF_HEIGHT  |
                            DLCONF_BUFFERMODE |
                            DLCONF_OPTIONS;

     /* Width and height is whatever we started up in......*/
     TM_TO_DFB_RETURN_ON_ERR(phStbVideoRenderer_GetOutputResolution(phStbVideoRenderer_DisplayChain_Main,
                                                                    &config->width,
                                                                    &config->height),
                             "Error calling phStbVideoRenderer_GetOutputResolution 0x%08x\n", err);

     D_DEBUG_AT(phStbDFB_Video, " -> config->width %d, config->height %d\n", config->width, config->height);

     /* Used to enable some capabilities: */
     config->options     = PH_STBDFB_DEFAULT_DLOP_SUPPORTED;

     /*Need to override the defaults and support size*/
     description->type = DLTF_VIDEO;

     /* set capabilities and type */
     description->caps = DLCAPS_NONE
                       | DLCAPS_OPACITY
                       | DLCAPS_SURFACE
					   | DLCAPS_DEINTERLACING
                       | DLCAPS_BRIGHTNESS
                       | DLCAPS_CONTRAST
                       | DLCAPS_SATURATION
                       | DLCAPS_LEVELS
                       | DLCAPS_SCREEN_POSITION
                       | DLCAPS_SCREEN_SIZE
                       | DLCAPS_SCREEN_LOCATION
                       | DLCAPS_NOWINDOWSTACK;

     /*
      *  fill out default color adjustment,
      *  only fields set in flags will be accepted from applications
      */
     adjustment->flags      = DCAF_BRIGHTNESS | DCAF_CONTRAST | DCAF_SATURATION;

     /*Set defaults to mid range of UInt16*/
     adjustment->brightness = 0x8000;
     adjustment->contrast   = 0x8000;
     adjustment->saturation = 0x8000;

     /*Increment our layer pointer.*/
     phStbDFB_video_layer_idx++;

     /* Set the return value.*/
     ret = DFB_OK;

 RETURN:
     D_DEBUG_EXIT( phStbDFB_Video, "");
     return ret;
}

/******************************************************************************/
DFBResult phStbDFB_Video_GetLevel(CoreLayer              *layer,
                                  void                   *driver_data,
                                  void                   *layer_data,
                                  int                    *level )
{
    DFBResult result = DFB_OK;
    phStbDFB_LayerData_t *pLayerData = (phStbDFB_LayerData_t*) driver_data;

    D_DEBUG_ENTER( phStbDFB_Video, " layer='%s'", pLayerData->name);

    *level = phStbDFB_GetZOrder(layer);

    D_DEBUG_EXIT( phStbDFB_Video, " ZOrder Position is %d result => 0x%08x", *level, result);
    return result;
}

/******************************************************************************/
DFBResult phStbDFB_Video_SetLevel(CoreLayer              *layer,
                                  void                   *driver_data,
                                  void                   *layer_data,
                                  int                     level )
{
    DFBResult result = DFB_OK;
    phStbDFB_LayerData_t *pLayerData = (phStbDFB_LayerData_t*) layer_data;

    D_DEBUG_ENTER( phStbDFB_Video, " layer='%s' layer %d Z - Order to %d", pLayerData->name, layer->shared->layer_id, level);

    if ((level > phStbVideoRenderer_Planes_Bottom) || (level < phStbVideoRenderer_Planes_Top))
    {
        result = DFB_INVARG;
    }
    else
    {
        result = phStbDFB_SetZOrder(layer, level);
    }

    D_DEBUG_EXIT( phStbDFB_Video, " result => 0x%08x", result);
    return result;
}

/******************************************************************************/
DFBResult phStbDFB_Video_SetColorAdjustment(CoreLayer          *layer,
                                      void               *driver_data,
                                      void               *layer_data,
                                      DFBColorAdjustment *adj)
{
    phStbDFB_LayerData_t       *pLayerData = (phStbDFB_LayerData_t*) layer_data;
    phStbVideoRenderer_Layer_t *pLayer     = pLayerData->pLayer;
    tmErrorCode_t err;
    DFBResult result = DFB_OK;

    D_DEBUG_ENTER( phStbDFB_Video, " layer='%s'", pLayerData->name);

    if (adj->flags & DCAF_BRIGHTNESS)
    {
        result = DFB_OK;
        /* Not currently working a known bug in Renderer Code */
        err = phStbVideoRenderer_Layer_SetBrightness(pLayer,
                                                     adj->brightness >> DFB2VIDEORENDERER_BRIGHTNESS_ADJ_SHIFT);
        if (err != 0)
        {
            D_ASSERT(err == 0);
            result = DFB_FAILURE;
        }
    }

    if (adj->flags & DCAF_CONTRAST)
    {
        err = phStbVideoRenderer_Layer_SetContrast(pLayer,
                                                   adj->contrast >> DFB2VIDEORENDERER_COLOR_ADJ_SHIFT);
        if (err != 0)
        {
            D_ASSERT(err == 0);
            result = DFB_FAILURE;
        }
    }

    if (adj->flags & DCAF_HUE)
    {
        err = phStbVideoRenderer_Layer_SetHue(pLayer,
                                              adj->hue >> DFB2VIDEORENDERER_COLOR_ADJ_SHIFT);
        if (err != 0)
        {
            D_ASSERT(err == 0);
            result = DFB_FAILURE;
        }
    }

    if (adj->flags & DCAF_SATURATION)
    {
        err = phStbVideoRenderer_Layer_SetSaturation(pLayer,
                                                     adj->saturation >> DFB2VIDEORENDERER_COLOR_ADJ_SHIFT);
        if (err != 0)
        {
            D_ASSERT(err == 0);
            result = DFB_FAILURE;
        }
    }

    D_DEBUG_EXIT( phStbDFB_Video, " result => 0x%08x", result);
    return result;
}

/**********************************************************************************************************************/
static DFBResult
phStbDFB_Video_TestRegion( CoreLayer                  *layer,
                           void                       *driver_data,
                           void                       *layer_data,
                           CoreLayerRegionConfig      *config,
                           CoreLayerRegionConfigFlags *failed )
{
     CoreLayerRegionConfigFlags fail = 0;
     DFBResult result = DFB_OK;

     D_DEBUG_ENTER( phStbDFB_Video, " config->options=0x%08x", config->options);

     if (config->options & ~PH_STBDFB_DLOP_SUPPORTED)
     {
          D_DEBUG_AT(phStbDFB_Video, " -> You have specified incorrect config->options (0x%08x) - only support (0x%08x)\n",
                     config->options,
                     PH_STBDFB_DLOP_SUPPORTED);
          fail |= CLRCF_OPTIONS;
     }

     if((config->options & DLCONF_WIDTH) ||
        (config->options & DLCONF_HEIGHT))
     {
         CoreScreen *pScreen = layer->screen;
         int screenWidth;
         int screenHeight;

         pScreen->funcs->GetScreenSize(NULL, NULL, NULL, &screenWidth, &screenHeight);

         /* Minimum size 16 pixels
            config->width config->height
            config->source.x,
            config->source.y,
            config->source.x + config->source.w,
            config->source.y + config->source.h);
         */
         if ((config->width < PH_STBVIDEORENDERER_LAYER_MIN_RECT_SIZE) ||
             (config->source.x < 0) ||
             (config->source.w < PH_STBVIDEORENDERER_LAYER_MIN_RECT_SIZE ) ||
             (config->source.x % 2 ) ||
             ((config->source.x + config->source.w) % 2 ))
         {
             D_DEBUG_AT(phStbDFB_Video, " -> Error specifying width (%d) or source rectangle(X=%d,W=%d)\n",
                        config->width,
                        config->source.x,
                        config->source.w );
             fail |= (CLRCF_WIDTH | CLRCF_SOURCE);
         }

         if ((config->height < PH_STBVIDEORENDERER_LAYER_MIN_RECT_SIZE) ||
             (config->source.y < 0 ) ||
             (config->source.h < PH_STBVIDEORENDERER_LAYER_MIN_RECT_SIZE ) ||
             (config->source.y % 2 ) ||
             ((config->source.y + config->source.h) % 2 ))
         {
             D_DEBUG_AT(phStbDFB_Video, " -> Error specifying width (%d) or source rectangle(Y=%d,H=%d)\n",
                        config->height,
                        config->source.y,
                        config->source.h );
             fail |= (CLRCF_HEIGHT | CLRCF_SOURCE);
         }
         /* Minimum size 16 pixels
            config->dest.x,
            config->dest.y,
            config->dest.x + config->dest.w,
            config->dest.y + config->dest.h);
          */
         if ((config->width < PH_STBVIDEORENDERER_LAYER_MIN_RECT_SIZE) ||
             (config->dest.x < 0 ) ||
             (config->dest.w < PH_STBVIDEORENDERER_LAYER_MIN_RECT_SIZE ) ||
             (config->dest.x % 2 ) ||
             ((config->dest.x + config->dest.w) % 2 ) ||
             ((config->dest.x + config->dest.w) > screenWidth))
         {
             D_DEBUG_AT(phStbDFB_Video, " -> Error specifying width (%d) or dest rectangle(X=%d,W=%d) screenWidth (%d)\n",
                        config->width,
                        config->dest.x,
                        config->dest.w,
                        screenHeight );
             fail |= (CLRCF_WIDTH | CLRCF_DEST);
         }

         if ((config->height < PH_STBVIDEORENDERER_LAYER_MIN_RECT_SIZE) ||
             (config->dest.y < 0 ) ||
             (config->dest.h < PH_STBVIDEORENDERER_LAYER_MIN_RECT_SIZE ) ||
             (config->dest.y % 2 ) ||
             ((config->dest.y + config->dest.h) % 2 ) ||
             ((config->dest.y + config->dest.h) > screenHeight))
         {
             D_DEBUG_AT(phStbDFB_Video, " -> Error specifying width (%d) or dest rectangle(X=%d,W=%d) screenWidth (%d)\n",
                        config->height,
                        config->dest.y,
                        config->dest.h,
                        screenHeight );
             fail |= (CLRCF_HEIGHT | CLRCF_DEST);
         }
     }

     /* return failure if any field failed */
     if (fail)
     {
         if(failed)
         {
             *failed = fail;
         }
         result = DFB_UNSUPPORTED;
     }

     D_DEBUG_EXIT( phStbDFB_Video, " result=0x%08x", result);
     return result;
}

/**********************************************************************************************************************/
static DFBResult
phStbDFB_Video_SetRegion( CoreLayer                  *layer,
                          void                       *driver_data,
                          void                       *layer_data,
                          void                       *region_data,
                          CoreLayerRegionConfig      *config,
                          CoreLayerRegionConfigFlags  updated,
                          CoreSurface                *surface,
                          CorePalette                *palette,
                          CoreSurfaceBufferLock      *lock )
{
     phStbDFB_LayerData_t         *pgfx = layer_data;
     phStbVideoRenderer_Layer_t *pLayer = pgfx->pLayer;
     DFBResult result = DFB_OK;
     tmErrorCode_t err = TM_OK;

     D_DEBUG_ENTER( phStbDFB_Video, " updated flags=0x%08x", updated );

     D_ASSERT( pgfx != NULL );

     if(updated & CLRCF_OPACITY)
     {
         if(config->opacity == 0)
         {
             /* Hide layer */
             err = phStbVideoRenderer_Layer_BlankOutput(pLayer, true, false);
             D_ASSERT(err == 0);
         }
         else
         {
             /* Show layer. */
             err = phStbVideoRenderer_Layer_BlankOutput(pLayer, false, false);
             D_ASSERT(err == 0);

             err = phStbVideoRenderer_Layer_SetAlphaLevel(pLayer, config->opacity);
             if (err != 0)
             {
                 D_ASSERT(err == 0);
                 result = DFB_FAILURE;
             }
         }
     }

     if(updated & CLRCF_SOURCE)
     {
         err = phStbVideoRenderer_Layer_SetSrcRectangle(
             pLayer,
             (uint32_t)config->source.x,
             (uint32_t)config->source.y,
             (uint32_t)(config->source.x + config->source.w),
             (uint32_t)(config->source.y + config->source.h),
             true);
         if (err != 0)
         {
             result = DFB_FAILURE;
         }
     }

     if(updated & (CLRCF_WIDTH | CLRCF_HEIGHT | CLRCF_DEST))
     {
         err = phStbVideoRenderer_Layer_SetDestRectangle(
             pLayer,
             (uint32_t)config->dest.x,
             (uint32_t)config->dest.y,
             (uint32_t)(config->dest.x + config->dest.w),
             (uint32_t)(config->dest.y + config->dest.h));
         if (err != 0)
         {
             D_ASSERT(err == 0);
             result = DFB_FAILURE;
         }
     }

     D_DEBUG_EXIT( phStbDFB_Video, " result => 0x%08x", result);
     return result;
}

const DisplayLayerFuncs phStbDFB_Video_LayerFuncs =
{
     .LayerDataSize =      phStbDFB_Video_LayerDataSize,
     .InitLayer =          phStbDFB_Video_InitLayer,

     .GetLevel =           phStbDFB_Video_GetLevel,
     .SetLevel =           phStbDFB_Video_SetLevel,
     .SetColorAdjustment = phStbDFB_Video_SetColorAdjustment,

     .TestRegion =         phStbDFB_Video_TestRegion,
     .SetRegion =          phStbDFB_Video_SetRegion
};

