/*****************************************************************************/
/* 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_2d.c
    @brief  2D Hardware Acceleration Implementation for DirectFB.

@b  Component:  phStbDFB.

*//*

Rev Date        Author      Comments
--------------------------------------------------------------------------------
  1 20080103    laird       New version for DirectFB 1.1.1
  3 20080725    laird       Tidy up
  4 20080730    laird       Make usleep 1000 otherwise it hangs system.
  5 20080902    laird       Update Copyright headers.
  6 20080918    laird       Tidy up debug.
  7 20081111    steel       add stretch blit.
  8 20081119    neill       improve stretch blit initialisation.
  9 20081219    neill       Add support for multiple graphics formats
 10 20090106    neill       Add triangle fill and rectangle draw.
 11 20090123    steel       Add cache flushing
 12 20090202    steel       Add more cache flushing
--------------------------------------------------------------------------------
    For consistency and standardisation retain the Section Separators.
*/

/***********************************************
* INCLUDE FILES                                *
************************************************/
#include "phStbDFB_internal.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
#define PNXBLT_BLTCTL_RIGHT_TO_LEFT 0x01000000
#define PNXBLT_BLTCTL_BOTTOM_TO_TOP 0x02000000

/*
 * 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( pdrv, pdev, state ); \
                                         } while (0)

D_DEBUG_DOMAIN( phStbDFB_2D, "NXP/STB22x/2D", "NXP STB22X 2D-Acceleration" );

/******************************************************************
* 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_DrawRectangle( void *drv, void *dev, DFBRectangle *rect );
static bool phStbDFB_FillRectangle( void *drv, void *dev, DFBRectangle *rect );
static bool phStbDFB_FillTriangle( void *drv, void *dev, DFBTriangle *triangle );
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 void phStbDFB_FlushCache(void *drv, void *dev );

static inline volatile void* phStbDFB_prepare_packet(__u32 header, phStbDFB_PNXDraw_t *pnxdraw);
static inline void phStbDFB_submit_packet(phStbDFB_DriverData_t *pdrv,
                                          phStbDFB_DeviceData_t *pdev,
                                          phStbDFB_PNXDraw_t *pnxdraw);

static inline void phStbDFB_validate_DESTINATION( phStbDFB_DriverData_t *pdrv,
                                                  phStbDFB_DeviceData_t *pdev,
                                                  CardState             *state );
static inline void phStbDFB_validate_SOURCE( phStbDFB_DriverData_t *pdrv,
                                             phStbDFB_DeviceData_t *pdev,
                                             CardState             *state );
static inline void phStbDFB_validate_COLOR( phStbDFB_DriverData_t *pdrv,
                                            phStbDFB_DeviceData_t *pdev,
                                            CardState             *state );
static inline void phStbDFB_validate_HALPHA( phStbDFB_DriverData_t *pdrv,
                                             phStbDFB_DeviceData_t *pdev,
                                             CardState             *state );
static inline void phStbDFB_validate_SURFALPHA( phStbDFB_DriverData_t *pdrv,
                                                phStbDFB_DeviceData_t *pdev,
                                                CardState             *state );
static inline void phStbDFB_validate_SRCKEY( phStbDFB_DriverData_t *pdrv,
                                             phStbDFB_DeviceData_t *pdev,
                                             CardState             *state );
static inline void phStbDFB_validate_DSTKEY( phStbDFB_DriverData_t *pdrv,
                                             phStbDFB_DeviceData_t *pdev,
                                             CardState             *state );

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

/**************************************************************************
* 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;
}

bool phStbDFB_2DInit(GraphicsDeviceFuncs *funcs, phStbDFB_PNXDraw_t *pnxdraw)
{
    /* Open Drawing Engine device. */
    pnxdraw->fd = direct_try_open( "/dev/phStbDraw", "/dev/misc/phStbDraw", O_RDWR, true );
    if (pnxdraw->fd >= 0)
    {
        /* Map shared area of device. */
        pnxdraw->shared = mmap( NULL, PAGE_ALIGN(sizeof(PNXDrawSharedArea_t)),
                                PROT_READ | PROT_WRITE,
                                MAP_SHARED, pnxdraw->fd, 0 );
        if (pnxdraw->shared == MAP_FAILED)
        {
            pnxdraw->shared = NULL;
            close( pnxdraw->fd );
            return false;
        }

        /* Get the hardware version information */
        ioctl( pnxdraw->fd, PNXDRAW_IOCTL_GET_HW_VERSION, &pnxdraw->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->DrawRectangle     = phStbDFB_DrawRectangle;
        funcs->FillTriangle      = phStbDFB_FillTriangle;
        funcs->Blit              = phStbDFB_Blit;
        funcs->StretchBlit       = phStbDFB_StretchBlit;
        funcs->FlushTextureCache = phStbDFB_FlushCache;

        gphStbDFB_stretchSupported = (strcmp("0", readDrawParam("scratch_memory_size")) != 0);
    }
    else
    {
        return false;
    }
    D_MAGIC_SET( pnxdraw, phStbDFB_PNXDraw_t );
    return true;
}

