/*
Copyright (c) 2012-2014, Broadcom Europe Ltd
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the copyright holder nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <string.h>
#include <stdlib.h>

#include "vchost_platform_config.h"
#include "vchost.h"

#include "interface/vcos/vcos.h"
#include "vc_dispservice_x_defs.h"
#include "vc_dispmanx.h"
#include "interface/vchi/vchi.h"
#include "vcinclude/common.h"
#include "interface/vchi/common/endian.h"
#include "interface/vchi/message_drivers/message.h"
#include "vc_vchi_dispmanx.h"
/******************************************************************************
Local types and defines.
******************************************************************************/
//DispmanX service
typedef struct {
   VCHI_SERVICE_HANDLE_T client_handle[VCHI_MAX_NUM_CONNECTIONS]; //To connect to server on VC
   VCHI_SERVICE_HANDLE_T notify_handle[VCHI_MAX_NUM_CONNECTIONS]; //For incoming notification
   uint32_t              msg_flag[VCHI_MAX_NUM_CONNECTIONS];
   char                  command_buffer[DISPMANX_MSGFIFO_SIZE];
   char                  response_buffer[DISPMANX_MSGFIFO_SIZE];
   uint32_t              response_length;
   uint32_t              notify_buffer[DISPMANX_MSGFIFO_SIZE/sizeof(uint32_t)];
   uint32_t              notify_length;
   uint32_t              num_connections;
   VCOS_MUTEX_T          lock;
   char dispmanx_devices[DISPMANX_MAX_HOST_DEVICES][DISPMANX_MAX_DEVICE_NAME_LEN];
   uint32_t num_devices;
   uint32_t num_modes[DISPMANX_MAX_HOST_DEVICES];

   //Callback for update
   DISPMANX_CALLBACK_FUNC_T update_callback;
   void *update_callback_param;
   DISPMANX_UPDATE_HANDLE_T pending_update_handle;

   //Callback for vsync
   DISPMANX_CALLBACK_FUNC_T vsync_callback;
   void *vsync_callback_param;
   int vsync_enabled;

   int initialised;
} DISPMANX_SERVICE_T;

/******************************************************************************
Static data.
******************************************************************************/
static DISPMANX_SERVICE_T dispmanx_client;
static VCOS_EVENT_T dispmanx_message_available_event;
static VCOS_EVENT_T dispmanx_notify_available_event;
static VCOS_THREAD_T dispmanx_notify_task;

/******************************************************************************
Static functions.
******************************************************************************/
//Lock the host state
static __inline void lock_obtain (void) {
   VCOS_STATUS_T status;
   uint32_t i;
   vcos_assert(dispmanx_client.initialised);
   status = vcos_mutex_lock( &dispmanx_client.lock );
   if(dispmanx_client.initialised)
   {
      for (i=0; i<dispmanx_client.num_connections; i++) {
         vchi_service_use(dispmanx_client.client_handle[i]);
      }
   }
   vcos_assert(status == VCOS_SUCCESS);
}

//Unlock the host state
static __inline void lock_release (void) {
   uint32_t i;
   vcos_assert(dispmanx_client.initialised);
   if(dispmanx_client.initialised)
   {
      for (i=0; i<dispmanx_client.num_connections; i++) {
         vchi_service_release(dispmanx_client.client_handle[i]);
      }
   }
   vcos_mutex_unlock( &dispmanx_client.lock );
}

//Forward declarations
static void dispmanx_client_callback( void *callback_param,
                                      VCHI_CALLBACK_REASON_T reason,
                                      void *msg_handle );

static void dispmanx_notify_callback( void *callback_param,
                                      VCHI_CALLBACK_REASON_T reason,
                                      void *msg_handle );

static int32_t dispmanx_wait_for_reply(void *response, uint32_t max_length);

static int32_t dispmanx_send_command(  uint32_t command, void *buffer, uint32_t length);

static int32_t dispmanx_send_command_reply(  uint32_t command, void *buffer, uint32_t length,
                                        void *response, uint32_t max_length);

static uint32_t dispmanx_get_handle(  uint32_t command, void *buffer, uint32_t length);

static void *dispmanx_notify_func( void *arg );

/******************************************************************************
NAME
   vc_vchi_gencmd_init

SYNOPSIS
   void vc_vchi_gencmd_init(VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections )

FUNCTION
   Initialise the general command service for use. A negative return value
   indicates failure (which may mean it has not been started on VideoCore).

RETURNS
   int
******************************************************************************/

void vc_vchi_dispmanx_init (VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections ) {
   VCOS_STATUS_T status;
   int32_t success;
   uint32_t i;

   if (dispmanx_client.initialised)
     return;

   // record the number of connections
   memset( &dispmanx_client, 0, sizeof(DISPMANX_SERVICE_T) );
   dispmanx_client.num_connections = num_connections;

   status = vcos_mutex_create(&dispmanx_client.lock, "HDispmanx");
   vcos_assert(status == VCOS_SUCCESS);

   status = vcos_event_create(&dispmanx_message_available_event, "HDispmanx");
   vcos_assert(status == VCOS_SUCCESS);

   status = vcos_event_create(&dispmanx_notify_available_event, "HDispmanx");
   vcos_assert(status == VCOS_SUCCESS);

   dispmanx_client.initialised = 1;

   for (i=0; i<dispmanx_client.num_connections; i++) {

      VCOS_THREAD_ATTR_T attrs;

      // Create a 'Client' service on the each of the connections
      SERVICE_CREATION_T dispmanx_parameters = { VCHI_VERSION(VC_DISPMANX_VERSION),
                                                 DISPMANX_CLIENT_NAME,     // 4cc service code
                                                 connections[i],           // passed in fn ptrs
                                                 0,                        // tx fifo size (unused)
                                                 0,                        // tx fifo size (unused)
                                                 &dispmanx_client_callback, // service callback
                                                 &dispmanx_message_available_event,  // callback parameter
                                                 VC_FALSE,                  // want_unaligned_bulk_rx
                                                 VC_FALSE,                  // want_unaligned_bulk_tx
                                                 VC_FALSE,                  // want_crc
                                                 };

      SERVICE_CREATION_T dispmanx_parameters2 = { VCHI_VERSION(VC_DISPMANX_VERSION),
                                                  DISPMANX_NOTIFY_NAME,   // 4cc service code
                                                  connections[i],           // passed in fn ptrs
                                                  0,                        // tx fifo size (unused)
                                                  0,                        // tx fifo size (unused)
                                                  &dispmanx_notify_callback, // service callback
                                                  &dispmanx_notify_available_event,  // callback parameter
                                                  VC_FALSE,                  // want_unaligned_bulk_rx
                                                  VC_FALSE,                  // want_unaligned_bulk_tx
                                                  VC_FALSE,                  // want_crc
                                                   };

      success = vchi_service_open( initialise_instance, &dispmanx_parameters, &dispmanx_client.client_handle[i] );
      vcos_assert( success == 0 );

      // Create the async service of dispman to handle update callback

      success = vchi_service_open( initialise_instance, &dispmanx_parameters2, &dispmanx_client.notify_handle[i] );
      vcos_assert( success == 0 );

      //Create the notifier task
      vcos_thread_attr_init(&attrs);
      vcos_thread_attr_setstacksize(&attrs, 2048);
      vcos_thread_attr_settimeslice(&attrs, 1);

      status = vcos_thread_create(&dispmanx_notify_task, "HDispmanx Notify", &attrs, dispmanx_notify_func, NULL);
      vcos_assert(status == VCOS_SUCCESS);

      // release services until they're actually used
      vchi_service_release(dispmanx_client.client_handle[i]);
      vchi_service_release(dispmanx_client.notify_handle[i]);
   }
}

