sde: Add support for DisplayEventHandler

- Add DisplayEventHandler to receive VSync event from driver.
- Add hooks to enable/disable VSync from client.
- Propogate VSync timestamp to the client

Change-Id: I27ea9773c424d8ad85f11f292ddd8792564bbcdc
diff --git a/displayengine/include/core/device_interface.h b/displayengine/include/core/device_interface.h
index 60d7667..01c5446 100644
--- a/displayengine/include/core/device_interface.h
+++ b/displayengine/include/core/device_interface.h
@@ -274,7 +274,7 @@
 
     @return \link DisplayError \endlink
   */
-  virtual DisplayError SetVSyncState(bool enabled) = 0;
+  virtual DisplayError SetVSyncState(bool enable) = 0;
 
  protected:
   virtual ~DeviceInterface() { }
diff --git a/displayengine/include/utils/constants.h b/displayengine/include/utils/constants.h
index 658c852..4b18ed6 100644
--- a/displayengine/include/utils/constants.h
+++ b/displayengine/include/utils/constants.h
@@ -45,6 +45,8 @@
 
 namespace sde {
 
+  const int kThreadPriorityUrgent = -9;
+
   typedef void * Handle;
 
 }  // namespace sde
diff --git a/displayengine/libs/core/device_base.cpp b/displayengine/libs/core/device_base.cpp
index 8854098..f991903 100644
--- a/displayengine/libs/core/device_base.cpp
+++ b/displayengine/libs/core/device_base.cpp
@@ -38,7 +38,7 @@
   : device_type_(device_type), event_handler_(event_handler), hw_block_type_(hw_block_type),
     hw_intf_(hw_intf), comp_manager_(comp_manager), state_(kStateOff), hw_device_(0),
     comp_mgr_device_(0), device_attributes_(NULL), num_modes_(0), active_mode_index_(0),