void phStbDFB_2DDeInit(phStbDFB_PNXDraw_t *pnxdraw)
{
    D_MAGIC_ASSERT( pnxdraw, phStbDFB_PNXDraw_t );

    /* Unmap shared area. */
    munmap( (void*)pnxdraw->shared, PAGE_ALIGN(sizeof(PNXDrawSharedArea_t)) );

    /* Close Drawing Engine device. */
    close( pnxdraw->fd );

    D_MAGIC_CLEAR( pnxdraw );
}

bool phStbDFB_2DAvailable(phStbDFB_PNXDraw_t *pnxdraw)
{
    return (pnxdraw->shared)?true:false;
}

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

static DFBResult phStbDFB_EngineSync( void *drv, void *dev )
{
    phStbDFB_DriverData_t *pdrv = drv;
    phStbDFB_DeviceData_t *pdev = dev;

    D_DEBUG_ENTER(phStbDFB_2D, "");

    if (pdrv->pnxdraw.shared->state & PNXDRAW_STATE_ACTIVE)
    {
        ioctl( pdrv->pnxdraw.fd, PNXDRAW_IOCTL_WAITIDLE );
    }
    pdev->cacheFlushNeeded = True;

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

static void phStbDFB_EngineReset( void *drv, void *dev )
{
    phStbDFB_DriverData_t *pdrv = drv;
    phStbDFB_DeviceData_t *pdev = dev;

    D_DEBUG_ENTER(phStbDFB_2D, "");

    pdev->cacheFlushNeeded = True;
    ioctl(pdrv->pnxdraw.fd, PNXDRAW_IOCTL_RESET );

    D_DEBUG_EXIT(phStbDFB_2D, "");
}

static void phStbDFB_EmitCommands( void *drv, void *dev )
{
    phStbDFB_DriverData_t *pdrv = drv;
    phStbDFB_DeviceData_t *pdev = dev;

    D_DEBUG_ENTER(phStbDFB_2D, "");

    if ((pdrv->pnxdraw.shared->readIndex != pdrv->pnxdraw.shared->writeIndex) &&
       !(pdrv->pnxdraw.shared->state & PNXDRAW_STATE_ACTIVE))
    {
        if (pdev->cacheFlushNeeded)
        {
            pdev->cacheFlushNeeded = False;
            phStbDFB_FlushCache(pdrv, dev);
        }
        ioctl( pdrv->pnxdraw.fd, PNXDRAW_IOCTL_CONTINUE );
    }

    D_DEBUG_EXIT(phStbDFB_2D, "");
}

static void phStbDFB_CheckState( void                *drv,
                                 void                *dev,
                                 CardState           *state,
                                 DFBAccelerationMask  accel )
{
    D_DEBUG_ENTER(phStbDFB_2D, " (state %p, accel 0x%08x) <- dest %p",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))
    {
        D_DEBUG_AT(phStbDFB_2D, " -> Drawing function not supported\n");
        return;
    }
    /* Return if the destination format is not supported. */
    switch (state->destination->config.format)
    {
        case DSPF_ARGB:
        case DSPF_ARGB1555:
        case DSPF_RGB16:
        case DSPF_ARGB4444:
        case DSPF_LUT8:
            break;
        default:
            D_DEBUG_AT(phStbDFB_2D, " -> Destination pixel format not supported\n");
            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)
        {
            D_DEBUG_AT(phStbDFB_2D, " -> Drawing flags not set\n");
            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)
            {
                D_DEBUG_AT(phStbDFB_2D, " -> Source blending function != DSBF_SRCALPHA\n");
                return;
            }

            /* Check destination blend function. */
            if (state->dst_blend != DSBF_INVSRCALPHA)
            {
                D_DEBUG_AT(phStbDFB_2D, " -> Destination blending function != DSBF_INVSRCALPHA\n");
                return;
            }

            if ((state->destination->config.format != DSPF_ARGB4444) &&
                (state->destination->config.format != DSPF_ARGB))
            {
                D_DEBUG_AT(phStbDFB_2D, " -> Blending function in non-supported mode\n");
                return;
            }
        }
    }
    else
    {

        /* Return if unsupported blitting flags are set. */
        if (state->blittingflags & ~PH_STBDFB_SUPPORTED_BLITTINGFLAGS)
        {
            D_DEBUG_AT(phStbDFB_2D, " -> Unsupported blitting flags have been specified\n");
            return;
        }

        /* Return if the source format doesn't match that of the destination. */
        if (state->source->config.format != state->destination->config.format)
        {
            D_DEBUG_AT(phStbDFB_2D, " -> Format conversion (src->dest) not supported\n");
            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)
                    {
                        D_DEBUG_AT(phStbDFB_2D, " -> Can not do premultiplied source blitting\n");
                        return;
                    }

                case DSBF_ONE:
                    break;

                default:
                    D_DEBUG_AT(phStbDFB_2D, " -> Not supported\n");
                    return;
            }

            /* Check destination blend function. */
            if (state->dst_blend != DSBF_INVSRCALPHA)
            {
                D_DEBUG_AT(phStbDFB_2D, " -> Destinatino belnd function is not supported\n");
                return;
            }

            if ((state->destination->config.format != DSPF_ARGB4444) &&
                (state->destination->config.format != DSPF_ARGB))
            {
                D_DEBUG_AT(phStbDFB_2D, " -> Blending function in non-supported mode\n");
                return;
            }
        }

        /* Return if source AND destination color keying is used. */
        if (D_FLAGS_ARE_SET( state->blittingflags, DSBLIT_SRC_COLORKEY | DSBLIT_DST_COLORKEY ))
        {
            D_DEBUG_AT(phStbDFB_2D, " -> Source and Destination colour keying not supported\n");
            return;
        }

        if ((accel == DFXL_STRETCHBLIT) && !gphStbDFB_stretchSupported)
        {
            D_DEBUG_AT(phStbDFB_2D, " -> No stretch scratch memory\n");
            return;
        }
    }

    /* Enable acceleration of the function. */
    state->accel |= accel;

    D_DEBUG_EXIT(phStbDFB_2D, " state->accel set to %x", 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_DriverData_t          *pdrv = drv;
    phStbDFB_DeviceData_t          *pdev = dev;
    StateModificationFlags  modified = state->mod_hw;

    D_DEBUG_ENTER(phStbDFB_2D, " (state %p, accel 0x%08x) <- dest %p, modified 0x%08x",
                  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:
        case DFXL_DRAWRECTANGLE:
        case DFXL_FILLTRIANGLE:
            /* ...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:

            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:
            D_DEBUG_AT(phStbDFB_2D, " -> unexpected drawing/blitting function\n");
            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->mod_hw = 0;

    D_DEBUG_EXIT(phStbDFB_2D, "");
}

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

    D_DEBUG_ENTER(phStbDFB_2D, "");

    fill = phStbDFB_prepare_packet(PNXDRAW_HEADER_EXECUTE_FILL, &pdrv->pnxdraw);

    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(pdrv, pdev, &pdrv->pnxdraw);

    D_DEBUG_EXIT(phStbDFB_2D, "");
    return true;
}

/*
 * Render a filled rectangle using the current hardware state.
 */
static bool phStbDFB_DrawRectangle( void *drv, void *dev, DFBRectangle *rect )
{
    phStbDFB_DeviceData_t      *pdev = dev;
    phStbDFB_DriverData_t      *pdrv = drv;
    PNXDrawExecuteRectangle_t  *rectangle;
    D_DEBUG_ENTER(phStbDFB_2D, "");

    rectangle = phStbDFB_prepare_packet(PNXDRAW_HEADER_EXECUTE_RECTANGLE, &pdrv->pnxdraw);

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

    rectangle->xy   = PNXDRAW_XY((unsigned int)rect->x,(unsigned int)rect->y);
    rectangle->size = PNXDRAW_WH((unsigned int)rect->w,(unsigned int)rect->h);

    phStbDFB_submit_packet(pdrv, pdev, &pdrv->pnxdraw);

    D_DEBUG_EXIT(phStbDFB_2D, "");
    return true;
}

/*
 * Render a filled triangle using the current hardware state.
 */
static bool phStbDFB_FillTriangle( void *drv, void *dev, DFBTriangle *triangle )
{
    phStbDFB_DeviceData_t      *pdev = dev;
    phStbDFB_DriverData_t      *pdrv = drv;
    PNXDrawExecuteTriangle_t   *tri;

    D_DEBUG_ENTER(phStbDFB_2D, "");

    tri = phStbDFB_prepare_packet(PNXDRAW_HEADER_EXECUTE_TRIANGLE, &pdrv->pnxdraw);

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

    tri->xy[0]   = PNXDRAW_XY((unsigned int)triangle->x1,(unsigned int)triangle->y1);
    tri->xy[1]   = PNXDRAW_XY((unsigned int)triangle->x2,(unsigned int)triangle->y2);
    tri->xy[2]   = PNXDRAW_XY((unsigned int)triangle->x3,(unsigned int)triangle->y3);

    phStbDFB_submit_packet(pdrv, pdev, &pdrv->pnxdraw);

    D_DEBUG_EXIT(phStbDFB_2D, "");
    return true;
}

static bool phStbDFB_Blit( void *drv, void *dev, DFBRectangle *rect, int x, int y )
{
    phStbDFB_DeviceData_t   *pdev  = dev;
    phStbDFB_DriverData_t   *pdrv  = drv;
    DFBSurfaceBlittingFlags  flags = pdev->blittingflags;
    PNXDrawExecuteBlit_t    *blit;
    unsigned int src_x, src_y, dst_x, dst_y;

    D_DEBUG_ENTER(phStbDFB_2D, "");

    blit = phStbDFB_prepare_packet(PNXDRAW_HEADER_EXECUTE_BLIT, &pdrv->pnxdraw);
    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;
        }
    }

	// The original NXP code always blitted from the top left corner, which is fine except when
	// the source and destination rectangles are within the same surface and overlap. Then we must 
	// blit from the source corner which lies within the destination rectangle if there is one 
	// i.e. the corner that is within the source/destination overlap.
	
	// Let's always do this math, as changing the direction of blits shouldn't matter even if they 
	// aren't from the same surface
	if((rect->x >= x) && (rect->x < (x + rect->w)) && (rect->y >= y) && (rect->y < (y + rect->h)))
	{
		// Top left corner of source is within destination rectangle
		src_x = rect->x;
		src_y = rect->y;
		dst_x = x;
		dst_y = y;
	}
	else if(((rect->x + rect->w) >= x) && ((rect->x/* + rect->w*/) < x/* + rect->w*/) && (rect->y >= y) && (rect->y < (y + rect->h)))
	{
		// Top right corner of source is within destination rectangle
		src_x = rect->x + rect->w - 1;
		src_y = rect->y;
		dst_x = x + rect->w - 1;
		dst_y = y;
		blit->bltctl |= PNXBLT_BLTCTL_RIGHT_TO_LEFT;
	}
	else if(((rect->x + rect->w) >= x) && ((rect->x/* + rect->w*/) < x/* + rect->w*/) && ((rect->y + rect->h) >= y) && ((rect->y/* + rect->h*/) < y/*+ rect->h*/))
	{
		// Bottom right corner of source is within destination rectangle
		src_x = rect->x + rect->w - 1;
		src_y = rect->y + rect->h - 1;
		dst_x = x + rect->w - 1;
		dst_y = y + rect->h - 1;
		blit->bltctl |= PNXBLT_BLTCTL_RIGHT_TO_LEFT;
		blit->bltctl |= PNXBLT_BLTCTL_BOTTOM_TO_TOP;
	}
	else if((rect->x >= x) && (rect->x < (x + rect->w)) && ((rect->y + rect->h) >= y) && ((rect->y/* + rect->h*/) < y/*+ rect->h*/))
	{
		// Bottom left corner of source is within destination rectangle
		src_x = rect->x;
		src_y = rect->y + rect->h - 1;
		dst_x = x;
		dst_y = y + rect->h - 1;
		blit->bltctl |= PNXBLT_BLTCTL_BOTTOM_TO_TOP;
	}
	else
	{
		// No overlap, default to using top left corner
		src_x = rect->x;
		src_y = rect->y;
		dst_x = x;
		dst_y = y;
	}

    blit->dstxy = PNXDRAW_XY(dst_x, dst_y);
    blit->srcxy = PNXDRAW_XY(src_x, src_y);
    blit->size  = PNXDRAW_WH((unsigned int)rect->w,(unsigned int)rect->h);

    phStbDFB_submit_packet(pdrv, pdev, &pdrv->pnxdraw);

    D_DEBUG_EXIT(phStbDFB_2D, "");
    return true;
}

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

    D_DEBUG_ENTER(phStbDFB_2D, "");

    blit = phStbDFB_prepare_packet(PNXDRAW_HEADER_EXECUTE_BLIT_BLEND, &pdrv->pnxdraw);

    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(pdrv, pdev, &pdrv->pnxdraw);

    D_DEBUG_EXIT(phStbDFB_2D, "");
    return true;
}

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

    D_DEBUG_ENTER(phStbDFB_2D, "");

    blit = phStbDFB_prepare_packet(PNXDRAW_HEADER_EXECUTE_STRETCH_BLIT, &pdrv->pnxdraw);

    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(pdrv, pdev, &pdrv->pnxdraw);

    D_DEBUG_EXIT(phStbDFB_2D, "");
    return true;
}