/***********************************************************
 * Name: vc_dispmanx_stop
 *
 * Arguments:
 *       -
 *
 * Description: Stops the Host side part of dispmanx
 *
 * Returns: -
 *
 ***********************************************************/
VCHPRE_ void VCHPOST_ vc_dispmanx_stop( void ) {
   // Wait for the current lock-holder to finish before zapping dispmanx.
   //TODO: kill the notifier task
   void *dummy;
   uint32_t i;

   if (!dispmanx_client.initialised)
      return;

   lock_obtain();
   for (i=0; i<dispmanx_client.num_connections; i++) {
      int32_t result;
      result = vchi_service_close(dispmanx_client.client_handle[i]);
      vcos_assert( result == 0 );
      result = vchi_service_close(dispmanx_client.notify_handle[i]);
      vcos_assert( result == 0 );
   }
   lock_release();
   dispmanx_client.initialised = 0;

   vcos_event_signal(&dispmanx_notify_available_event);
   vcos_thread_join(&dispmanx_notify_task, &dummy);
   vcos_mutex_delete(&dispmanx_client.lock);
   vcos_event_delete(&dispmanx_message_available_event);
   vcos_event_delete(&dispmanx_notify_available_event);
}

/***********************************************************
 * Name: vc_dispmanx_rect_set
 *
 * Arguments:
 *       VC_RECT_T *rect
 *       uint32_t x_offset
 *       uint32_t y_offset
 *       uint32_t width
 *       uint32_t height
 *
 * Description: Fills in the fields of the supplied VC_RECT_T structure
 *
 * Returns: 0 or failure
 *
 ***********************************************************/
VCHPRE_ int VCHPOST_ vc_dispmanx_rect_set( VC_RECT_T *rect, uint32_t x_offset, uint32_t y_offset, uint32_t width, uint32_t height ) {
   rect->x = (int32_t) x_offset;
   rect->y = (int32_t) y_offset;
   rect->width = (int32_t) width;
   rect->height = (int32_t) height;
   return 0;
}

/******************************************************************************
NAME
   vc_dispmanx_query_image_formats

PARAMS
   uint32_t *support_formats - the returned supported image formats

FUNCTION
   Returns the support image formats from the VMCS host

RETURNS
   Success: 0
   Otherwise non-zero
******************************************************************************/
VCHPRE_ int  VCHPOST_ vc_dispmanx_query_image_formats( uint32_t *supported_formats ) {
   *supported_formats = dispmanx_get_handle(EDispmanQueryImageFormats, NULL, 0);
   return (*supported_formats)? 0 : -1;
}

/***********************************************************
 * Name: vc_dispmanx_resource_create
 *
 * Arguments:
 *       VC_IMAGE_TYPE_T type
 *       uint32_t width
 *       uint32_t height
 *
 * Description: Create a new resource (in Videocore memory)
 *
 * Returns: resource handle
 *
 ***********************************************************/
VCHPRE_ DISPMANX_RESOURCE_HANDLE_T VCHPOST_ vc_dispmanx_resource_create( VC_IMAGE_TYPE_T type, uint32_t width, uint32_t height, uint32_t *native_image_handle ) {
   uint32_t resourceCreateParams[] = { (uint32_t)VC_HTOV32(type), VC_HTOV32(width), VC_HTOV32(height) };

   uint32_t resource = 0;

   resource = dispmanx_get_handle(EDispmanResourceCreate, resourceCreateParams, sizeof(resourceCreateParams));

   //We don't get an image handle back, so explicitly set this to zero to let the caller know
   *native_image_handle = 0;
   //The caller should call vc_dispmanx_resource_get_image_handle below to get the VC_IMAGE_T *
   //This will be deprecated soon, however

   return (DISPMANX_RESOURCE_HANDLE_T) resource;
}

/***********************************************************
 * Name: vc_dispmanx_resource_get_image_handle
 *
 * Arguments: resource handle
 *
 * Description: xxx only for hacking purpose, will be obsolete soon
 *
 * Returns: vc_image pointer
 *
 ***********************************************************/
VCHPRE_ uint32_t VCHPOST_ vc_dispmanx_resource_get_image_handle( DISPMANX_RESOURCE_HANDLE_T res) {
   return dispmanx_get_handle(EDispmanResourceGetImage, &res, sizeof(res));
}

/***********************************************************
 * Name: vc_dispmanx_resource_delete
 *
 * Arguments:
 *       DISPMANX_RESOURCE_HANDLE_T res
 *
 * Description:
 *
 * Returns: 0 or failure
 *
 ***********************************************************/
