/*****************************************************************************/
/* Copyright (c) 2009 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   */
/*                                                                           */
/*****************************************************************************/
/**
    @file   phStbDFB_Init.c
    @brief  Driver init/deinit routines for DirectFB.

@b  Component:  phStbDFB.

*//*

Rev Date        Author      Comments
--------------------------------------------------------------------------------
  1 20050909    charrett    Original from Denis Kropp but cleaned up
  2 20051028    laird       Port to 0.9.24
  3 20051208    laird       Reducing print verbosity
----------------------------STB225----------------------------------------------
  4 20070109    laird       Moving to STB225
  5 20070308    neill       Changes to support multiple frame buffers
  6 20070502    burningh    DirectFB 1.0 update
  7 20070904    neill       Changes to support multiple hardware versions
  8 20070912    barnish     Use M0A hardware version check
  9 20071029    burningh    Improve QA C confidence factor
 10 20080226    batelaan    Remove PNX8335 M0A option.
 11 20080421    neill       Fix usleep to be realistic(!)
 12 20081111    steel       add stretch blit.
 13 20081117    neill       fix compile problem
 14 20081119    neill       improve stretch blit initialisation.
 15 20090120    batelaan    Update file copyright/license text
--------------------------------------------------------------------------------
    For consistency and standardisation retain the Section Separators.
*/

/***********************************************
* INCLUDE FILES                                *
************************************************/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

#include <ph833xCore.h>

/*Must be included before directfb.h due to bool nonsense*/
#include <phStbDFB_Local.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <malloc.h>
#include <errno.h>

#include <directfb.h>

#include <direct/debug.h>
#include <direct/mem.h>
#include <direct/messages.h>
#include <direct/util.h>

#include <core/coredefs.h>
#include <core/coretypes.h>
#include <core/system.h>
#include <core/state.h>
#include <core/gfxcard.h>
#include <core/surfaces.h>

#include <gfx/convert.h>

/***********************************************
* LOCAL MACROS                                 *
* recommendation only: <MODULE><_WORD>+        *
************************************************/
#define PNXBLT_PSIZE_8BPP           0x00000008
#define PNXBLT_PSIZE_16BPP          0x00000010
#define PNXBLT_PSIZE_32BPP          0x00000020

#define PNXBLT_PSIZE_RGB565         0x00000000
#define PNXBLT_PSIZE_ARGB4444       0x00000100
#define PNXBLT_PSIZE_ARGB8888       0x00000000

#define PNXBLT_BLTCTL_SP            0x00001000
#define PNXBLT_BLTCTL_KEY_DST       0x00020000
#define PNXBLT_BLTCTL_KEY_SRC       0x00050000
#define PNXBLT_BLTCTL_BLEND_NORMAL  0x00100000
#define PNXBLT_BLTCTL_BLEND_PREMULT 0x00200000
#define PNXBLT_BLTCTL_UPDATE_DST_A  0x00400000
#define PNXBLT_BLTCTL_BLEND_BLACK   0x00800000

/*
 * State handling macros.
 */

#define PH_STBDFB_VALIDATE(flags)        do { pdev->v_flags |=  (flags); } while (0)
#define PH_STBDFB_INVALIDATE(flags)      do { pdev->v_flags &= ~(flags); } while (0)

#define PH_STBDFB_CHECK_VALIDATE(flag)   do {                                               \
                                             if (! (pdev->v_flags & (flag)))                \
                                               phStbDFB_validate_##flag( pdev, state );     \
                                         } while (0)

/******************************************************************
* LOCAL TYPEDEFS                                                  *
* recommendation only:                [p]phStbTemplate_<Word>+_t  *
*******************************************************************/
/*
 * State validation flags.
 *
 * There's no prefix because of the macros below.
 */
enum {
     DESTINATION = 0x00000001,
     SOURCE      = 0x00000002,
     COLOR       = 0x00000004,
     HALPHA      = 0x00000008,
     SURFALPHA   = 0x00000010,
     SRCKEY      = 0x00000020,
     DSTKEY      = 0x00000040,

     ALL         = 0x0000007F
};

/******************************************************************
* STATIC FUNCTION PROTOTYPES                  <Module>_<Word>+    *
*******************************************************************/
static DFBResult phStbDFB_EngineSync(void *drv, void *dev );
static void phStbDFB_EngineReset(void *drv, void *dev );
static void phStbDFB_EmitCommands(void *drv, void *dev );
static void phStbDFB_CheckState(void               *drv,
                                void               *dev,
                                CardState          *state,
                                DFBAccelerationMask accel );
static void phStbDFB_SetState(void                *drv,
                              void                *dev,
                              GraphicsDeviceFuncs *funcs,
                              CardState           *state,
                              DFBAccelerationMask  accel );