static void phStbDFB_FlushCache( void *drv, void *dev )
{
    phStbDFB_DeviceData_t *pdev = dev;
    phStbDFB_DriverData_t *pdrv = drv;
    PNXDrawCacheFlush_t    flushData;

    D_DEBUG_ENTER(phStbDFB_2D, "");

    flushData.addr = pdev->fix[PH_STBDFB_SCREEN_MAIN_GRAPHICS1].smem_start;
    flushData.size = (pdrv->fb[PH_STBDFB_SCREEN_MAIN_GRAPHICS1].size * pdrv->num_gfx_layers) + dfb_config->video_length;
    ioctl( pdrv->pnxdraw.fd, PNXDRAW_IOCTL_CACHE_FLUSH, &flushData );

    D_DEBUG_EXIT(phStbDFB_2D, "");
}


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

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

    D_DEBUG_ENTER(phStbDFB_2D, "");

    while (phStbDFB_nrof_packets(pnxdraw) == PNXDRAW_NUM_PACKETS - 1)
    {
        loop --;
        if (!loop)
        {
            D_DEBUG_AT(phStbDFB_2D, " -> PNX8335/BLT: Timeout waiting for free packet entry, resetting!\n");
            ioctl( pnxdraw->fd, PNXDRAW_IOCTL_RESET );
        }

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

    D_DEBUG_EXIT(phStbDFB_2D, " index=%d packet->header=%x", pnxdraw->shared->writeIndex, header);
    return &packet->data;
}