VCHPRE_ int VCHPOST_ vc_dispmanx_resource_delete( DISPMANX_RESOURCE_HANDLE_T res ) {
   int status;
   res = VC_HTOV32(res);
   //We block to make sure the memory is freed after the call
   status = (int) dispmanx_send_command(EDispmanResourceDelete, &res, sizeof(res));

   return status;
}

/***********************************************************
 * Name: vc_dispmanx_resource_write_data
 *
 * Arguments:
 *       DISPMANX_RESOURCE_HANDLE_T res
 *       int src_pitch
 *       void * src_address
 *       const VC_RECT_T * rect
 *
 * Description: Copy the bitmap data to VideoCore memory
 *
 * Returns: 0 or failure
 *
 ***********************************************************/
VCHPRE_ int VCHPOST_ vc_dispmanx_resource_write_data( DISPMANX_RESOURCE_HANDLE_T handle, VC_IMAGE_TYPE_T src_type /* not used */,
                                                      int src_pitch, void * src_address, const VC_RECT_T * rect ) {
	(void)src_type;

   //Note that x coordinate of the rect is NOT used
   //Address of data in host
   uint8_t *host_start = (uint8_t *)src_address + src_pitch * rect->y;
   int32_t bulk_len = src_pitch * rect->height, success = 0;

   //Now send the bulk transfer across
   //command parameters: resource handle, destination y, bulk length
   uint32_t param[] = {VC_HTOV32(handle), VC_HTOV32(rect->y), VC_HTOV32(bulk_len), VC_HTOV32(src_type) };
   success = dispmanx_send_command(  EDispmanBulkWrite | DISPMANX_NO_REPLY_MASK, param, sizeof(param));
   if(success == 0)
   {
      lock_obtain();
      success = vchi_bulk_queue_transmit( dispmanx_client.client_handle[0],
                                          host_start,
                                          bulk_len,
                                          VCHI_FLAGS_BLOCK_UNTIL_DATA_READ,
                                          NULL );
      lock_release();
   }
   return (int) success;
}
/***********************************************************
 * Name: vc_dispmanx_resource_read_data
 *
 * Arguments:
 *       DISPMANX_RESOURCE_HANDLE_T res
 *       int src_pitch
 *       void * src_address
 *       const VC_RECT_T * rect
 *
 * Description: Copy the bitmap data from VideoCore memory
 *
 * Returns: 0 or failure
 *
 ***********************************************************/
VCHPRE_ int VCHPOST_
vc_dispmanx_resource_read_data(
   DISPMANX_RESOURCE_HANDLE_T handle,
   const VC_RECT_T* p_rect,
   void *   dst_address,
   uint32_t dst_pitch )
{
   uint8_t* host_start;
   int32_t  bulk_len;
   int32_t  success = 0;

   if ( p_rect == 0 || dst_address == 0 || dst_pitch == 0 )
   {
      return -1;
   }

   host_start = (uint8_t *)dst_address + (dst_pitch * p_rect->y);
   bulk_len   = (int32_t)dst_pitch * p_rect->height;

   // Now send the bulk transfer across
   // command parameters: resource handle, destination y, bulk length
   uint32_t param[] = { VC_HTOV32(handle), VC_HTOV32(p_rect->y), VC_HTOV32(bulk_len) };
   success = dispmanx_send_command( EDispmanBulkRead | DISPMANX_NO_REPLY_MASK, param, sizeof(param));
   if (success == 0)
   {
      lock_obtain();
      success = vchi_bulk_queue_receive(  dispmanx_client.client_handle[0],
                                          host_start,
                                          bulk_len,
                                          VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE,
                                          0 );
      lock_release();
   }
   return (int) success;
}

/***********************************************************
 * Name: vc_dispmanx_resource_write_data_handle
 *
 * Arguments:
 *       DISPMANX_RESOURCE_HANDLE_T res
 *       int src_pitch
 *       MEM_HANDLE_T handle
 *       uint32_t offset
 *       const VC_RECT_T * rect
 *
 * Description: Copy the bitmap data to VideoCore memory
 *
 * Returns: 0 or failure
 *
 ***********************************************************/
#ifdef SELF_HOSTED
VCHPRE_ int VCHPOST_ vc_dispmanx_resource_write_data_handle( DISPMANX_RESOURCE_HANDLE_T handle, VC_IMAGE_TYPE_T src_type /* not used */,
                                                             int src_pitch, VCHI_MEM_HANDLE_T mem_handle, uint32_t offset,
                                                             const VC_RECT_T * rect ) {
   int32_t bulk_len;
   uint32_t param[3];
   uint32_t success = 0;

   //Note that x coordinate of the rect is NOT used
   //Address of data in host
   offset += src_pitch * rect->y;
   bulk_len = src_pitch * rect->height;

   //Now send the bulk transfer across
   //command parameters: resource handle, destination y, bulk length
   param[0] = VC_HTOV32(handle);
   param[1] = VC_HTOV32(rect->y);
   param[2] = VC_HTOV32(bulk_len);
   success = dispmanx_send_command(  EDispmanBulkWrite | DISPMANX_NO_REPLY_MASK, param, sizeof(param));
   if(success == 0)
   {
      lock_obtain();
      success = vchi_bulk_queue_transmit_reloc( dispmanx_client.client_handle[0],
                                                mem_handle, offset,
                                                bulk_len,
                                                VCHI_FLAGS_BLOCK_UNTIL_DATA_READ,
                                                NULL );
      lock_release();
   }
   return (int) success;
}
#endif

/***********************************************************
 * Name: vc_dispmanx_display_open
 *
 * Arguments:
 *       uint32_t device
 *
 * Description:
 *
 * Returns:
 *
 ***********************************************************/
VCHPRE_ DISPMANX_DISPLAY_HANDLE_T VCHPOST_ vc_dispmanx_display_open( uint32_t device ) {
   uint32_t display_handle;
   char *env = getenv("VC_DISPLAY");

   if (device == 0 && env)
   {
      device = atoi(env);
   }

   device = VC_HTOV32(device);
   display_handle = dispmanx_get_handle(EDispmanDisplayOpen,
                                                 &device, sizeof(device));

   return (DISPMANX_DISPLAY_HANDLE_T) display_handle;
}

