/*****************************************************************************/
/* 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_utils.c
    @brief  Utilities for DirectFB implementation on NXP STB22X.

@b  Component:  phStbDFB.

*//*

Rev Date        Author      Comments
--------------------------------------------------------------------------------
  1 20080103    laird       New version for DirectFB 1.1.1
  2 20080902    laird       Update Copyright headers.
  3 20080904    laird       Add common Z Order functions.
  4 20080905    laird       Add another Zorder function.
  5 20080905    laird       Tidy up debug prints.
  6 20081023    neill       Correct Z ordering for single framebuffers
  7 20090226    boudy       Add support for UDA3000
--------------------------------------------------------------------------------
    For consistency and standardisation retain the Section Separators.
*/

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

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

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

/******************************************************************
* STATIC FUNCTION PROTOTYPES                  <Module>_<Word>+    *
*******************************************************************/
static phStbDFB_VdencMap_t* findFreeVdenc(phStbDFB_ScreenData_t *screen_data);

static phStbDFB_VdencMap_t* findVdenc(phStbDFB_ScreenData_t *screen_data,
                                      uint32_t renderer,
                                      uint32_t encoder);

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

/**
 * @brief Device name to control the AK470X.
 */
static const char_t *pAvSwitchDevNameAk470x  = "/dev/ak470x";
static const char_t *pAvSwitchDevNameUda3000 = "/dev/uda3000";

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

/*******************************************************************************
* FUNCTION IMPLEMENTATION  <Module>[_<Word>+] for static functions             *
*                          tm[<screen>]<Module>[_<Word>+] for exported functions*
********************************************************************************/
/**
 * Find a empty structure.
 *
 * @return phStbDFB_VdencMap_t if empty one found or NULL if full.
 */
static phStbDFB_VdencMap_t* findFreeVdenc(phStbDFB_ScreenData_t *screen_data)
{
    uint32_t i = 0;
    for(i = 0; i < PH_STBDFB_MAX_ENCODERS_RENDERER; i++)
    {
        if(screen_data->vdencMapping[i].inUse == false)
        {
            return &screen_data->vdencMapping[i];
        }
    }
    return NULL;
}

/**
 * Find a structure that is inuse and matches the renderer and encoder.
 *
 *  @param renderer IN the renderer number to match against.
 *  @param encoder  IN the encoder number to match against.
 *
 *  @return phStbDFB_VdencMap_t if matched or NULL.
 */
static phStbDFB_VdencMap_t* findVdenc(phStbDFB_ScreenData_t *screen_data,
                                      uint32_t renderer,
                                      uint32_t encoder)
{
    uint32_t i = 0;

    for(i = 0; i < PH_STBDFB_MAX_ENCODERS_RENDERER; i++)
    {
        if(screen_data->vdencMapping[i].renderer == renderer &&
           screen_data->vdencMapping[i].encoder == encoder &&
           screen_data->vdencMapping[i].inUse == true)
        {
            return &screen_data->vdencMapping[i];
        }
    }
    return NULL;
}

/******************************************************************************/
extern uint32_t phStbDFB_dfb_to_renderer_translated(CoreLayer* layer)
{
    uint32_t renderer_layer_id = phStbVideoRenderer_Layers_None;

    D_DEBUG_ENTER( phStbDFB_Utils, "");

    switch (dfb_layer_id(layer))
    {
        case 0:
            renderer_layer_id = phStbVideoRenderer_Layers_Graphics1;
            break;

        case 1:
            renderer_layer_id = phStbVideoRenderer_Layers_Graphics2;
            break;

        case 2:
            renderer_layer_id = phStbVideoRenderer_Layers_Display1;
            break;

        case 3:
            renderer_layer_id = phStbVideoRenderer_Layers_Display2;
            break;

        default:
            renderer_layer_id = phStbVideoRenderer_Layers_None;
    }
    D_DEBUG_EXIT( phStbDFB_Utils, " renderer_layer_id = %d");
    return renderer_layer_id;
}