-    pending_commit_(false) {
+    pending_commit_(false), vsync_enable_(false) {
 }
 
 DisplayError DeviceBase::Init() {
@@ -46,7 +46,7 @@
 
   DisplayError error = kErrorNone;
 
-  error = hw_intf_->Open(hw_block_type_, &hw_device_);
+  error = hw_intf_->Open(hw_block_type_, &hw_device_, this);
   if (UNLIKELY(error != kErrorNone)) {
     return error;
   }
@@ -267,9 +267,30 @@
   return kErrorNone;
 }
 
-DisplayError DeviceBase::SetVSyncState(bool enabled) {
+DisplayError DeviceBase::SetVSyncState(bool enable) {
   SCOPE_LOCK(locker_);
+  DisplayError error = kErrorNone;
+  if (vsync_enable_ != enable) {
+    error = hw_intf_->SetVSyncState(hw_device_, enable);
+    if (error == kErrorNone) {
+      vsync_enable_ = enable;
+    }
+  }
 
+  return error;
+}
+
+DisplayError DeviceBase::VSync(int64_t timestamp) {
+  if (vsync_enable_) {
+    DeviceEventVSync vsync;
+    vsync.timestamp = timestamp;
+    event_handler_->VSync(vsync);
+  }
+
+  return kErrorNone;
+}
+
+DisplayError DeviceBase::Blank(bool blank) {
   return kErrorNone;
 }
 
diff --git a/displayengine/libs/core/device_base.h b/displayengine/libs/core/device_base.h
index 6693142..458983c 100644
--- a/displayengine/libs/core/device_base.h
+++ b/displayengine/libs/core/device_base.h
@@ -34,7 +34,7 @@
 
 namespace sde {
 
-class DeviceBase : public DeviceInterface, DumpImpl {
+class DeviceBase : public DeviceInterface, DumpImpl, HWEventHandler   {
  public:
   DeviceBase(DeviceType device_type, DeviceEventHandler *event_handler,
              HWBlockType hw_block_type, HWInterface *hw_intf, CompManager *comp_manager);
@@ -50,11 +50,15 @@
   virtual DisplayError GetVSyncState(bool *enabled);
   virtual DisplayError SetDeviceState(DeviceState state);
   virtual DisplayError SetConfig(uint32_t mode);
-  virtual DisplayError SetVSyncState(bool enabled);
+  virtual DisplayError SetVSyncState(bool enable);
 
   // DumpImpl method
   virtual uint32_t GetDump(uint8_t *buffer, uint32_t length);
 
+  // Implement the HWEventHandlers
+  virtual DisplayError VSync(int64_t timestamp);
+  virtual DisplayError Blank(bool blank);
+
  protected:
   Locker locker_;
   DeviceType device_type_;
@@ -70,6 +74,7 @@
   uint32_t active_mode_index_;
   HWLayers hw_layers_;
   bool pending_commit_;
+  bool vsync_enable_;
 };
 
 }  // namespace sde
diff --git a/displayengine/libs/core/hw_framebuffer.cpp b/displayengine/libs/core/hw_framebuffer.cpp
index 3d0078b..3057fa2 100644
--- a/displayengine/libs/core/hw_framebuffer.cpp
+++ b/displayengine/libs/core/hw_framebuffer.cpp
@@ -35,6 +35,9 @@
 #include <sys/stat.h>
 #include <sys/ioctl.h>
 #include <linux/fb.h>
+#include <sys/resource.h>
+#include <sys/prctl.h>
+#include <pthread.h>
 #include <utils/constants.h>
 
 #include "hw_framebuffer.h"
@@ -45,15 +48,20 @@
 extern int virtual_ioctl(int fd, int cmd, ...);
 extern int virtual_open(const char *file_name, int access, ...);
 extern int virtual_close(int fd);
+extern int virtual_poll(struct pollfd *fds,  nfds_t num, int timeout);
+extern ssize_t virtual_pread(int fd, void *data, size_t count, off_t offset);
 #endif
 
 namespace sde {
 
-HWFrameBuffer::HWFrameBuffer() {
-  // Point to actual driver interfaces.
+HWFrameBuffer::HWFrameBuffer() : event_thread_name_("SDE_EventThread"), fake_vsync_(false),
+                                 exit_threads_(false) {
+  // Pointer to actual driver interfaces.
   ioctl_ = ::ioctl;
   open_ = ::open;
   close_ = ::close;
+  poll_ = ::poll;
+  pread_ = ::pread;
 
 #ifdef DISPLAY_CORE_VIRTUAL_DRIVER
   // If debug property to use virtual driver is set, point to virtual driver interfaces.
@@ -61,15 +69,83 @@
     ioctl_ = virtual_ioctl;
     open_ = virtual_open;
     close_ = virtual_close;
+    poll_ = virtual_poll;
+    pread_ = virtual_pread;
   }
 #endif
 }
 
 DisplayError HWFrameBuffer::Init() {
+  DisplayError error = kErrorNone;
+
+  // TODO(user): Need to read the fbnode info, hw capabilities here
+
+  // Open nodes for polling
+  char node_path[kMaxStringLength] = {0};
+  char data[kMaxStringLength] = {0};
+  const char* event_name[kNumDisplayEvents] = {"vsync_event", "show_blank_event"};
+
+  for (int display = 0; display < kNumPhysicalDisplays; display++) {
+    for (int event = 0; event < kNumDisplayEvents; event++) {
+      poll_fds_[display][event].fd = -1;
+    }
+  }
+
+  if (!fake_vsync_) {
+    for (int display = 0; display < kNumPhysicalDisplays; display++) {
+      for (int event = 0; event < kNumDisplayEvents; event++) {
+        pollfd &poll_fd = poll_fds_[display][event];
+
+        snprintf(node_path, sizeof(node_path), "/sys/class/graphics/fb%d/%s",
+                (display == 0) ? 0 : 1/*TODO(user): HDMI fb node index*/, event_name[event]);
+
+        poll_fd.fd = open_(node_path, O_RDONLY);
+        if (poll_fd.fd < 0) {
+          DLOGE("open failed for display=%d event=%zu, error=%s", display, event, strerror(errno));
+          error = kErrorHardware;
+          goto CleanupOnError;
+        }
+
+        // Read once on all fds to clear data on all fds.
+        pread_(poll_fd.fd, data , kMaxStringLength, 0);
+        poll_fd.events = POLLPRI | POLLERR;
+      }
+    }
+  }
+
+  // Start the Event thread
+  if (pthread_create(&event_thread_, NULL, &DisplayEventThread, this) < 0) {
+    DLOGE("Failed to start %s, error = %s", event_thread_name_);
+    error = kErrorResources;
+    goto CleanupOnError;
+  }
+
   return kErrorNone;
+
+CleanupOnError:
+  // Close all poll fds
+  for (int display = 0; display < kNumPhysicalDisplays; display++) {
+    for (int event = 0; event < kNumDisplayEvents; event++) {
+      int &fd = poll_fds_[display][event].fd;
+      if (fd >= 0) {
+        close_(fd);
+      }
+    }
+  }
+
+  return error;
 }
 
 DisplayError HWFrameBuffer::Deinit() {
+  exit_threads_ = true;
+  pthread_join(event_thread_, NULL);
+
+  for (int display = 0; display < kNumPhysicalDisplays; display++) {
+    for (int event = 0; event < kNumDisplayEvents; event++) {
+      close(poll_fds_[display][event].fd);
+    }
+  }
+
   return kErrorNone;
 }
 
@@ -91,7 +167,7 @@
   return kErrorNone;
 }
 
-DisplayError HWFrameBuffer::Open(HWBlockType type, Handle *device) {
+DisplayError HWFrameBuffer::Open(HWBlockType type, Handle *device, HWEventHandler* eventhandler) {
   DisplayError error = kErrorNone;
 
   HWContext *hw_context = new HWContext();
@@ -119,6 +195,8 @@
   }
 
   *device = hw_context;
+  event_handler_[device_id] = eventhandler;
+
   return error;
 }
 
@@ -216,6 +294,17 @@
   return kErrorNone;
 }
 
+DisplayError HWFrameBuffer::SetVSyncState(Handle device, bool enable) {
+  HWContext *hw_context = reinterpret_cast<HWContext *>(device);
+  int vsync_on = enable ? 1 : 0;
+  if (ioctl_(hw_context->device_fd, MSMFB_OVERLAY_VSYNC_CTRL, &vsync_on) == -1) {
+    IOCTL_LOGE(MSMFB_OVERLAY_VSYNC_CTRL);
+    return kErrorHardware;
+  }
+
+  return kErrorNone;
+}
+
 DisplayError HWFrameBuffer::Validate(Handle device, HWLayers *hw_layers) {
   HWContext *hw_context = reinterpret_cast<HWContext *>(device);
 
@@ -357,5 +446,86 @@
   target->h = INT(floorf(source.bottom)) - target->y;
 }
 
-}  // namespace sde
+void* HWFrameBuffer::DisplayEventThread(void *context) {
+  if (context) {
+    return reinterpret_cast<HWFrameBuffer *>(context)->DisplayEventThreadHandler();
+  }
 
+  return NULL;
+}
+
+void* HWFrameBuffer::DisplayEventThreadHandler() {
+  char data[kMaxStringLength] = {0};
+
+  prctl(PR_SET_NAME, event_thread_name_, 0, 0, 0);
+  setpriority(PRIO_PROCESS, 0, kThreadPriorityUrgent);
+
+  if (fake_vsync_) {
+    while (!exit_threads_) {
+      // Fake vsync is used only when set explicitly through a property(todo) or when
+      // the vsync timestamp node cannot be opened at bootup. There is no
+      // fallback to fake vsync from the true vsync loop, ever, as the
+      // condition can easily escape detection.
+      // Also, fake vsync is delivered only for the primary display.
+      usleep(16666);
+      STRUCT_VAR(timeval, time_now);
+      gettimeofday(&time_now, NULL);
+      uint64_t ts = uint64_t(time_now.tv_sec)*1000000000LL +uint64_t(time_now.tv_usec)*1000LL;
+
+      // Send Vsync event for primary display(0)
+      event_handler_[0]->VSync(ts);
+    }
+
+    pthread_exit(0);
+  }
+
+  typedef void (HWFrameBuffer::*EventHandler)(int, char*);
+  EventHandler event_handler[kNumDisplayEvents] = { &HWFrameBuffer::HandleVSync,
+                                                    &HWFrameBuffer::HandleBlank };
+
+  while (!exit_threads_) {
+    int error = poll_(poll_fds_[0], kNumPhysicalDisplays * kNumDisplayEvents, -1);
+    if (error < 0) {
+      DLOGE("poll failed errno: %s", strerror(errno));
+      continue;
+    }
+
+    for (int display = 0; display < kNumPhysicalDisplays; display++) {
+      for (int event = 0; event < kNumDisplayEvents; event++) {
+        pollfd &poll_fd = poll_fds_[display][event];
+
+        if (poll_fd.revents & POLLPRI) {
+          ssize_t length = pread_(poll_fd.fd, data, kMaxStringLength, 0);
+          if (length < 0) {
+            // If the read was interrupted - it is not a fatal error, just continue.
+            DLOGE("Failed to read event:%zu for display=%d : %s", event, display, strerror(errno));
+            continue;
+          }
+
+          (this->*event_handler[event])(display, data);
+        }
+      }
+    }
+  }
+
+  pthread_exit(0);
+
+  return NULL;
+}
+
+void HWFrameBuffer::HandleVSync(int display_id, char *data) {
+  int64_t timestamp = 0;
+  if (!strncmp(data, "VSYNC=", strlen("VSYNC="))) {
+    timestamp = strtoull(data + strlen("VSYNC="), NULL, 0);
+  }
+  event_handler_[display_id]->VSync(timestamp);
+
+  return;
+}
+
+void HWFrameBuffer::HandleBlank(int display_id, char* data) {
+  // TODO(user): Need to send blank Event
+  return;
+}
+
+}  // namespace sde
diff --git a/displayengine/libs/core/hw_framebuffer.h b/displayengine/libs/core/hw_framebuffer.h
index ebd8ccb..b64e869 100644
--- a/displayengine/libs/core/hw_framebuffer.h
+++ b/displayengine/libs/core/hw_framebuffer.h
@@ -26,6 +26,8 @@
 #define __HW_FRAMEBUFFER_H__
 
 #include <linux/msm_mdp.h>