/***********************************************************
 * Name: vc_dispmanx_display_open_mode
 *
 * Arguments:
 *       uint32_t device
 *       uint32_t mode
 *
 * Description:
 *
 * Returns:
 *
 ***********************************************************/
VCHPRE_ DISPMANX_DISPLAY_HANDLE_T VCHPOST_ vc_dispmanx_display_open_mode( uint32_t device, uint32_t mode ) {
   uint32_t display_open_param[] = {VC_HTOV32(device), VC_HTOV32(mode)};
   uint32_t display_handle = dispmanx_get_handle(EDispmanDisplayOpenMode,
                                                 &display_open_param, sizeof(display_open_param));

   return (DISPMANX_DISPLAY_HANDLE_T) display_handle;
}

/***********************************************************
 * Name: vc_dispmanx_display_open_offscreen
 *
 * Arguments:
 *       DISPMANX_RESOURCE_HANDLE_T dest
 *       DISPMANX_TRANSFORM_T orientation
 *
 * Description:
 *
 * Returns:
 *
 ***********************************************************/
VCHPRE_ DISPMANX_DISPLAY_HANDLE_T VCHPOST_ vc_dispmanx_display_open_offscreen( DISPMANX_RESOURCE_HANDLE_T dest, DISPMANX_TRANSFORM_T orientation ) {
   uint32_t display_open_param[] = {(uint32_t)VC_HTOV32(dest), (uint32_t)VC_HTOV32(orientation)};
   uint32_t display_handle = dispmanx_get_handle(EDispmanDisplayOpenOffscreen,
                                                 &display_open_param, sizeof(display_open_param));

   return (DISPMANX_DISPLAY_HANDLE_T) display_handle;
}

/***********************************************************
 * Name: vc_dispmanx_display_reconfigure
 *
 * Arguments:
 *       DISPMANX_DISPLAY_HANDLE_T display
 *       uint32_t mode
 *
 * Description:
 *
 * Returns:
 *
 ***********************************************************/
VCHPRE_ int VCHPOST_ vc_dispmanx_display_reconfigure( DISPMANX_DISPLAY_HANDLE_T device, uint32_t mode ) {
   uint32_t display_param[] = {(uint32_t)VC_HTOV32(device), VC_HTOV32(mode)};
   int32_t success = dispmanx_send_command(  EDispmanDisplayReconfigure | DISPMANX_NO_REPLY_MASK,
                                             display_param, sizeof(display_param));
   return (int) success;
}

/***********************************************************
 * Name: vc_dispmanx_display_set_destination
 *
 * Arguments:
 *       DISPMANX_DISPLAY_HANDLE_T display
 *       DISPMANX_RESOURCE_HANDLE_T dest
 *
 * Description:
 *
 * Returns:
 *
 ***********************************************************/
VCHPRE_ int VCHPOST_ vc_dispmanx_display_set_destination( DISPMANX_DISPLAY_HANDLE_T display, DISPMANX_RESOURCE_HANDLE_T dest ) {
   uint32_t display_param[] = {(uint32_t)VC_HTOV32(display), (uint32_t)VC_HTOV32(dest)};
   int32_t success = dispmanx_send_command(  EDispmanDisplaySetDestination | DISPMANX_NO_REPLY_MASK,
                                             display_param, sizeof(display_param));
   return (int) success;
}

/***********************************************************
 * Name: vc_dispmanx_display_set_background
 *
 * Arguments:
 *       DISPMANX_UPDATE_HANDLE_T update
 *       DISPMANX_DISPLAY_HANDLE_T display
 *       uint8_t red
 *       uint8_t green
 *       uint8_t blue
 *
 * Description:
 *
 * Returns:
 *
 ***********************************************************/
VCHPRE_ int VCHPOST_ vc_dispmanx_display_set_background( DISPMANX_UPDATE_HANDLE_T update, DISPMANX_DISPLAY_HANDLE_T display,
                                                                       uint8_t red, uint8_t green, uint8_t blue ) {
   uint32_t display_param[] = {(uint32_t)VC_HTOV32(update), (uint32_t) VC_HTOV32(display), VC_HTOV32(red), VC_HTOV32(green), VC_HTOV32(blue)};
   int success = (int) dispmanx_send_command( EDispmanDisplaySetBackground | DISPMANX_NO_REPLY_MASK,
                                        display_param, sizeof(display_param));
   return success;
}

/***********************************************************
 * Name: vc_dispmanx_display_get_info
 *
 * Arguments:
 *       DISPMANX_DISPLAY_HANDLE_T display
 *       DISPMANX_MODEINFO_T * pinfo
 *
 * Description:
 *
 * Returns: VCHI error
 *
 ***********************************************************/
VCHPRE_ int VCHPOST_
vc_dispmanx_display_get_info (DISPMANX_DISPLAY_HANDLE_T display,
                              DISPMANX_MODEINFO_T *pinfo)
{
   GET_INFO_DATA_T info;
   int32_t success;
   display = VC_HTOV32(display);
   success = dispmanx_send_command_reply (EDispmanDisplayGetInfo,
                                          &display, sizeof(display),
                                          &info, sizeof(info));
   if(success == 0) {
      pinfo->width = VC_VTOH32(info.width);
      pinfo->height = VC_VTOH32(info.height);
      pinfo->transform = (DISPMANX_TRANSFORM_T)VC_VTOH32(info.transform);
      pinfo->input_format = (DISPLAY_INPUT_FORMAT_T)VC_VTOH32(info.input_format);
   }

   return (int) success;
}

/***********************************************************
 * Name: vc_dispmanx_display_close
 *
 * Arguments:
 *       DISPMANX_DISPLAY_HANDLE_T display
 *
 * Description:
 *
 * Returns:
 *
 ***********************************************************/