/******************************************************************************/
extern uint32_t phStbDFB_GetNumberVideoDencs(uint32_t rendererNum)
{
    char_t vdencName[32];
    int32_t tmpVdenc;
    uint32_t numEncoders = 0;
    uint32_t i;

    D_DEBUG_ENTER( phStbDFB_Utils, "");

    for(i = 0; i < VIDEODENC_MAX; i++)
    {
        sprintf(vdencName, "/dev/vdenc/renderer%d/vdenc%d", rendererNum, i);
        if ((tmpVdenc = open(vdencName, O_RDWR)) < 0)
        {
            /*As vdencs should be sequential break out of loop*/
            break;
        }
        else
        {
            numEncoders++;
            close(tmpVdenc);
        }
    }
    if(numEncoders > PH_STBDFB_MAX_ENCODERS_RENDERER)
    {
        D_WARN("You have more than %d Video Dencs on Renderer %d this is not supported\n", PH_STBDFB_MAX_ENCODERS_RENDERER, rendererNum);
    }
    D_DEBUG_EXIT( phStbDFB_Utils, " Num of Encoders found %d", numEncoders);

    return numEncoders;
}

/******************************************************************************/
extern DFBResult phStbDFB_InitVideoDenc(phStbDFB_ScreenData_t *screen_data,
                                        uint32_t encoder,
                                        DFBScreenEncoderDescription *pEncDesc)
{
    char_t vdencName[32];
    int32_t tmpVdenc;
    DFBResult result = DFB_ITEMNOTFOUND;
    int32_t localError;
    uint32_t conIndex;
    uint32_t numModes;
    struct videodenc_fix_screeninfo fixInfo;
    phStbDFB_VdencMap_t* vdenc;

    D_DEBUG_ENTER( phStbDFB_Utils, "");
    sprintf(vdencName, "/dev/vdenc/renderer%d/vdenc%d", screen_data->unitId, encoder);

    if (( tmpVdenc = open(vdencName, O_RDWR)) < 0)
    {
        D_INFO("Failed to open device %s\n", vdencName);
    }
    else
    {
        /*We have found a valid device now we need to get the settings and compare name*/
        /*Write the settings to the board*/
        if ((localError = ioctl(tmpVdenc, VIDEODENC_IOGET_FSCREENINFO, &fixInfo)) < 0)
        {
            close(tmpVdenc);
            D_WARN("ERROR: sending vdenc command %s (%d)\n", strerror(errno), localError);
            result = DFB_FAILURE;
            goto RETURN;
        }
        else
        {
            /*Find empty Entry*/
            vdenc = findFreeVdenc(screen_data);

            /*Copy the name into the description.*/
            strcpy(pEncDesc->name, fixInfo.id);

            /*Copy in the connectors we support.*/
            for(conIndex =0; conIndex < VIDEODENC_CONNECTORS_MAX; conIndex++)
            {
                if(fixInfo.all_connectors->in_use)
                {
                    uint32_t supportedConnector = CONVERT_TO_DFB_OUTPUT_CONNECTOR(fixInfo.all_connectors[conIndex].type,
                                                                                  fixInfo.all_connectors[conIndex].connector_id);
                    pEncDesc->all_connectors |= supportedConnector;
                }
            }

            if(fixInfo.sout_signals & VIDEODENC_OUTSIGNAL_CVBS)
            {
                pEncDesc->out_signals |= DSOS_CVBS;
            }
            if(fixInfo.sout_signals & VIDEODENC_OUTSIGNAL_YC)
            {
                pEncDesc->out_signals |= DSOS_YC;
            }
            if(fixInfo.sout_signals & VIDEODENC_OUTSIGNAL_YPRPB)
            {
                pEncDesc->out_signals |= DSOS_YCBCR;
            }
            if(fixInfo.sout_signals & VIDEODENC_OUTSIGNAL_RGB)
            {
                pEncDesc->out_signals |= DSOS_RGB;
                if(pEncDesc->all_connectors & DSOC_VGA)
                {
                    pEncDesc->out_signals |= DSOS_VGA;
                }
            }
            if(fixInfo.sout_signals & VIDEODENC_OUTSIGNAL_DIGITAL)
            {
                pEncDesc->out_signals |= DSOS_HDMI;
            }

            /*Store values*/
            vdenc->encoder = encoder;
            vdenc->renderer = screen_data->unitId;
            vdenc->inUse = true;
            strcpy(vdenc->devName, vdencName);

            /* Now get the number of modes and then increment through them.*/
            if ((localError = ioctl(tmpVdenc, VIDEODENC_IOGET_NUM_MODES, &numModes)) < 0)
            {
                close(tmpVdenc);
                D_WARN("ERROR: sending vdenc command %s (%d)\n", strerror(errno), localError);
                result = DFB_FAILURE;
                goto RETURN;
            }
            else
            {
                char_t *buffer;
                struct videodenc_videomode *curVidMode;
                uint32_t modeIndex;

                buffer = malloc(numModes * sizeof(struct videodenc_videomode));
                curVidMode = (struct videodenc_videomode *)buffer;

                if ((localError = ioctl(tmpVdenc, VIDEODENC_IOGET_MODES, buffer)) < 0)
                {
                    close(tmpVdenc);
                    free(buffer);
                    D_WARN("ERROR: sending vdenc command %s (%d)\n", strerror(errno), localError);
                    result = DFB_FAILURE;
                    goto RETURN;
                }
                for(modeIndex = 0; modeIndex < numModes; modeIndex++)
                {
                    pEncDesc->tv_standards |= CONVERT_TO_DFB_TVSTANDARD(curVidMode->tv_standard);
                    pEncDesc->all_resolutions |= CONVERT_TO_DFB_OUTPUT_RESOLUTION(curVidMode->xres, curVidMode->yres);
                    curVidMode++;
                }
                free(buffer);
                result = DFB_OK;
            }
        }
    }
    /*If no errors then close the device*/
    if(result == DFB_OK)
    {
        close(tmpVdenc);
    }
RETURN:
    D_DEBUG_EXIT( phStbDFB_Utils, "");

    return result;
}