+#include <poll.h>
+#include <pthread.h>
 #include "hw_interface.h"
 
 namespace sde {
@@ -36,14 +38,15 @@
   DisplayError Init();
   DisplayError Deinit();
   virtual DisplayError GetHWCapabilities(HWResourceInfo *hw_res_info);
-  virtual DisplayError Open(HWBlockType type, Handle *device);
+  virtual DisplayError Open(HWBlockType type, Handle *device, HWEventHandler *eventhandler);
   virtual DisplayError Close(Handle device);
   virtual DisplayError GetNumDeviceAttributes(Handle device, uint32_t *count);
   virtual DisplayError GetDeviceAttributes(Handle device, HWDeviceAttributes *device_attributes,
-                                       uint32_t mode);
+                                           uint32_t mode);
   virtual DisplayError PowerOn(Handle device);
   virtual DisplayError PowerOff(Handle device);
   virtual DisplayError Doze(Handle device);
+  virtual DisplayError SetVSyncState(Handle device, bool enable);
   virtual DisplayError Standby(Handle device);
   virtual DisplayError Validate(Handle device, HWLayers *hw_layers);
   virtual DisplayError Commit(Handle device, HWLayers *hw_layers);
@@ -54,14 +57,40 @@
     int device_fd;
   };
 
+  enum {
+    kHWEventVSync,
+    kHWEventBlank,
+  };
+
+  static const int kMaxStringLength = 1024;
+  static const int kNumPhysicalDisplays = 2;
+  static const int kNumDisplayEvents = 2;
+
   inline void SetFormat(uint32_t *target, const LayerBufferFormat &source);
   inline void SetBlending(uint32_t *target, const LayerBlending &source);
   inline void SetRect(mdp_rect *target, const LayerRect &source);
 