VCHPRE_ int VCHPOST_ vc_dispmanx_display_close( DISPMANX_DISPLAY_HANDLE_T display ) {
   int success;
   display = VC_HTOV32(display);
   success = (int) dispmanx_send_command( EDispmanDisplayClose | DISPMANX_NO_REPLY_MASK,
                                        &display, sizeof(display));
   return success;
}
/***********************************************************
 * Name: vc_dispmanx_update_start
 *
 * Arguments:
 *       int32_t priority
 *
 * Description:
 *
 * Returns:
 *
 ***********************************************************/
VCHPRE_ DISPMANX_UPDATE_HANDLE_T VCHPOST_ vc_dispmanx_update_start( int32_t priority ) {
   uint32_t handle;
   priority = VC_HTOV32(priority);
   handle = dispmanx_get_handle(EDispmanUpdateStart,
                                         &priority, sizeof(priority));

   return (DISPMANX_UPDATE_HANDLE_T) handle;
}

/***********************************************************
 * Name: vc_dispmanx_update_submit
 *
 * Arguments:
 *       DISPMANX_UPDATE_HANDLE_T update
 *       DISPMANX_CALLBACK_FUNC_T cb_func
 *       void *cb_arg
 *
 * Description:
 *
 * Returns:
 *
 ***********************************************************/
VCHPRE_ int VCHPOST_ vc_dispmanx_update_submit( DISPMANX_UPDATE_HANDLE_T update, DISPMANX_CALLBACK_FUNC_T cb_func, void *cb_arg ) {
   uint32_t update_param[] = {(uint32_t) VC_HTOV32(update), (uint32_t) ((cb_func)? VC_HTOV32(1) : 0)};
   int success;

   vcos_assert(update); // handles must be non-zero
   if (update)
   {
      //Set the callback
      dispmanx_client.update_callback = cb_func;
      dispmanx_client.update_callback_param = cb_arg;
      dispmanx_client.pending_update_handle = update;
      vchi_service_use(dispmanx_client.notify_handle[0]); // corresponding release is in dispmanx_notify_func
      success = (int) dispmanx_send_command( EDispmanUpdateSubmit | DISPMANX_NO_REPLY_MASK,
                                             update_param, sizeof(update_param));
   }
   else
   {
      success = -1;
   }
   return success;
}

/***********************************************************
 * Name: vc_dispmanx_update_submit_sync
 *
 * Arguments:
 *       DISPMANX_UPDATE_HANDLE_T update
 *
 * Description:
 *
 * Returns: VCHI error code
 *
 ***********************************************************/
VCHPRE_ int VCHPOST_ vc_dispmanx_update_submit_sync( DISPMANX_UPDATE_HANDLE_T update ) {
   int success;
   update = VC_HTOV32(update);
   success = (int) dispmanx_send_command( EDispmanUpdateSubmitSync,
                                          &update, sizeof(update));
   return success;
}

/***********************************************************
 * Name: vc_dispmanx_element_add
 *
 * Arguments:
 *       DISPMANX_UPDATE_HANDLE_T update
 *       DISPMANX_DISPLAY_HANDLE_T display
 *       int32_t layer
 *       const VC_RECT_T *dest_rect
 *       DISPMANX_RESOURCE_HANDLE_T src
 *       const VC_RECT_T *src_rect
 *       DISPMANX_FLAGS_T flags
 *       uint8_t opacity
 *       DISPMANX_RESOURCE_HANDLE_T mask
 *       DISPMANX_TRANSFORM_T transform
 *
 * Description:
 *
 * Returns: VCHI error code
 *
 ***********************************************************/
VCHPRE_ DISPMANX_ELEMENT_HANDLE_T VCHPOST_ vc_dispmanx_element_add ( DISPMANX_UPDATE_HANDLE_T update,
                                                                     DISPMANX_DISPLAY_HANDLE_T display,
                                                                     int32_t layer,
                                                                     const VC_RECT_T *dest_rect,
                                                                     DISPMANX_RESOURCE_HANDLE_T src,
                                                                     const VC_RECT_T *src_rect,
                                                                     DISPMANX_PROTECTION_T protection,
                                                                     VC_DISPMANX_ALPHA_T *alpha,
                                                                     DISPMANX_CLAMP_T *clamp,
                                                                     DISPMANX_TRANSFORM_T transform ) {

   int32_t element_param[] = {
      (int32_t) VC_HTOV32(update),
      (int32_t) VC_HTOV32(display),
      (int32_t) VC_HTOV32(layer),
      (int32_t) VC_HTOV32(dest_rect->x),
      (int32_t) VC_HTOV32(dest_rect->y),
      (int32_t) VC_HTOV32(dest_rect->width),
      (int32_t) VC_HTOV32(dest_rect->height),
      (int32_t) VC_HTOV32(src),
      (int32_t) VC_HTOV32(src_rect->x),
      (int32_t) VC_HTOV32(src_rect->y),
      (int32_t) VC_HTOV32(src_rect->width),
      (int32_t) VC_HTOV32(src_rect->height),
      (int32_t) VC_HTOV32(protection),
      alpha ? (int32_t) VC_HTOV32(alpha->flags) : 0,
      alpha ? (int32_t) VC_HTOV32(alpha->opacity) : 0,
      alpha ? (int32_t) VC_HTOV32(alpha->mask) : 0,
      clamp ? (int32_t) VC_HTOV32(clamp->mode) : 0,
      clamp ? (int32_t) VC_HTOV32(clamp->key_mask) : 0,
      clamp ? (int32_t) VC_HTOV32(clamp->key_value.yuv.yy_upper) : 0,
      clamp ? (int32_t) VC_HTOV32(clamp->key_value.yuv.yy_lower) : 0,
      clamp ? (int32_t) VC_HTOV32(clamp->key_value.yuv.cr_upper) : 0,
      clamp ? (int32_t) VC_HTOV32(clamp->key_value.yuv.cr_lower) : 0,
      clamp ? (int32_t) VC_HTOV32(clamp->key_value.yuv.cb_upper) : 0,
      clamp ? (int32_t) VC_HTOV32(clamp->key_value.yuv.cb_lower) : 0,
      clamp ? (int32_t) VC_HTOV32(clamp->replace_value) : 0,
      (int32_t) VC_HTOV32(transform)
   };

   uint32_t handle =  dispmanx_get_handle(EDispmanElementAdd,
                                          element_param, sizeof(element_param));
   return (DISPMANX_ELEMENT_HANDLE_T) handle;
}