static bool phStbDFB_FillRectangle( void *drv, void *dev, DFBRectangle *rect );
static bool phStbDFB_Blit( void *drv, void *dev, DFBRectangle *rect, int x, int y );
static bool phStbDFB_BlitBlend( void *drv, void *dev, DFBRectangle *rect, int x, int y );
static bool phStbDFB_StretchBlit( void *drv, void *dev, DFBRectangle *srect, DFBRectangle *drect );

static inline volatile void* phStbDFB_prepare_packet(__u32 header);
static inline void phStbDFB_submit_packet(void);

static inline void phStbDFB_validate_DESTINATION( phStbDFB_DeviceData_t *pdev,
                                                  CardState             *state );
static inline void phStbDFB_validate_SOURCE( phStbDFB_DeviceData_t *pdev,
                                             CardState             *state );
static inline void phStbDFB_validate_COLOR( phStbDFB_DeviceData_t *pdev,
                                            CardState             *state );
static inline void phStbDFB_validate_HALPHA( phStbDFB_DeviceData_t *pdev,
                                             CardState             *state );
static inline void phStbDFB_validate_SURFALPHA( phStbDFB_DeviceData_t *pdev,
                                                CardState             *state );
static inline void phStbDFB_validate_SRCKEY( phStbDFB_DeviceData_t *pdev,
                                             CardState             *state );
static inline void phStbDFB_validate_DSTKEY( phStbDFB_DeviceData_t *pdev,
                                             CardState             *state );
/******************************************************************
* STATIC DATA                  g[k|p|kp|pk|kpk]<Module>_<Word>+   *
*******************************************************************/
static int                  gphStbDFB_DrawFD;
static PNXDrawSharedArea_t *gpphStbDFB_DrawShared = NULL;
static ph833xCore_hwVersion gphStbDFB_HwVersion;

static unsigned int         gDestinationBase;
static unsigned int         gDestinationStride;
static unsigned int         gSourceBase;
static unsigned int         gSourceStride;
static unsigned int         gPsize;

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

bool gphStbDFB_stretchSupported = false;

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

/*=============================================================================
| Initialisation/Deinitialisation functions                                   |
==============================================================================*/

#define STRING_SIZE (128)
static char* readDrawParam(char* param)
{
    char parameterName[STRING_SIZE];
    static char text[STRING_SIZE];
    int32_t fd;
    int32_t offset = 0;

    snprintf(parameterName, STRING_SIZE, "/sys/module/phStbDraw/parameters/%s", param);

    fd = open(parameterName, O_RDONLY);
    if (fd >= 0)
    {
        while(1)
        {
            /* read characters */
            char c;

            if ((read(fd, &c, 1) < 1) ||
                (c =='\n') ||
                (offset == (STRING_SIZE-1)))
            {
                (void)close(fd);
                break;
            }

            text[offset++] = c;
        }
    }
    text[offset] = '\0';

    return text;
}

void phStbDFB_InitHWAccel(GraphicsDeviceFuncs *funcs)
{
    /* Open Drawing Engine device. */
    gphStbDFB_DrawFD = direct_try_open( "/dev/phStbDraw", "/dev/misc/phStbDraw", O_RDWR, true );
    if (gphStbDFB_DrawFD >= 0)
    {

        /* Map shared area of device. */
        gpphStbDFB_DrawShared = mmap( NULL, PAGE_ALIGN(sizeof(PNXDrawSharedArea_t)),
                                      PROT_READ | PROT_WRITE,
                                      MAP_SHARED, gphStbDFB_DrawFD, 0 );
        if (gpphStbDFB_DrawShared == MAP_FAILED)
        {
            gpphStbDFB_DrawShared = NULL;
            close( gphStbDFB_DrawFD );
            return;
        }

        /* Get the hardware version information */
        ioctl( gphStbDFB_DrawFD, PNXDRAW_IOCTL_GET_HW_VERSION, &gphStbDFB_HwVersion );

        /* Initialize function table. */
        funcs->EngineSync    = phStbDFB_EngineSync;
        funcs->EngineReset   = phStbDFB_EngineReset;
        funcs->EmitCommands  = phStbDFB_EmitCommands;
        funcs->CheckState    = phStbDFB_CheckState;
        funcs->SetState      = phStbDFB_SetState;
        funcs->FillRectangle = phStbDFB_FillRectangle;
        funcs->Blit          = phStbDFB_Blit;
        funcs->StretchBlit   = phStbDFB_StretchBlit;

        gphStbDFB_stretchSupported = (strcmp("0", readDrawParam("scratch_memory_size")) != 0);
    }
}

void phStbDFB_DeInitHwAccel(void)
{
    if (gpphStbDFB_DrawShared)
    {
        /* Unmap shared area. */
        munmap( (void*)gpphStbDFB_DrawShared, PAGE_ALIGN(sizeof(PNXDrawSharedArea_t)) );

        /* Close Drawing Engine device. */
        close( gphStbDFB_DrawFD );
    }
}