-  // For dynamically linking virtual driver
+  // Event Thread to receive vsync/blank events
+  static void* DisplayEventThread(void *context);
+  void* DisplayEventThreadHandler();
+
+  void HandleVSync(int display_id, char *data);
+  void HandleBlank(int display_id, char *data);
+
+  // Pointers to system calls which are either mapped to actual system call or virtual driver.
   int (*ioctl_)(int, int, ...);
   int (*open_)(const char *, int, ...);
   int (*close_)(int);
+  int (*poll_)(struct pollfd *, nfds_t, int);
+  ssize_t (*pread_)(int, void *, size_t, off_t);
+
+  // Store the Device EventHandlers - used for callback
+  HWEventHandler *event_handler_[kNumPhysicalDisplays];
+  pollfd poll_fds_[kNumPhysicalDisplays][kNumDisplayEvents];
+  pthread_t event_thread_;
+  const char *event_thread_name_;
+  bool fake_vsync_;
+  bool exit_threads_;
 };
 
 }  // namespace sde
diff --git a/displayengine/libs/core/hw_interface.h b/displayengine/libs/core/hw_interface.h
index 9653424..ddf631e 100644
--- a/displayengine/libs/core/hw_interface.h
+++ b/displayengine/libs/core/hw_interface.h
@@ -99,12 +99,21 @@
   HWDeviceAttributes() : is_device_split(false), split_left(0) { }
 };
 