/***********************************************************
 * Name: vc_dispmanx_element_change_source
 *
 * Arguments:
 *       DISPMANX_UPDATE_HANDLE_T update
 *       DISPMANX_ELEMENT_HANDLE_T element
 *       DISPMANX_RESOURCE_HANDLE_T src
 *
 * Description:
 *
 * Returns: VCHI error code
 *
 ***********************************************************/
VCHPRE_ int VCHPOST_ vc_dispmanx_element_change_source( DISPMANX_UPDATE_HANDLE_T update, DISPMANX_ELEMENT_HANDLE_T element,
                                                        DISPMANX_RESOURCE_HANDLE_T src ) {
   uint32_t element_param[] = { (uint32_t) VC_HTOV32(update),
                                (uint32_t) VC_HTOV32(element),
                                (uint32_t) VC_HTOV32(src) };

   int success = (int) dispmanx_send_command( EDispmanElementChangeSource | DISPMANX_NO_REPLY_MASK,
                                              element_param, sizeof(element_param));
   return success;

}

/***********************************************************
 * Name: vc_dispmanx_element_change_layer
 *
 * Arguments:
 *       DISPMANX_UPDATE_HANDLE_T update
 *       DISPMANX_ELEMENT_HANDLE_T element
 *       int32_t layer
 *
 * Description:
 *
 * Returns: VCHI error code
 *
 ***********************************************************/
VCHPRE_ int VCHPOST_ vc_dispmanx_element_change_layer (DISPMANX_UPDATE_HANDLE_T update,
                                                       DISPMANX_ELEMENT_HANDLE_T element,
                                                       int32_t layer)
{
   uint32_t element_param[] = { (uint32_t) VC_HTOV32(update),
                                (uint32_t) VC_HTOV32(element),
                                (uint32_t) VC_HTOV32(layer) };

   int success = (int) dispmanx_send_command( EDispmanElementChangeLayer | DISPMANX_NO_REPLY_MASK,
                                              element_param, sizeof(element_param));
   return success;

}

/***********************************************************
 * Name: vc_dispmanx_element_modified
 *
 * Arguments:
 *       DISPMANX_UPDATE_HANDLE_T update
 *       DISPMANX_ELEMENT_HANDLE_T element
 *       const VC_RECT_T * rect
 *
 * Description:
 *
 * Returns: VCHI error code
 *
 ***********************************************************/
VCHPRE_ int VCHPOST_ vc_dispmanx_element_modified( DISPMANX_UPDATE_HANDLE_T update, DISPMANX_ELEMENT_HANDLE_T element, const VC_RECT_T * rect ) {

   uint32_t element_param[6] = { (uint32_t) VC_HTOV32(update),
                                 (uint32_t) VC_HTOV32(element), 0, 0, 0, 0};
   uint32_t param_length = 2*sizeof(uint32_t);
   int success;

   if(rect) {
      element_param[2] = VC_HTOV32(rect->x);
      element_param[3] = VC_HTOV32(rect->y);
      element_param[4] = VC_HTOV32(rect->width);
      element_param[5] = VC_HTOV32(rect->height);
      param_length = 6*sizeof(uint32_t);
   }
   success = (int) dispmanx_send_command( EDispmanElementModified | DISPMANX_NO_REPLY_MASK,
                                            element_param, param_length);
   return success;
}

/***********************************************************
 * Name: vc_dispmanx_element_remove
 *
 * Arguments:
 *       DISPMANX_UPDATE_HANDLE_T update
 *       DISPMANX_ELEMENT_HANDLE_T element
 *
 * Description:
 *
 * Returns:
 *
 ***********************************************************/
VCHPRE_ int VCHPOST_ vc_dispmanx_element_remove( DISPMANX_UPDATE_HANDLE_T update, DISPMANX_ELEMENT_HANDLE_T element ) {
   uint32_t element_param[] = {(uint32_t) VC_HTOV32(update), (uint32_t) VC_HTOV32(element)};
   int success = (int) dispmanx_send_command( EDispmanElementRemove | DISPMANX_NO_REPLY_MASK,
                                              element_param, sizeof(element_param));
   return success;
}

/***********************************************************
 * Name: vc_dispmanx_element_change_attributes
 *
 * Arguments:
 *       DISPMANX_UPDATE_HANDLE_T update
 *       DISPMANX_ELEMENT_HANDLE_T element
 *       uint32_t change flags (bit 0 layer, bit 1 opacity, bit 2 dest rect, bit 3 src rect, bit 4 mask, bit 5 transform
 *       uint32_t layer
 *       uint8_t opacity
 *       const VC_RECT_T *dest rect
 *       const VC_RECT_T *src rect
 *       DISPMANX_RESOURCE_HANDLE_T mask
 *       VC_DISPMAN_TRANSFORM_T transform
 *
 * Description:
 *
 * Returns:
 *
 ***********************************************************/
VCHPRE_ int VCHPOST_ vc_dispmanx_element_change_attributes( DISPMANX_UPDATE_HANDLE_T update,
                                                            DISPMANX_ELEMENT_HANDLE_T element,
                                                            uint32_t change_flags,
                                                            int32_t layer,
                                                            uint8_t opacity,
                                                            const VC_RECT_T *dest_rect,
                                                            const VC_RECT_T *src_rect,
                                                            DISPMANX_RESOURCE_HANDLE_T mask,
                                                            DISPMANX_TRANSFORM_T transform ) {

   uint32_t element_param[15] = { (uint32_t) VC_HTOV32(update),
                                  (uint32_t) VC_HTOV32(element),
                                  VC_HTOV32(change_flags),
                                  VC_HTOV32(layer),
                                  VC_HTOV32(opacity),
                                  (uint32_t) VC_HTOV32(mask),
                                  (uint32_t) VC_HTOV32(transform), 0, 0, 0, 0, 0, 0, 0, 0};

   uint32_t param_length = 7*sizeof(uint32_t);
   int success;
   if(dest_rect) {
      element_param[7]  = VC_HTOV32(dest_rect->x);
      element_param[8]  = VC_HTOV32(dest_rect->y);
      element_param[9]  = VC_HTOV32(dest_rect->width);
      element_param[10] = VC_HTOV32(dest_rect->height);
      element_param[2] |= ELEMENT_CHANGE_DEST_RECT;
      param_length += 4*sizeof(uint32_t);
   }
   if(src_rect) {
      element_param[11] = VC_HTOV32(src_rect->x);
      element_param[12] = VC_HTOV32(src_rect->y);
      element_param[13] = VC_HTOV32(src_rect->width);
      element_param[14] = VC_HTOV32(src_rect->height);
      element_param[2] |= ELEMENT_CHANGE_SRC_RECT;
      param_length += 4*sizeof(uint32_t);
   }

   success = (int) dispmanx_send_command( EDispmanElementChangeAttributes | DISPMANX_NO_REPLY_MASK,
                                            element_param, param_length);
   return success;
}