static inline void phStbDFB_submit_packet(phStbDFB_DriverData_t *pdrv,
                                          phStbDFB_DeviceData_t *pdev,
                                          phStbDFB_PNXDraw_t *pnxdraw)
{
    unsigned int writeIndex = pnxdraw->shared->writeIndex;
    D_DEBUG_ENTER(phStbDFB_2D, " index=%d packet->header=%x",
                  writeIndex,
                  pnxdraw->shared->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;
    }
    pnxdraw->shared->writeIndex = writeIndex;

    if ((phStbDFB_nrof_packets(pnxdraw) > (PNXDRAW_NUM_PACKETS/4)) &&
       !(pnxdraw->shared->state & PNXDRAW_STATE_ACTIVE))
    {
        if (pdev->cacheFlushNeeded)
        {
            pdev->cacheFlushNeeded = False;
            phStbDFB_FlushCache(pdrv, pdev);
        }
        ioctl( pnxdraw->fd, PNXDRAW_IOCTL_CONTINUE );
    }

    D_DEBUG_EXIT(phStbDFB_2D, "");
}

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

static inline void phStbDFB_validate_DESTINATION( phStbDFB_DriverData_t *pdrv,
                                                  phStbDFB_DeviceData_t *pdev,
                                                  CardState             *state )
{
    CoreSurfaceBuffer             *buffer  = state->dst.buffer;
    PNXDrawSetupDestination_t *dest;
    D_DEBUG_ENTER(phStbDFB_2D, "");

    dest = phStbDFB_prepare_packet(PNXDRAW_HEADER_SETUP_DESTINATION, &pdrv->pnxdraw);
    dest->addr   = state->dst.phys;
    dest->stride = (unsigned int)state->dst.pitch;

    switch (buffer->format)
    {
        case DSPF_LUT8:
            dest->psize = PNXBLT_PSIZE_8BPP;
            break;

        case DSPF_RGB16:
            dest->psize = PNXBLT_PSIZE_16BPP | PNXBLT_PSIZE_RGB565;
            break;

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

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

        default:
            D_DEBUG_AT(phStbDFB_2D, " -> unexpected pixelformat\n");
    }

    phStbDFB_submit_packet(pdrv, pdev, &pdrv->pnxdraw);

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

    D_DEBUG_EXIT(phStbDFB_2D, "");
}