/******************************************************************************/
extern bool phStbDFB_AK470XInstalled(void)
{
    bool result = false;
    int32_t fdScart;

    D_DEBUG_ENTER( phStbDFB_Utils, "");

    /*Only open if this setting is supported*/
    if ((fdScart = open(pAvSwitchDevNameAk470x, O_RDWR)) < 0)
    {
        result = false;
    }
    else
    {
        /*Close the file descriptor*/
        close(fdScart);
        result = true;
    }

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

/******************************************************************************/
extern DFBResult phStbDFB_SetupAK470X(av_switch_data *pAvSwitchSettings)
{
    DFBResult result = DFB_OK;
    int32_t fdScart;
    int32_t localError;

    D_DEBUG_ENTER( phStbDFB_Utils, "");

    /*Only oepn if this setting is supported*/
    if ((fdScart = open(pAvSwitchDevNameAk470x, O_RDWR)) < 0)
    {
        D_WARN("ERROR: opening %s\n", pAvSwitchDevNameAk470x);
        result = DFB_FAILURE;
        goto RETURN;
    }

    /*Write the settings to the board*/
    if ((localError = ioctl(fdScart, AK470X_IOCTL_SWITCH_MODE_SET, pAvSwitchSettings)) < 0)
    {
        close(fdScart);
        D_WARN("ERROR: sending ak470x command %s (%d)\n", strerror(errno), localError);
        result = DFB_FAILURE;
        goto RETURN;
    }

    /*Close the file descriptor*/
    close(fdScart);

RETURN:
    D_DEBUG_EXIT( phStbDFB_Utils, " result => 0x%08x", result);
    return result;
}


/******************************************************************************/
extern DFBResult phStbDFB_GetSetupAK470X(av_switch_data *pAvSwitchSettings)
{
    DFBResult result = DFB_OK;
    int32_t fdScart;
    int32_t localError;

    D_DEBUG_ENTER( phStbDFB_Utils, "");

    /*Only oepn if this setting is supported*/
    if ((fdScart = open(pAvSwitchDevNameAk470x, O_RDWR)) < 0)
    {
        D_WARN("ERROR: opening %s\n", pAvSwitchDevNameAk470x);
        result = DFB_FAILURE;
        goto RETURN;
    }

    if ((localError = ioctl(fdScart, AK470X_IOCTL_SWITCH_MODE_GET, pAvSwitchSettings)) < 0)
    {
        close(fdScart);
        D_WARN("ERROR: sending ak470x command %s (%d)\n", strerror(errno), localError);
        result = DFB_FAILURE;
        goto RETURN;
    }

    /*Close the file descriptor*/
    close(fdScart);

RETURN:
    D_DEBUG_EXIT( phStbDFB_Utils, " result => 0x%08x", result);
    return result;
}

/******************************************************************************/
extern bool phStbDFB_UDA3000Installed(void)
{
    bool result = false;
    int32_t fdScart;

    D_DEBUG_ENTER( phStbDFB_Utils, "");

    /* Only open if this setting is supported */
    if ((fdScart = open(pAvSwitchDevNameUda3000, O_RDWR)) < 0)
    {
        result = false;
    }
    else
    {
        /* Close the file descriptor */
        close(fdScart);
        result = true;
    }

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

/******************************************************************************/
extern DFBResult phStbDFB_GetSetupUDA3000(phStbScartSwitch_data_t *pAvSwitchSettings)
{
    DFBResult result = DFB_OK;
    int32_t fdScart;
    int32_t localError;

    D_DEBUG_ENTER( phStbDFB_Utils, "");

    /*Only oepn if this setting is supported*/
    if ((fdScart = open(pAvSwitchDevNameUda3000, O_RDWR)) < 0)
    {
        D_WARN("ERROR: opening %s\n", pAvSwitchDevNameUda3000);
        result = DFB_FAILURE;
        goto phStbDFB_GetSetupUda3000_error;
    }

    if ((localError = ioctl(fdScart, phStbScartSwitch_GET_SWITCH_SETTINGS, pAvSwitchSettings)) < 0)
    {
        close(fdScart);
        D_WARN("ERROR: sending uda3000 command %s (%d)\n", strerror(errno), localError);
        result = DFB_FAILURE;
        goto phStbDFB_GetSetupUda3000_error;
    }

    /*Close the file descriptor*/
    close(fdScart);

phStbDFB_GetSetupUda3000_error:
    D_DEBUG_EXIT( phStbDFB_Utils, " result => 0x%08x", result);
    return result;
}

/******************************************************************************/
extern DFBResult phStbDFB_SetupUDA3000(phStbScartSwitch_data_t *pAvSwitchSettings)
{
    DFBResult result = DFB_OK;
    int32_t fdScart;
    int32_t localError;

    D_DEBUG_ENTER( phStbDFB_Utils, "");

    /* Only oepn if this setting is supported */
    if ((fdScart = open(pAvSwitchDevNameUda3000, O_RDWR)) < 0)
    {
        D_WARN("ERROR: opening %s\n", pAvSwitchDevNameUda3000);
        result = DFB_FAILURE;
        goto phStbDFB_SetupUDA3000_error;
    }

    /* Write the settings to the board */
    if ((localError = ioctl(fdScart, phStbScartSwitch_SET_SWITCH_SETTINGS, pAvSwitchSettings)) < 0)
    {
        close(fdScart);
        D_WARN("ERROR: sending uda3000 command %s (%d)\n", strerror(errno), localError);
        result = DFB_FAILURE;
        goto phStbDFB_SetupUDA3000_error;
    }

    /* Close the file descriptor */
    close(fdScart);

phStbDFB_SetupUDA3000_error:
    D_DEBUG_EXIT( phStbDFB_Utils, " result => 0x%08x", result);
    return result;
}

/******************************************************************************/
extern DFBResult phStbDFB_SetupDenc(phStbDFB_ScreenData_t *screen_data,
                                    uint32_t encoder,
                                    struct videodenc_var_screeninfo *pDencSettings)
{
    DFBResult result = DFB_OK;
    int32_t localError;
    phStbDFB_VdencMap_t *vdenc;
    int32_t fd;

    D_DEBUG_ENTER( phStbDFB_Utils, " xres=>%d: yres=>%d: scantype=>0x%08x: tv_standard=>0x%08x: out_signal=>0x%08x: refresh=>0x%08x",
                   pDencSettings->xres,
                   pDencSettings->yres,
                   pDencSettings->scantype,
                   pDencSettings->tv_standard,
                   pDencSettings->output_signal,
                   pDencSettings->refresh);

    vdenc = findVdenc(screen_data, screen_data->unitId, encoder);
    if(vdenc == NULL)
    {
        D_WARN("ERROR: Specified Video denc not found (renderer %d, encoder %d)\n", screen_data->unitId, encoder);
        result = DFB_ITEMNOTFOUND;
        goto RETURN;
    }
    /* Now open the device */
    if (( fd = open(vdenc->devName, O_RDWR)) < 0)
    {
        D_WARN("Failed to open device - error %s\n", strerror(errno), vdenc->devName);
        result = DFB_FILENOTFOUND;
        goto RETURN;
    }

    if ((localError = ioctl(fd,
                            VIDEODENC_IOSET_MODE,
                            pDencSettings)) < 0)
    {
        D_WARN("ERROR: Invalid Video Denc command %s (%d)\n", strerror(errno), localError);
        result = DFB_FAILURE;
        goto RETURN;
    }
    /* Now close the device */
    close(fd);

RETURN:
    D_DEBUG_EXIT( phStbDFB_Utils, " result => 0x%08x", result);
    return result;
}

/******************************************************************************/
extern DFBResult phStbDFB_GetDencSetup(phStbDFB_ScreenData_t *screen_data,
                                       uint32_t encoder,
                                       struct videodenc_var_screeninfo *pDencSettings)
{
    DFBResult result = DFB_OK;
    int32_t localError;
    phStbDFB_VdencMap_t *vdenc;
    int32_t fd;

    D_DEBUG_ENTER( phStbDFB_Utils, "");

    vdenc = findVdenc(screen_data, screen_data->unitId, encoder);
    if (vdenc == NULL)
    {
        D_WARN("ERROR: Specified Video denc not found (renderer %d, encoder %d)\n", screen_data->unitId, encoder);
        result = DFB_ITEMNOTFOUND;
        goto RETURN;
    }
    /* Now open the device */
    if (( fd = open(vdenc->devName, O_RDWR)) < 0)
    {
        D_WARN("Failed to open device - error %s\n", strerror(errno), vdenc->devName);
        result = DFB_FILENOTFOUND;
        goto RETURN;
    }

    /*Write the settings to the board*/
    if ((localError = ioctl(fd,
                            VIDEODENC_IOGET_VSCREENINFO,
                            pDencSettings)) < 0)
    {
        D_WARN("ERROR: sending Video Denc command %s (%d)\n", strerror(errno), localError);
        result = DFB_FAILURE;
        goto RETURN;
    }
    /* Now close the device */
    close(fd);

    D_DEBUG_EXIT( phStbDFB_Utils, " xres=>%d: yres=>%d: scantype=>0x%08x: tv_standard=>0x%08x: out_signal=>0x%08x: refresh=>0x%08x",
                  pDencSettings->xres,
                  pDencSettings->yres,
                  pDencSettings->scantype,
                  pDencSettings->tv_standard,
                  pDencSettings->output_signal,
                  pDencSettings->refresh);
RETURN:
    return result;
}

/******************************************************************************/

extern DFBResult phStbDFB_Utils_SetPowerMode(
    CoreScreen             *screen,
    void                   *driver_data,
    void                   *screen_data,
    DFBScreenPowerMode      mode )
{
    DFBResult result = DFB_OK;
    phStbDFB_ScreenData_t *pScreenData = (phStbDFB_ScreenData_t*)screen_data;
    uint32_t num_encoders;
    uint32_t encoder;

    D_DEBUG_ENTER( phStbDFB_Utils, "");

    num_encoders = phStbDFB_GetNumberVideoDencs(pScreenData->unitId);
    for(encoder = 0; encoder < num_encoders; encoder++)
    {
        int32_t fd;
        int32_t localError;
        phStbDFB_VdencMap_t *vdenc;
        vdenc = findVdenc(screen_data, pScreenData->unitId, encoder);

        if(vdenc == NULL)
        {
            D_WARN("ERROR: Specified Video denc not found (renderer %d, encoder %d)\n", pScreenData->unitId, encoder);
            result = DFB_ITEMNOTFOUND;
            goto RETURN;
        }
        /* Now open the device */
        if (( fd = open(vdenc->devName, O_RDWR)) < 0)
        {
            D_WARN("Failed to open device - error %s\n", strerror(errno), vdenc->devName);
            result = DFB_FILENOTFOUND;
            goto RETURN;
        }

        if ((localError = ioctl(fd, VIDEODENC_IOBLANK, mode == DSPM_ON ? False : True)) < 0)
        {
            D_WARN("ERROR: Invalid Video Denc command %s (%d)\n", strerror(errno), localError);
            result = DFB_FAILURE;
            goto RETURN;
        }
        /* Now close the device */
        close(fd);
    }

RETURN:
    D_DEBUG_EXIT( phStbDFB_Utils, " result => 0x%08x", result);
    return result;
}
/******************************************************************************/
extern DFBResult phStbDFB_SetupDencGamma(phStbDFB_ScreenData_t *screen_data,
                                       uint32_t encoder,
                                       uint32_t *pGammaValues)
{
    DFBResult result = DFB_OK;
    int32_t localError;
    phStbDFB_VdencMap_t *vdenc;
    int32_t fd;

    D_DEBUG_ENTER( phStbDFB_Utils, "");

    vdenc = findVdenc(screen_data, screen_data->unitId, encoder);
    if(vdenc == NULL)
    {
        D_WARN("ERROR: Specified Video denc not found (renderer %d, encoder %d)\n", screen_data->unitId, encoder);
        result = DFB_ITEMNOTFOUND;
        goto RETURN;
    }
    /* Now open the device */
    if (( fd = open(vdenc->devName, O_RDWR)) < 0)
    {
        D_WARN("Failed to open device - error %s\n", strerror(errno), vdenc->devName);
        result = DFB_FILENOTFOUND;
        goto RETURN;
    }

    if ((localError = ioctl(fd,
                            VIDEODENC_IOSET_GAMMA,
                            pGammaValues)) < 0)
    {
        D_WARN("ERROR: Invalid Video Denc command %s (%d)\n", strerror(errno), localError);
        result = DFB_FAILURE;
        goto RETURN;
    }
    /* Now close the device */
    close(fd);

RETURN:
    D_DEBUG_EXIT( phStbDFB_Utils, " result => 0x%08x", result);
    return result;
}

/******************************************************************************/
static uint32_t phStbDFB_GetZOrderInternal(uint32_t layerId)
{
    uint32_t i;
    uint32_t current = 0;
    tmErrorCode_t err;
    phStbVideoRenderer_t *pVideoRenderer;
    phStbVideoRenderer_Plane_t layers[phStbVideoRenderer_Planes_Count];

    err = phStbVideoRenderer_GetInstance(0, &pVideoRenderer);

    if (err == TM_OK)
    {
        err = phStbVideoRenderer_GetPlaneStack(pVideoRenderer, layers);
    
        if (err == TM_OK)
        {
            for(i = 0; i < phStbVideoRenderer_Planes_Count; i++)
            {
                if (layers[i].layer == layerId)
                {
                    current = i;
                    i = phStbVideoRenderer_Planes_Count;
                }
            }
        }
    }
    return current;
}

/******************************************************************************/
extern uint32_t phStbDFB_GetZOrder(CoreLayer *layer)
{
    uint32_t current;
    uint32_t layerId;

    D_DEBUG_ENTER( phStbDFB_Utils, "");

    layerId = phStbDFB_dfb_to_renderer_translated(layer);

    current = phStbDFB_GetZOrderInternal(layerId);

    D_DEBUG_EXIT( phStbDFB_Utils, " current = %d", current);
    return current;
}

/******************************************************************************/
static tmErrorCode_t phStbDFB_Zreorder(uint32_t currentPosition, uint32_t newPosition)
{
    tmErrorCode_t err = TM_OK;
    phStbVideoRenderer_t *pVideoRenderer;

    D_DEBUG_ENTER( phStbDFB_Utils, "");

    err = phStbVideoRenderer_GetInstance(0, &pVideoRenderer);

    if (err == TM_OK)
    {
        phStbVideoRenderer_Plane_t layers[phStbVideoRenderer_Planes_Count];
        err = phStbVideoRenderer_GetPlaneStack(pVideoRenderer, layers);

        if (err == TM_OK)
        {
            uint32_t i;
            phStbVideoRenderer_Layers_t thisLayer;
            bool thisEnable;

            thisLayer = layers[currentPosition].layer;
            thisEnable = layers[currentPosition].enabled;

            /* Extract layer from list */
            for(i=currentPosition; i>0; i--)
            {
                layers[i].enabled = layers[i-1].enabled;
                layers[i].layer = layers[i-1].layer;
            }

            /* Insert layer into list */
            for(i=0; i<newPosition; i++)
            {
                layers[i].enabled = layers[i+1].enabled;
                layers[i].layer = layers[i+1].layer;
            }

            layers[newPosition].enabled = thisEnable;
            layers[newPosition].layer = thisLayer;

            err = phStbVideoRenderer_SetPlaneStack(pVideoRenderer, layers);
            if (err == TM_OK)
            {
                err = phStbVideoRenderer_GetInstance(1, &pVideoRenderer);
                if (err == TM_OK)
                {
                    err = phStbVideoRenderer_SetPlaneStack(pVideoRenderer, layers);
                }
            }
        }
    }

    D_DEBUG_EXIT( phStbDFB_Utils, " err = 0x%08x", err);
    return err;
}

/******************************************************************************/
static tmErrorCode_t phStbDFB_setZInfo(phStbVideoRenderer_Layers_t *pLayers)
{
    tmErrorCode_t err = TM_OK;
    phStbVideoRenderer_t *pVideoRenderer;

    D_DEBUG_ENTER( phStbDFB_Utils, "");

    err = phStbVideoRenderer_GetInstance(0, &pVideoRenderer);

    if (err == TM_OK)
    {
        phStbVideoRenderer_Plane_t layers[phStbVideoRenderer_Planes_Count];

        /* Get the current layer Z ordering */
        err = phStbVideoRenderer_GetPlaneStack(pVideoRenderer, layers);

        if (err == TM_OK)
        {
            uint32_t i;
            bool enabled[phStbVideoRenderer_Planes_Count];
            phStbVideoRenderer_Plane_t outputLayers[phStbVideoRenderer_Planes_Count];

            /* Save the layer visibility information */
            for(i=0; i<phStbVideoRenderer_Planes_Count; i++)
            {
                enabled[layers[i].layer] = layers[i].enabled;
            }

            /* Update the layer ordering */
            for(i=0; i<phStbVideoRenderer_Planes_Count; i++)
            {
                outputLayers[i].layer   = pLayers[i];
                outputLayers[i].enabled = enabled[pLayers[i]];
            }

            /* Save the new Z order information - to both mixers */
            err = phStbVideoRenderer_SetPlaneStack(pVideoRenderer, outputLayers);
            if (err == TM_OK)
            {
                err = phStbVideoRenderer_GetInstance(1, &pVideoRenderer);
                if (err == TM_OK)
                {
                    err = phStbVideoRenderer_SetPlaneStack(pVideoRenderer, outputLayers);
                }
            }
        }
    }

    D_DEBUG_EXIT( phStbDFB_Utils, " err = 0x%08x", err);
    return err;
}

/******************************************************************************/
extern DFBResult phStbDFB_SetZOrder(CoreLayer *layer,
                                    uint32_t value)
{
    tmErrorCode_t err;
    uint32_t current;

    /* Check for a single framebuffer */
    if (phStbDFB_gfx_layer_idx == 1)
    {
        uint32_t layerId;
        uint32_t layerZorder[phStbVideoRenderer_Planes_Count];

        /* Get the Z order position of the graphics and video layers */
        layerZorder[phStbVideoRenderer_Layers_Graphics1] = phStbDFB_GetZOrderInternal(phStbVideoRenderer_Layers_Graphics1);
        layerZorder[phStbVideoRenderer_Layers_Graphics2] = phStbDFB_GetZOrderInternal(phStbVideoRenderer_Layers_Graphics2);
        layerZorder[phStbVideoRenderer_Layers_Display1]  = phStbDFB_GetZOrderInternal(phStbVideoRenderer_Layers_Display1);
        layerZorder[phStbVideoRenderer_Layers_Display2]  = phStbDFB_GetZOrderInternal(phStbVideoRenderer_Layers_Display2);

        layerId = phStbDFB_dfb_to_renderer_translated(layer);

        /* Check for a reference to the graphics layer, or to the 2nd graphics layer! */
        /* Which would be a bit odd but treat as a reference to the graphics layer    */
        if ((layerId == phStbVideoRenderer_Layers_Graphics1) ||
            (layerId == phStbVideoRenderer_Layers_Graphics2))
        {
            phStbVideoRenderer_Layers_t layers[phStbVideoRenderer_Planes_Count];

            /* Check for invalid layer positioning */
            if (value >= phStbVideoRenderer_Planes_Bottom)
            {
                value = phStbVideoRenderer_Planes_Bottom - 1;
            }

            /* Position the graphics layers */
            layers[value] = phStbVideoRenderer_Layers_Graphics1;
            layers[value+1] = phStbVideoRenderer_Layers_Graphics2;

            /* Check the order of the video layers */
            if (layerZorder[phStbVideoRenderer_Layers_Display1] < 
                layerZorder[phStbVideoRenderer_Layers_Display2])
            {
                /* Position the video layers */
                if (value == 0)
                {
                    layers[2] = phStbVideoRenderer_Layers_Display1;
                    layers[3] = phStbVideoRenderer_Layers_Display2;
                }
                else
                if (value == 1)
                {
                    layers[0] = phStbVideoRenderer_Layers_Display1;
                    layers[3] = phStbVideoRenderer_Layers_Display2;
                }
                else
                {
                    layers[0] = phStbVideoRenderer_Layers_Display1;
                    layers[1] = phStbVideoRenderer_Layers_Display2;
                }
            }
            else
            {
                /* Position the video layers */
                if (value == 0)
                {
                    layers[2] = phStbVideoRenderer_Layers_Display2;
                    layers[3] = phStbVideoRenderer_Layers_Display1;
                }
                else
                if (value == 1)
                {
                    layers[0] = phStbVideoRenderer_Layers_Display2;
                    layers[3] = phStbVideoRenderer_Layers_Display1;
                }
                else
                {
                    layers[0] = phStbVideoRenderer_Layers_Display2;
                    layers[1] = phStbVideoRenderer_Layers_Display1;
                }
            }

            err = phStbDFB_setZInfo(layers);
        }
        else
        {
            /* A reference to a video layer */
            /* Check to see if the new position is in between the 2 graphics layers! */
            if ((value == layerZorder[phStbVideoRenderer_Layers_Graphics2]) &&
                (value != phStbVideoRenderer_Planes_Bottom))
            {
                value++;
            }

            /* Deal with the requested layer */
            current = phStbDFB_GetZOrder(layer);
            err = phStbDFB_Zreorder(current, value);
        }
    }
    else
    {
        /* Deal with the requested layer */
        current = phStbDFB_GetZOrder(layer);
        err = phStbDFB_Zreorder(current, value);
    }

    if (err != TM_OK)
    {
        return DFB_FAILURE;
    }

    return DFB_OK;
}