/***********************************************************
 * Name: vc_dispmanx_snapshot
 *
 * Arguments:
 *       DISPMANX_DISPLAY_HANDLE_T display
 *       DISPMANX_RESOURCE_HANDLE_T snapshot_resource
 *       DISPMANX_TRANSFORM_T transform
 *
 * Description: Take a snapshot of a display in its current state
 *
 * Returns:
 *
 ***********************************************************/
VCHPRE_ int VCHPOST_ vc_dispmanx_snapshot( DISPMANX_DISPLAY_HANDLE_T display,
                                           DISPMANX_RESOURCE_HANDLE_T snapshot_resource,
                                           DISPMANX_TRANSFORM_T transform )
{
   uint32_t display_snapshot_param[] = {
      VC_HTOV32(display),
      VC_HTOV32(snapshot_resource),
      VC_HTOV32(transform)};

   int success = (int) dispmanx_send_command( EDispmanSnapshot,
                                              display_snapshot_param,
                                              sizeof(display_snapshot_param));
   return success;
}

/***********************************************************
 * Name: vc_dispmanx_resource_set_palette
 *
 * Arguments:
 *       DISPMANX_RESOURCE_HANDLE_T res
 *       void * src_address
 *       int offset
 *       int size
 *
 * Description: Set the resource palette (for VC_IMAGE_4BPP and VC_IMAGE_8BPP)
 *              offset should be 0
 *              size is 16*2 for 4BPP and 256*2 for 8BPP
 * Returns: 0 or failure
 *
 ***********************************************************/
VCHPRE_ int VCHPOST_ vc_dispmanx_resource_set_palette( DISPMANX_RESOURCE_HANDLE_T handle,
                                                      void * src_address, int offset, int size) {
   //Note that x coordinate of the rect is NOT used
   //Address of data in host
   uint8_t *host_start = src_address;
   int32_t bulk_len = size, success = 0;

   //Now send the bulk transfer across
   //command parameters: resource size
   uint32_t param[] = {VC_HTOV32(handle), VC_HTOV32(offset), VC_HTOV32(bulk_len) };
   success = dispmanx_send_command(  EDispmanSetPalette | DISPMANX_NO_REPLY_MASK, param, sizeof(param));
   if(success == 0)
   {
      lock_obtain();
      success = vchi_bulk_queue_transmit( dispmanx_client.client_handle[0],
                                          host_start,
                                          bulk_len,
                                          VCHI_FLAGS_BLOCK_UNTIL_DATA_READ,
                                          NULL );
      lock_release();
   }
   return (int) success;
}

/***********************************************************
 * Name: vc_dispmanx_vsync_callback
 *
 * Arguments:
 *       DISPMANX_DISPLAY_HANDLE_T display
 *       DISPMANX_CALLBACK_FUNC_T cb_func
 *       void *cb_arg
 *
 * Description: start sending callbacks on vsync events
 *              Use a NULL cb_func to stop the callbacks
 * Returns: 0 or failure
 *
 ***********************************************************/
VCHPRE_ int VCHPOST_ vc_dispmanx_vsync_callback( DISPMANX_DISPLAY_HANDLE_T display, DISPMANX_CALLBACK_FUNC_T cb_func, void *cb_arg )
{
   // Steal the invalid 0 handle to indicate this is a vsync request
   DISPMANX_UPDATE_HANDLE_T update = 0;
   int enable = (cb_func != NULL);
   uint32_t update_param[] = {(uint32_t) VC_HTOV32(display), VC_HTOV32(update), (int32_t)enable};
   int success;

   // Set the callback
   dispmanx_client.vsync_callback = cb_func;
   dispmanx_client.vsync_callback_param = cb_arg;

   if (!dispmanx_client.vsync_enabled && enable) {
      // An extra "use" is required while a vsync callback is registered.
      // The corresponding "release" is below.
      vchi_service_use(dispmanx_client.notify_handle[0]);
   }

   success = (int) dispmanx_send_command( EDispmanVsyncCallback | DISPMANX_NO_REPLY_MASK,
                                          update_param, sizeof(update_param));

   if (dispmanx_client.vsync_enabled && !enable) {
      // The extra "use" added above is no longer required.
      vchi_service_release(dispmanx_client.notify_handle[0]);
   }

   dispmanx_client.vsync_enabled = enable;

   return (int) success;
}

/*********************************************************************************
 *
 *  Static functions definitions
 *
 *********************************************************************************/
//TODO: Might need to handle multiple connections later
/***********************************************************
 * Name: dispmanx_client_callback
 *
 * Arguments: semaphore, callback reason and message handle
 *
 * Description: VCHI callback for the DISP service
 *
 ***********************************************************/
static void dispmanx_client_callback( void *callback_param,
                                      const VCHI_CALLBACK_REASON_T reason,
                                      void *msg_handle ) {

	(void)msg_handle;

   VCOS_EVENT_T *event = (VCOS_EVENT_T *)callback_param;

   if ( reason != VCHI_CALLBACK_MSG_AVAILABLE )
      return;

   if ( event == NULL )
      return;

   vcos_event_signal(event);
}

/***********************************************************
 * Name: dispmanx_notify_callback
 *
 * Arguments: semaphore, callback reason and message handle
 *
 * Description: VCHI callback for the update callback
 *
 ***********************************************************/