int phStbDFB_IsHWAccelerated(void)
{
    return (gpphStbDFB_DrawShared)?1:0;
}

/*=============================================================================
| DirectFB Interface to HW Acceleration functions                             |
==============================================================================*/

static DFBResult phStbDFB_EngineSync( void *drv, void *dev )
{
    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s", __FUNCTION__);
    if (gpphStbDFB_DrawShared->state & PNXDRAW_STATE_ACTIVE)
    {
        ioctl( gphStbDFB_DrawFD, PNXDRAW_IOCTL_WAITIDLE );
    }
    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s", __FUNCTION__);

    return DFB_OK;
}

static void phStbDFB_EngineReset( void *drv, void *dev )
{
    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s", __FUNCTION__);
    ioctl(gphStbDFB_DrawFD, PNXDRAW_IOCTL_RESET );
    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s", __FUNCTION__);
}

static void phStbDFB_EmitCommands( void *drv, void *dev )
{
    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s", __FUNCTION__);
    if ((gpphStbDFB_DrawShared->readIndex != gpphStbDFB_DrawShared->writeIndex) &&
       !(gpphStbDFB_DrawShared->state & PNXDRAW_STATE_ACTIVE))
    {
        ioctl( gphStbDFB_DrawFD, PNXDRAW_IOCTL_CONTINUE );
    }
    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s", __FUNCTION__);
}