static inline void phStbDFB_validate_SOURCE( phStbDFB_DriverData_t *pdrv,
                                             phStbDFB_DeviceData_t *pdev,
                                             CardState             *state )
{
    PNXDrawSetupSource_t *source;

    D_DEBUG_ENTER(phStbDFB_2D, "");

    source = phStbDFB_prepare_packet(PNXDRAW_HEADER_SETUP_SOURCE, &pdrv->pnxdraw);

    source->addr   = state->src.phys;
    source->stride = (unsigned int)state->src.pitch;

    phStbDFB_submit_packet(pdrv, pdev, &pdrv->pnxdraw);

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

    D_DEBUG_EXIT(phStbDFB_2D, "");
}

static inline void phStbDFB_validate_COLOR( phStbDFB_DriverData_t *pdrv,
                                            phStbDFB_DeviceData_t *pdev,
                                            CardState             *state )
{
    CoreSurfaceBuffer *buffer  = state->dst.buffer;

    D_DEBUG_ENTER(phStbDFB_2D, "");

    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_ARGB1555:
            pdev->MonoPatFColor = (unsigned int)PIXEL_ARGB1555( 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;

       case DSPF_LUT8:
            pdev->MonoPatFColor = state->color_index;
            break;


        default:
            D_DEBUG_AT(phStbDFB_2D, " -> unexpected pixelformat\n");
    }

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

    D_DEBUG_EXIT(phStbDFB_2D, "");
}

