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