+// HWEventHandler - Implemented in DeviceBase and HWInterface implementation
+class HWEventHandler {
+ public:
+  virtual DisplayError VSync(int64_t timestamp) = 0;
+  virtual DisplayError Blank(bool blank)= 0;
+ protected:
+  virtual ~HWEventHandler() {}
+};
+
 class HWInterface {
  public:
   static DisplayError Create(HWInterface **intf);
   static DisplayError Destroy(HWInterface *intf);
   virtual DisplayError GetHWCapabilities(HWResourceInfo *hw_res_info) = 0;
-  virtual DisplayError Open(HWBlockType type, Handle *device) = 0;
+  virtual DisplayError Open(HWBlockType type, Handle *device, HWEventHandler *eventhandler) = 0;
   virtual DisplayError Close(Handle device) = 0;
   virtual DisplayError GetNumDeviceAttributes(Handle device, uint32_t *count) = 0;
   virtual DisplayError GetDeviceAttributes(Handle device, HWDeviceAttributes *device_attributes,
@@ -112,6 +121,7 @@
   virtual DisplayError PowerOn(Handle device) = 0;
   virtual DisplayError PowerOff(Handle device) = 0;
   virtual DisplayError Doze(Handle device) = 0;
+  virtual DisplayError SetVSyncState(Handle device, bool enable) = 0;
   virtual DisplayError Standby(Handle device) = 0;
   virtual DisplayError Validate(Handle device, HWLayers *hw_layers) = 0;
   virtual DisplayError Commit(Handle device, HWLayers *hw_layers) = 0;
diff --git a/displayengine/libs/hwc/hwc_sink.cpp b/displayengine/libs/hwc/hwc_sink.cpp
index 2041944..e5b07f5 100644
--- a/displayengine/libs/hwc/hwc_sink.cpp
+++ b/displayengine/libs/hwc/hwc_sink.cpp
@@ -144,12 +144,18 @@
 }
 
 DisplayError HWCSink::VSync(const DeviceEventVSync &vsync) {
-  (*hwc_procs_)->vsync(*hwc_procs_, id_, vsync.timestamp);
+  if (*hwc_procs_) {
+    (*hwc_procs_)->vsync(*hwc_procs_, id_, vsync.timestamp);
+  }
+
   return kErrorNone;
 }
 
 DisplayError HWCSink::Refresh() {
-  (*hwc_procs_)->invalidate(*hwc_procs_);
+  if (*hwc_procs_) {
+    (*hwc_procs_)->invalidate(*hwc_procs_);
+  }
+
   return kErrorNone;
 }