static inline void phStbDFB_validate_HALPHA( phStbDFB_DriverData_t *pdrv,
                                             phStbDFB_DeviceData_t *pdev,
                                             CardState             *state )
{
    D_DEBUG_ENTER(phStbDFB_2D, "");

    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 );

    D_DEBUG_EXIT(phStbDFB_2D, "");
}

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

    D_DEBUG_ENTER(phStbDFB_2D, "");

    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 );

    D_DEBUG_EXIT(phStbDFB_2D, "");
}

static inline void phStbDFB_validate_SRCKEY( phStbDFB_DriverData_t *pdrv,
                                             phStbDFB_DeviceData_t *pdev,
                                             CardState             *state )
{
    CoreSurfaceBuffer     *buffer  = state->src.buffer;
    PNXDrawSetupKey_t *key;

    D_DEBUG_ENTER(phStbDFB_2D, "");

    key = phStbDFB_prepare_packet(PNXDRAW_HEADER_SETUP_KEY, &pdrv->pnxdraw);
    key->cccolor   = state->src_colorkey;
    key->transmask = (unsigned int)((1 << DFB_COLOR_BITS_PER_PIXEL(buffer->format)) - 1);

    phStbDFB_submit_packet(pdrv, pdev, &pdrv->pnxdraw);

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

    /* DST/SRCKEY is mutual exclusive. */
    PH_STBDFB_INVALIDATE( DSTKEY );

    D_DEBUG_EXIT(phStbDFB_2D, "");
}

static inline void phStbDFB_validate_DSTKEY( phStbDFB_DriverData_t *pdrv,
                                             phStbDFB_DeviceData_t *pdev,
                                             CardState             *state )
{
    CoreSurfaceBuffer     *buffer  = state->dst.buffer;
    PNXDrawSetupKey_t *key;

    D_DEBUG_ENTER(phStbDFB_2D, "");
    key = phStbDFB_prepare_packet(PNXDRAW_HEADER_SETUP_KEY, &pdrv->pnxdraw);
    key->cccolor   = state->dst_colorkey;
    key->transmask = (unsigned int)((1 << DFB_COLOR_BITS_PER_PIXEL(buffer->format)) - 1);

    phStbDFB_submit_packet(pdrv, pdev, &pdrv->pnxdraw);

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

    /* SRC/DSTKEY is mutual exclusive. */
    PH_STBDFB_INVALIDATE( SRCKEY );

    D_DEBUG_EXIT(phStbDFB_2D, "");
}