static void phStbDFB_CheckState( void                *drv,
                                 void                *dev,
                                 CardState           *state,
                                 DFBAccelerationMask  accel )
{
    DBG_PRINT((gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s: (state %p, accel 0x%08x) <- dest %p\n", __FUNCTION__,state, accel, state->destination ));

    /* Return if the desired function is not supported at all. */
    if (accel & ~(PH_STBDFB_SUPPORTED_DRAWINGFUNCTIONS | PH_STBDFB_SUPPORTED_BLITTINGFUNCTIONS | PH_STBDFB_SUPPORTED_STRETCHFUNCTIONS))
    {
        DBG_PRINT((gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s: Left at line %d", __FUNCTION__, __LINE__));
        return;
    }
    /* Return if the destination format is not supported. */
    switch (state->destination->format)
    {
        case DSPF_RGB16:
        case DSPF_ARGB4444:
        case DSPF_ARGB:
            break;
        default:
            DBG_PRINT((gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s: Left at line %d", __FUNCTION__, __LINE__));
            return;
    }

    /* Check if drawing or blitting is requested. */
    if (DFB_DRAWING_FUNCTION( accel ))
    {
        /* Return if unsupported drawing flags are set. */
        if (state->drawingflags & ~PH_STBDFB_SUPPORTED_DRAWINGFLAGS)
        {
            DBG_PRINT((gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s: Left at line %d", __FUNCTION__, __LINE__));
            return;
        }

        /* Return if blending with unsupported blend functions is requested. */
        if (state->drawingflags & DSDRAW_BLEND)
        {
            /* Check source blend function. */
            if (state->src_blend != DSBF_SRCALPHA)
            {
                DBG_PRINT((gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s: Left at line %d", __FUNCTION__, __LINE__));
                return;
            }

            /* Check destination blend function. */
            if (state->dst_blend != DSBF_INVSRCALPHA)
            {
                DBG_PRINT((gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s: Left at line %d", __FUNCTION__, __LINE__));
                return;
            }
        }
    }
    else
    {

        /* Return if unsupported blitting flags are set. */
        if (state->blittingflags & ~PH_STBDFB_SUPPORTED_BLITTINGFLAGS)
        {
            DBG_PRINT((gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s: Left at line %d", __FUNCTION__, __LINE__));
            return;
        }

        /* Return if the source format doesn't match that of the destination. */
        if (state->source->format != state->destination->format)
        {
            DBG_PRINT((gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s: Left at line %d", __FUNCTION__, __LINE__));
            return;
        }

        /* Return if blending with unsupported blend functions is requested. */
        if (state->blittingflags & (DSBLIT_BLEND_ALPHACHANNEL | DSBLIT_BLEND_COLORALPHA))
        {
            /* Check source blend function. */
            switch (state->src_blend)
            {
                case DSBF_SRCALPHA:
                    /* Not with premultiplication. */
                    if (state->blittingflags & DSBLIT_SRC_PREMULTIPLY)
                    {
                        DBG_PRINT((gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s: Left at line %d", __FUNCTION__, __LINE__));
                        return;
                    }

                case DSBF_ONE:
                    break;

                default:
                    DBG_PRINT((gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s: Left at line %d", __FUNCTION__, __LINE__));
                    return;
            }

            /* Check destination blend function. */
            if (state->dst_blend != DSBF_INVSRCALPHA)
            {
                DBG_PRINT((gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s: Left at line %d", __FUNCTION__, __LINE__));
                return;
            }
        }

        /* Return if source AND destination color keying is used. */
        if (D_FLAGS_ARE_SET( state->blittingflags, DSBLIT_SRC_COLORKEY | DSBLIT_DST_COLORKEY ))
        {
            DBG_PRINT((gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s: Left at line %d", __FUNCTION__, __LINE__));
            return;
        }

        if ((accel == DFXL_STRETCHBLIT) && !gphStbDFB_stretchSupported)
        {
            DBG_PRINT((gphStbDFB_DbgUnit, DBG_LEVEL_5, " -> No stretch scratch memory", __FUNCTION__, __LINE__));
            return;
        }
    }

    /* Enable acceleration of the function. */
    state->accel |= accel;
    DBG_PRINT((gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s: state->accel set to %x", __FUNCTION__, state->accel));
}

/*
 * Make sure that the hardware is programmed for execution of 'accel' according to the 'state'.
 */
static void phStbDFB_SetState( void                *drv,
                               void                *dev,
                               GraphicsDeviceFuncs *funcs,
                               CardState           *state,
                               DFBAccelerationMask  accel )
{
    phStbDFB_DeviceData_t          *pdev     = dev;
    StateModificationFlags  modified = state->modified;

    DBG_PRINT((gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s: (state %p, accel 0x%08x) <- dest %p, modified 0x%08x\n",
               __FUNCTION__,
               state, accel, state->destination, state->modified ));

    /*
    * 1) Invalidate hardware states
    *
    * Each modification to the hw independent state invalidates one or more hardware states.
    */

    /* Simply invalidate all? */
    if (modified == SMF_ALL)
    {
        PH_STBDFB_INVALIDATE( ALL );
    }
    else
    {
        if (modified)
        {
            /* Invalidate destination registers. */
            if (modified & SMF_DESTINATION)
            {
                PH_STBDFB_INVALIDATE( DESTINATION | COLOR | DSTKEY );
            }
            else
            {
                if (modified & SMF_DST_COLORKEY)
                {
                    PH_STBDFB_INVALIDATE( DSTKEY );
                }
            }

            /* Invalidate the color register. */
            if (modified & SMF_COLOR)
            {
                PH_STBDFB_INVALIDATE( COLOR | HALPHA | SURFALPHA );
            }
            else
            {
                if (modified & SMF_BLITTING_FLAGS)
                {
                    PH_STBDFB_INVALIDATE( SURFALPHA );
                }
            }

            /* Invalidate the source. */
            if (modified & SMF_SOURCE)
            {
                PH_STBDFB_INVALIDATE( SOURCE | SRCKEY );
            }
            else
            {
                if (modified & SMF_SRC_COLORKEY)
                {
                    PH_STBDFB_INVALIDATE( SRCKEY );
                }
            }
        }
    }

    /*
    * 2) Validate hardware states
    *
    * Each function has its own set of states that need to be validated.
    */

    /* Always requiring valid destination. */
    PH_STBDFB_CHECK_VALIDATE( DESTINATION );

    /* Depending on the function... */
    switch (accel)
    {
        case DFXL_FILLRECTANGLE:
            /* ...require valid drawing color. */
            if (state->drawingflags & DSDRAW_BLEND)
            {
                PH_STBDFB_CHECK_VALIDATE( HALPHA );
            }
            else
            {
                PH_STBDFB_CHECK_VALIDATE( COLOR );
            }

            /*
            * 3) Tell which functions can be called without further validation, i.e. SetState()
            *
            * When the hw independent state is changed, this collection is reset.
            */
            state->set |= PH_STBDFB_SUPPORTED_DRAWINGFUNCTIONS;
            break;

        case DFXL_BLIT:
            /* ...require valid source. */
            PH_STBDFB_CHECK_VALIDATE( SOURCE );

            /* Validate source or destination color key if required. */
            if (state->blittingflags & DSBLIT_SRC_COLORKEY)
            {
                PH_STBDFB_CHECK_VALIDATE( SRCKEY );
            }
            else
            {
                if (state->blittingflags & DSBLIT_DST_COLORKEY)
                {
                    PH_STBDFB_CHECK_VALIDATE( DSTKEY );
                }
            }

            /* Set blitting function depending on flags... */
            if (state->blittingflags & ~(DSBLIT_SRC_COLORKEY | DSBLIT_DST_COLORKEY))
            {
                funcs->Blit = phStbDFB_BlitBlend;

                /* ...and validate surface alpha values. */
                PH_STBDFB_CHECK_VALIDATE( SURFALPHA );
            }
            else
            {
                funcs->Blit = phStbDFB_Blit;
            }

            /*
            * 3) Tell which functions can be called without further validation, i.e. SetState()
            *
            * When the hw independent state is changed, this collection is reset.
            */
            state->set |= PH_STBDFB_SUPPORTED_BLITTINGFUNCTIONS;
            break;

        case DFXL_STRETCHBLIT:
    
            /* ...require valid source. */
            PH_STBDFB_CHECK_VALIDATE( SOURCE );
    
            /* Validate source or destination color key if required. */
            if (state->blittingflags & DSBLIT_SRC_COLORKEY)
            {
                PH_STBDFB_CHECK_VALIDATE( SRCKEY );
            }
            else
            {
                if (state->blittingflags & DSBLIT_DST_COLORKEY)
                {
                    PH_STBDFB_CHECK_VALIDATE( DSTKEY );
                }
            }
    
            /* Set blitting function depending on flags... */
            if (state->blittingflags & ~(DSBLIT_SRC_COLORKEY | DSBLIT_DST_COLORKEY))
            {
                funcs->StretchBlit = phStbDFB_StretchBlit;
    
                /* ...and validate surface alpha values. */
                PH_STBDFB_CHECK_VALIDATE( SURFALPHA );
            }
            else
            {
                funcs->StretchBlit = phStbDFB_StretchBlit;
            }
    
            /*
            * 3) Tell which functions can be called without further validation, i.e. SetState()
            *
            * When the hw independent state is changed, this collection is reset.
            */
            state->set |= PH_STBDFB_SUPPORTED_STRETCHFUNCTIONS;
            break;

        default:
            DBG_PRINT1(gphStbDFB_DbgUnit, DBG_WARNING, "%s: unexpected drawing/blitting function", __FUNCTION__);
            break;
    }

    pdev->drawingflags  = state->drawingflags;
    pdev->blittingflags = state->blittingflags;
    pdev->src_blend     = state->src_blend;

    /*
    * 4) Clear modification flags
    *
    * All flags have been evaluated in 1) and remembered for further validation.
    * If the hw independent state is not modified, this function won't get called
    * for subsequent rendering functions, unless they aren't defined by 3).
    *
    * NAB: no "0" value in enum.
    */
    state->modified = 0;
    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s", __FUNCTION__);
}

/*
 * Render a filled rectangle using the current hardware state.
 */
static bool phStbDFB_FillRectangle( void *drv, void *dev, DFBRectangle *rect )
{
    phStbDFB_DeviceData_t      *pdev = dev;
    PNXDrawExecuteFill_t       *fill;

    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s", __FUNCTION__);

    fill = phStbDFB_prepare_packet(PNXDRAW_HEADER_EXECUTE_FILL);

    if (pdev->drawingflags & DSDRAW_BLEND)
    {
        fill->bltctl = PNXBLT_BLTCTL_BLEND_NORMAL | PNXBLT_BLTCTL_UPDATE_DST_A | 0x600;
        fill->color  = pdev->HAlphaColor;
    }
    else
    {
        fill->bltctl = PNXBLT_BLTCTL_SP | 0xF0;
        fill->color  = pdev->MonoPatFColor;
    }

    fill->xy   = PNXDRAW_XY((unsigned int)rect->x,(unsigned int)rect->y);
    fill->size = PNXDRAW_WH((unsigned int)rect->w,(unsigned int)rect->h);
    phStbDFB_submit_packet();
    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s", __FUNCTION__);
    return true;
}

static bool phStbDFB_Blit( void *drv, void *dev, DFBRectangle *rect, int x, int y )
{
    phStbDFB_DeviceData_t   *pdev  = dev;
    DFBSurfaceBlittingFlags  flags = pdev->blittingflags;
    PNXDrawExecuteBlit_t    *blit;

    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s", __FUNCTION__);

    blit = phStbDFB_prepare_packet(PNXDRAW_HEADER_EXECUTE_BLIT);
    blit->bltctl = 0xCC;

    if (flags & DSBLIT_SRC_COLORKEY)
    {
        blit->bltctl |= PNXBLT_BLTCTL_KEY_SRC;
    }
    else
    {
        if (flags & DSBLIT_DST_COLORKEY)
        {
            blit->bltctl |= PNXBLT_BLTCTL_KEY_DST;
        }
    }

    blit->dstxy = PNXDRAW_XY((unsigned int)x,(unsigned int)y);
    blit->srcxy = PNXDRAW_XY((unsigned int)rect->x,(unsigned int)rect->y);
    blit->size  = PNXDRAW_WH((unsigned int)rect->w,(unsigned int)rect->h);

    phStbDFB_submit_packet();
    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s", __FUNCTION__);
    return true;
}

static bool phStbDFB_BlitBlend( void *drv, void *dev, DFBRectangle *rect, int x, int y )
{
    phStbDFB_DeviceData_t     *pdev  = dev;
    DFBSurfaceBlittingFlags    flags = pdev->blittingflags;
    PNXDrawExecuteBlitBlend_t *blit;

    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s", __FUNCTION__);

    blit = phStbDFB_prepare_packet(PNXDRAW_HEADER_EXECUTE_BLIT_BLEND);

    blit->bltctl = PNXBLT_BLTCTL_UPDATE_DST_A;

    if (flags & DSBLIT_SRC_COLORKEY)
    {
        blit->bltctl |= PNXBLT_BLTCTL_KEY_SRC;
    }
    else
    {
        if (flags & DSBLIT_DST_COLORKEY)
        {
            blit->bltctl |= PNXBLT_BLTCTL_KEY_DST;
        }

        /* Blend with the destination? */
        if (flags & (DSBLIT_BLEND_ALPHACHANNEL | DSBLIT_BLEND_COLORALPHA))
        {
            /* Premultiply source? */
            if ((flags & DSBLIT_SRC_PREMULTIPLY) || pdev->src_blend == DSBF_SRCALPHA)
            {
                blit->bltctl |= PNXBLT_BLTCTL_BLEND_NORMAL;
            }
            else
            {
                blit->bltctl |= PNXBLT_BLTCTL_BLEND_PREMULT;
            }
        }
        else
        {
            /* Blend with black, i.e. use source only. */
            blit->bltctl |= PNXBLT_BLTCTL_BLEND_BLACK;

            /* Premultiply source? */
            if (flags & DSBLIT_SRC_PREMULTIPLY)
            {
                blit->bltctl |= PNXBLT_BLTCTL_BLEND_NORMAL;
            }
            else
            {
                blit->bltctl |= PNXBLT_BLTCTL_BLEND_PREMULT;
            }
        }
    }
    blit->color = pdev->SurfAlpha;
    blit->dstxy = PNXDRAW_XY((unsigned int)x, (unsigned int)y);
    blit->srcxy = PNXDRAW_XY((unsigned int)rect->x, (unsigned int)rect->y);
    blit->size  = PNXDRAW_WH((unsigned int)rect->w, (unsigned int)rect->h);

    phStbDFB_submit_packet();
    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s", __FUNCTION__);
    return true;
}


static bool phStbDFB_StretchBlit( void *drv, void *dev, DFBRectangle *srect, DFBRectangle *drect )
{
    phStbDFB_DeviceData_t     *pdev  = dev;
    DFBSurfaceBlittingFlags    flags = pdev->blittingflags;
    PNXDrawExecuteStretchBlit_t    *blit;

    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s", __FUNCTION__);

    blit = phStbDFB_prepare_packet(PNXDRAW_HEADER_EXECUTE_STRETCH_BLIT);

    blit->bltctl = 0xCC;

    if (flags & DSBLIT_SRC_COLORKEY)
    {
        blit->bltctl |= PNXBLT_BLTCTL_KEY_SRC;
    }
    else
    {
        /* Blend with the destination? */
        if (flags & (DSBLIT_BLEND_ALPHACHANNEL | DSBLIT_BLEND_COLORALPHA))
        {
            blit->bltctl = PNXBLT_BLTCTL_UPDATE_DST_A;
            /* Premultiply source? */
            if ((flags & DSBLIT_SRC_PREMULTIPLY) || pdev->src_blend == DSBF_SRCALPHA)
            {
                blit->bltctl |= PNXBLT_BLTCTL_BLEND_NORMAL;
            }
            else
            {
                blit->bltctl |= PNXBLT_BLTCTL_BLEND_PREMULT;
            }
        }

        if (flags & DSBLIT_DST_COLORKEY)
        {
            blit->bltctl |= PNXBLT_BLTCTL_KEY_DST;
        }
    }

    blit->color   = pdev->SurfAlpha;
    blit->dstxy   = PNXDRAW_XY(drect->x, drect->y);
    blit->srcxy   = PNXDRAW_XY(srect->x, srect->y);
    blit->dstsize = PNXDRAW_WH(drect->w, drect->h);
    blit->srcsize = PNXDRAW_WH(srect->w, srect->h);

    phStbDFB_submit_packet();

    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s", __FUNCTION__);
    return true;
}

/*=============================================================================
| Ring Buffer Packet functions                                                |
==============================================================================*/
static inline unsigned int phStbDFB_nrof_packets(void)
{
    unsigned int nrofPackets;
    if (gpphStbDFB_DrawShared->writeIndex < gpphStbDFB_DrawShared->readIndex)
    {
        nrofPackets = (PNXDRAW_NUM_PACKETS - gpphStbDFB_DrawShared->readIndex) + gpphStbDFB_DrawShared->writeIndex;
    }
    else
    {
        nrofPackets = gpphStbDFB_DrawShared->writeIndex - gpphStbDFB_DrawShared->readIndex;
    }
    return nrofPackets;
}

static inline volatile void* phStbDFB_prepare_packet(__u32 header)
{
    int loop   = 100;
    PNXDrawPacket_t *packet;

    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s", __FUNCTION__);

    while (phStbDFB_nrof_packets() == PNXDRAW_NUM_PACKETS - 1)
    {
        loop --;
        if (!loop)
        {
            DBG_PRINT1(gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s: PNX8335/BLT: Timeout waiting for free packet entry, resetting!\n", __FUNCTION__);
            ioctl( gphStbDFB_DrawFD, PNXDRAW_IOCTL_RESET );
        }

        usleep( 1000 );
    }
    packet = &gpphStbDFB_DrawShared->packets[ gpphStbDFB_DrawShared->writeIndex];
    packet->header = header;

    DBG_PRINT((gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s: index=%d packet->header=%x", __FUNCTION__, gpphStbDFB_DrawShared->writeIndex, header));
    return &packet->data;
}

static inline void phStbDFB_submit_packet(void)
{
    unsigned int writeIndex = gpphStbDFB_DrawShared->writeIndex;
    DBG_PRINT((gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s: index=%d packet->header=%x",
               __FUNCTION__,
               writeIndex,
               gpphStbDFB_DrawShared->packets[ writeIndex ].header));
    /*
     * We do this with a intermediate variable so that the modules
     * doesn't at any stage see PNXDRAW_NUM_PACKETS in writeIndex
     * it will only ever see PNXDRAW_NUM_PACKETS-1 or 0
     */
    writeIndex ++;
    if (writeIndex >= PNXDRAW_NUM_PACKETS)
    {
        writeIndex = 0;
    }
    gpphStbDFB_DrawShared->writeIndex = writeIndex;

    if ((phStbDFB_nrof_packets() > (PNXDRAW_NUM_PACKETS/4)) &&
       !(gpphStbDFB_DrawShared->state & PNXDRAW_STATE_ACTIVE))
    {
        ioctl( gphStbDFB_DrawFD, PNXDRAW_IOCTL_CONTINUE );
    }
    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s", __FUNCTION__);
}

/*=============================================================================
| State Validation functions                                                  |
==============================================================================*/

static inline void phStbDFB_validate_DESTINATION( phStbDFB_DeviceData_t *pdev,
                                                  CardState             *state )
{
    CoreSurface               *surface = state->destination;
    SurfaceBuffer             *buffer  = surface->back_buffer;
    PNXDrawSetupDestination_t *dest;
    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s", __FUNCTION__);

    dest = phStbDFB_prepare_packet(PNXDRAW_HEADER_SETUP_DESTINATION);
    dest->addr   = dfb_system_video_memory_physical(surface->source_ref, (unsigned int)buffer->video.offset);
    dest->stride = (unsigned int)buffer->video.pitch;

    switch (buffer->format)
    {
        case DSPF_RGB16:
            dest->psize = PNXBLT_PSIZE_16BPP | PNXBLT_PSIZE_RGB565;
            break;

        case DSPF_ARGB4444:
            dest->psize = PNXBLT_PSIZE_16BPP | PNXBLT_PSIZE_ARGB4444;
            break;

        case DSPF_ARGB:
            dest->psize = PNXBLT_PSIZE_32BPP | PNXBLT_PSIZE_ARGB8888;
            break;

        default:
            DBG_PRINT1(gphStbDFB_DbgUnit, DBG_WARNING, "%s: unexpected pixelformat", __FUNCTION__);
    }

    gDestinationBase = dest->addr;
    gDestinationStride = dest->stride;
    gPsize = dest->psize;

    phStbDFB_submit_packet();

    /* Set the flag. */
    PH_STBDFB_VALIDATE( DESTINATION );
    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s", __FUNCTION__);
}

static inline void phStbDFB_validate_SOURCE( phStbDFB_DeviceData_t *pdev,
                                             CardState             *state )
{
    CoreSurface          *surface = state->source;
    SurfaceBuffer        *buffer  = surface->front_buffer;
    PNXDrawSetupSource_t *source;

    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s", __FUNCTION__);

    source = phStbDFB_prepare_packet(PNXDRAW_HEADER_SETUP_SOURCE);

    source->addr   = dfb_system_video_memory_physical(surface->source_ref, (unsigned int)buffer->video.offset);
    source->stride = (unsigned int)buffer->video.pitch;

    gSourceBase = source->addr;
    gSourceStride = source->stride;

    phStbDFB_submit_packet();

    /* Set the flag. */
    PH_STBDFB_VALIDATE( SOURCE );
    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s", __FUNCTION__);
}

static inline void phStbDFB_validate_COLOR( phStbDFB_DeviceData_t *pdev,
                                            CardState             *state )
{
    CoreSurface   *surface = state->destination;
    SurfaceBuffer *buffer  = surface->back_buffer;
    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s", __FUNCTION__);
    switch (buffer->format)
    {
        case DSPF_RGB16:
            pdev->MonoPatFColor = (unsigned int)PIXEL_RGB16( state->color.r,
                                                             state->color.g,
                                                             state->color.b );
            break;

        case DSPF_ARGB4444:
            pdev->MonoPatFColor = (unsigned int)PIXEL_ARGB4444( state->color.a,
                                                                state->color.r,
                                                                state->color.g,
                                                                state->color.b );
            break;

        case DSPF_ARGB:
            pdev->MonoPatFColor = PIXEL_ARGB( (unsigned int)state->color.a,
                                              (unsigned int)state->color.r,
                                              (unsigned int)state->color.g,
                                              (unsigned int)state->color.b );
            break;

        default:
            DBG_PRINT1(gphStbDFB_DbgUnit, DBG_WARNING, "%s: unexpected pixelformat", __FUNCTION__);
    }

    /* Set the flag. */
    PH_STBDFB_VALIDATE( COLOR );
    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s", __FUNCTION__);
}

static inline void phStbDFB_validate_HALPHA( phStbDFB_DeviceData_t *pdev,
                                             CardState             *state )
{
    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s", __FUNCTION__);
    pdev->HAlphaColor = PIXEL_ARGB( (unsigned int)state->color.a,
                                    (unsigned int)state->color.r,
                                    (unsigned int)state->color.g,
                                    (unsigned int)state->color.b );

    /* Set the flag. */
    PH_STBDFB_VALIDATE( HALPHA );
    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s", __FUNCTION__);
}

static inline void phStbDFB_validate_SURFALPHA( phStbDFB_DeviceData_t *pdev,
                                                CardState             *state )
{
    unsigned int r = 0xff, g = 0xff, b = 0xff, a = 0xff;

    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s", __FUNCTION__);
    if (state->blittingflags & DSBLIT_COLORIZE)
    {
        r = state->color.r;
        g = state->color.g;
        b = state->color.b;
    }

    if (state->blittingflags & DSBLIT_BLEND_COLORALPHA)
    {
        a = state->color.a;

        if ((state->src_blend == DSBF_SRCALPHA) ||
            (state->blittingflags & DSBLIT_SRC_PREMULTIPLY))
        {
            r = (r * (a + 1)) >> 8;
            g = (g * (a + 1)) >> 8;
            b = (b * (a + 1)) >> 8;
        }
    }

    pdev->SurfAlpha = PIXEL_ARGB( a, r, g, b );

    /* Set the flag. */
    PH_STBDFB_VALIDATE( SURFALPHA );
    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s", __FUNCTION__);
}

static inline void phStbDFB_validate_SRCKEY( phStbDFB_DeviceData_t *pdev,
                                             CardState             *state )
{
    CoreSurface       *surface = state->source;
    SurfaceBuffer     *buffer  = surface->front_buffer;
    PNXDrawSetupKey_t *key;

    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s", __FUNCTION__);
    key = phStbDFB_prepare_packet(PNXDRAW_HEADER_SETUP_KEY);
    key->cccolor   = state->src_colorkey;
    key->transmask = (unsigned int)((1 << DFB_COLOR_BITS_PER_PIXEL(buffer->format)) - 1);

    phStbDFB_submit_packet();

    /* Set the flag. */
    PH_STBDFB_VALIDATE( SRCKEY );

    /* DST/SRCKEY is mutual exclusive. */
    PH_STBDFB_INVALIDATE( DSTKEY );
    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s", __FUNCTION__);
}

static inline void phStbDFB_validate_DSTKEY( phStbDFB_DeviceData_t *pdev,
                                             CardState             *state )
{
    CoreSurface       *surface = state->destination;
    SurfaceBuffer     *buffer  = surface->back_buffer;
    PNXDrawSetupKey_t *key;

    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s", __FUNCTION__);

    key = phStbDFB_prepare_packet(PNXDRAW_HEADER_SETUP_KEY);
    key->cccolor   = state->dst_colorkey;
    key->transmask = (unsigned int)((1 << DFB_COLOR_BITS_PER_PIXEL(buffer->format)) - 1);

    phStbDFB_submit_packet();

    /* Set the flag. */
    PH_STBDFB_VALIDATE( DSTKEY );

    /* SRC/DSTKEY is mutual exclusive. */
    PH_STBDFB_INVALIDATE( SRCKEY );
    DBG_PRINT1(gphStbDFB_DbgUnit, DBG_LEVEL_5, "%s", __FUNCTION__);
}