static void dispmanx_notify_callback( void *callback_param,
                                      const VCHI_CALLBACK_REASON_T reason,
                                      void *msg_handle ) {
   VCOS_EVENT_T *event = (VCOS_EVENT_T *)callback_param;

   (void)msg_handle;

   if ( reason != VCHI_CALLBACK_MSG_AVAILABLE )
      return;

   if ( event == NULL )
      return;

   vcos_event_signal(event);
}

/***********************************************************
 * Name: dispmanx_wait_for_reply
 *
 * Arguments: response buffer, buffer length
 *
 * Description: blocked until something is in the buffer
 *
 * Returns error code of vchi
 *
 ***********************************************************/
static int32_t dispmanx_wait_for_reply(void *response, uint32_t max_length) {
   int32_t success = 0;
   uint32_t length_read = 0;
   do {
      //TODO : we need to deal with messages coming through on more than one connections properly
      //At the moment it will always try to read the first connection if there is something there
      //Check if there is something in the queue, if so return immediately
      //otherwise wait for the semaphore and read again
      success = vchi_msg_dequeue( dispmanx_client.client_handle[0], response, max_length, &length_read, VCHI_FLAGS_NONE );
   } while( length_read == 0 && vcos_event_wait(&dispmanx_message_available_event) == VCOS_SUCCESS );

   return success;

}
/***********************************************************
 * Name: dispmanx_send_command
 *
 * Arguments: command, parameter buffer, parameter legnth
 *
 * Description: send a command and wait for its response (int)
 *
 * Returns: response
 *
 ***********************************************************/

static int32_t dispmanx_send_command(  uint32_t command, void *buffer, uint32_t length) {
   VCHI_MSG_VECTOR_T vector[] = { {&command, sizeof(command)},
                                  {buffer, length} };
   int32_t success = 0, response = -1;
   lock_obtain();
   success = vchi_msg_queuev( dispmanx_client.client_handle[0],
                              vector, sizeof(vector)/sizeof(vector[0]),
                              VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL );
   if(success == 0 && !(command & DISPMANX_NO_REPLY_MASK)) {
      //otherwise only wait for a reply if we ask for one
      success = dispmanx_wait_for_reply(&response, sizeof(response));
   } else {
      //Not waiting for a reply, send the success code back instead
      response = success;
   }
   lock_release();
   return VC_VTOH32(response);
}

int32_t vc_dispmanx_send_command (uint32_t command, void *buffer,
                                  uint32_t length)
{
  return dispmanx_send_command (command, buffer, length);
}

/***********************************************************
 * Name: dispmanx_send_command_reply
 *
 * Arguments: command, parameter buffer, parameter legnth, reply buffer, buffer length
 *
 * Description: send a command and wait for its response (in a buffer)
 *
 * Returns: error code
 *
 ***********************************************************/

static int32_t dispmanx_send_command_reply(  uint32_t command, void *buffer, uint32_t length,
                                            void *response, uint32_t max_length) {
   VCHI_MSG_VECTOR_T vector[] = { {&command, sizeof(command)},
                                   {buffer, length} };

   int32_t success = 0;
   lock_obtain();
   success = vchi_msg_queuev( dispmanx_client.client_handle[0],
                               vector, sizeof(vector)/sizeof(vector[0]),
                               VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL );
   if(success == 0)
      success = dispmanx_wait_for_reply(response, max_length);

   lock_release();

   return success;
}

int32_t vc_dispmanx_send_command_reply (uint32_t command, void *buffer, uint32_t length,
                                        void *response, uint32_t max_length)
{
  return dispmanx_send_command_reply (command, buffer, length, response, max_length);
}

/***********************************************************
 * Name: dispmanx_get_handle
 *
 * Arguments: command, parameter buffer, parameter legnth
 *
 * Description: same as dispmanx_send_command but returns uint instead
 *
 * Returns: handle
 *
 ***********************************************************/
static uint32_t dispmanx_get_handle(  uint32_t command, void *buffer, uint32_t length) {
   VCHI_MSG_VECTOR_T vector[] = { {&command, sizeof(command)},
                                   {buffer, length} };
   uint32_t success = 0;
   uint32_t response = 0;
   lock_obtain();
   success += vchi_msg_queuev( dispmanx_client.client_handle[0],
                               vector, sizeof(vector)/sizeof(vector[0]),
                               VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL );
   if(success == 0)
      success = dispmanx_wait_for_reply(&response, sizeof(response));

   lock_release();
   return VC_VTOH32(response);
}

/***********************************************************
 * Name: dispmanx_notify_handle
 *
 * Arguments: not used
 *
 * Description: this purely notifies the update callback
 *
 * Returns: does not return
 *
 ***********************************************************/
static void *dispmanx_notify_func( void *arg ) {
   int32_t success;
   VCOS_STATUS_T status;

   (void)arg;

   while (1) {
      DISPMANX_UPDATE_HANDLE_T handle;
      status = vcos_event_wait(&dispmanx_notify_available_event);
      if (status != VCOS_SUCCESS || !dispmanx_client.initialised)
         break;

      while (1) {
         success = vchi_msg_dequeue( dispmanx_client.notify_handle[0], dispmanx_client.notify_buffer, sizeof(dispmanx_client.notify_buffer), &dispmanx_client.notify_length, VCHI_FLAGS_NONE );
         if (success != 0)
            break;

         handle = (DISPMANX_UPDATE_HANDLE_T)dispmanx_client.notify_buffer[0];
         if (handle) {
            // This is the response to an update submit
            // Decrement the use count - the corresponding "use" is in vc_dispmanx_update_submit.
            vchi_service_release(dispmanx_client.notify_handle[0]);
            if (dispmanx_client.update_callback ) {
               vcos_assert( dispmanx_client.pending_update_handle == handle);
               dispmanx_client.update_callback(handle, dispmanx_client.update_callback_param);
            }
         } else {
            // This is a vsync notification
            if (dispmanx_client.vsync_callback ) {
               dispmanx_client.vsync_callback(handle, dispmanx_client.vsync_callback_param);
            }
         }
      }
   }
   return 0;
}
