hwc: Add support for HDMI vsync
SurfaceFlinger can enable HDMI vsync through event control.
Poll for HDMI vsync events and report them to SurfaceFlinger.
Change-Id: I301f1dffbab56b3b5a3dda5a59c37721688d7849
diff --git a/libexternal/external.h b/libexternal/external.h
index d35490f..7ab7c3f 100644
--- a/libexternal/external.h
+++ b/libexternal/external.h
@@ -58,6 +58,7 @@
int configureWFDDisplay();
int teardownHDMIDisplay();
int teardownWFDDisplay();
+ int getHDMIIndex() { return mHdmiFbNum; }
private:
void setSPDInfo(const char* node, const char* property);
diff --git a/libhwcomposer/hwc_vsync.cpp b/libhwcomposer/hwc_vsync.cpp
index 2f4475e..e4da4f7 100644
--- a/libhwcomposer/hwc_vsync.cpp
+++ b/libhwcomposer/hwc_vsync.cpp
@@ -33,6 +33,7 @@
namespace qhwc {
#define HWC_VSYNC_THREAD_NAME "hwcVsyncThread"
+#define MAX_SYSFS_FILE_PATH 255
int hwc_vsync_control(hwc_context_t* ctx, int dpy, int enable)
{
@@ -49,10 +50,6 @@
static void *vsync_loop(void *param)
{
- const char* vsync_timestamp_fb0 = "/sys/class/graphics/fb0/vsync_event";
- const char* vsync_timestamp_fb1 = "/sys/class/graphics/fb1/vsync_event";
- int dpy = HWC_DISPLAY_PRIMARY;
-
hwc_context_t * ctx = reinterpret_cast<hwc_context_t *>(param);
char thread_name[64] = HWC_VSYNC_THREAD_NAME;
@@ -61,16 +58,14 @@
android::PRIORITY_MORE_FAVORABLE);
const int MAX_DATA = 64;
- static char vdata[MAX_DATA];
- struct pollfd pfd;
-
- uint64_t cur_timestamp=0;
- ssize_t len = -1;
- int fb0_fd = -1;
- int ret = 0;
- bool fb1_vsync = false;
+ char vdata[MAX_DATA];
bool logvsync = false;
+ struct pollfd pfd[2];
+ int fb_fd[2];
+ uint64_t timestamp[2];
+ int num_displays;
+
char property[PROPERTY_VALUE_MAX];
if(property_get("debug.hwc.fakevsync", property, NULL) > 0) {
if(atoi(property) == 1)
@@ -82,62 +77,91 @@
logvsync = true;
}
- /* Currently read vsync timestamp from drivers
- e.g. VSYNC=41800875994
- */
- fb0_fd = open(vsync_timestamp_fb0, O_RDONLY);
- pfd.fd = fb0_fd;
- pfd.events = POLLPRI | POLLERR;
+ if (ctx->mExtDisplay->getHDMIIndex() > 0)
+ num_displays = 2;
+ else
+ num_displays = 1;
- if (fb0_fd < 0) {
- // Make sure fb device is opened before starting this thread so this
- // never happens.
- ALOGE ("FATAL:%s:not able to open file:%s, %s", __FUNCTION__,
- (fb1_vsync) ? vsync_timestamp_fb1 : vsync_timestamp_fb0,
- strerror(errno));
- ctx->vstate.fakevsync = true;
+ char vsync_node_path[MAX_SYSFS_FILE_PATH];
+ for (int dpy = HWC_DISPLAY_PRIMARY; dpy < num_displays; dpy++) {
+ snprintf(vsync_node_path, sizeof(vsync_node_path),
+ "/sys/class/graphics/fb%d/vsync_event",
+ dpy == HWC_DISPLAY_PRIMARY ? 0 :
+ ctx->mExtDisplay->getHDMIIndex());
+ ALOGI("%s: Reading vsync for dpy=%d from %s", __FUNCTION__, dpy,
+ vsync_node_path);
+ fb_fd[dpy] = open(vsync_node_path, O_RDONLY);
+
+ if (fb_fd[dpy] < 0) {
+ // Make sure fb device is opened before starting this thread so this
+ // never happens.
+ ALOGE ("%s:not able to open vsync node for dpy=%d, %s",
+ __FUNCTION__, dpy, strerror(errno));
+ if (dpy == HWC_DISPLAY_PRIMARY) {
+ ctx->vstate.fakevsync = true;
+ break;
+ }
+ }
+
+ pfd[dpy].fd = fb_fd[dpy];
+ if (pfd[dpy].fd >= 0)
+ pfd[dpy].events = POLLPRI | POLLERR;
}
- do {
- if (LIKELY(!ctx->vstate.fakevsync)) {
- int err = poll(&pfd, 1, -1);
+ if (LIKELY(!ctx->vstate.fakevsync)) {
+ do {
+ int err = poll(pfd, num_displays, -1);
if(err > 0) {
- if (pfd.revents & POLLPRI) {
- len = pread(fb0_fd, vdata, MAX_DATA, 0);
- if (UNLIKELY(len < 0)) {
- // If the read was just interrupted - it is not a fatal
- // error. Just continue in this case
- ALOGE ("FATAL:%s:not able to read file:%s, %s",
- __FUNCTION__,
- vsync_timestamp_fb0, strerror(errno));
- continue;
- }
- // extract timestamp
- const char *str = vdata;
- if (!strncmp(str, "VSYNC=", strlen("VSYNC="))) {
- cur_timestamp = strtoull(str + strlen("VSYNC="),
- NULL, 0);
+ for (int dpy = HWC_DISPLAY_PRIMARY; dpy < num_displays; dpy++) {
+ if (pfd[dpy].revents & POLLPRI) {
+ int len = pread(pfd[dpy].fd, vdata, MAX_DATA, 0);
+ if (UNLIKELY(len < 0)) {
+ // If the read was just interrupted - it is not a
+ // fatal error. Just continue in this case
+ ALOGE ("%s:Unable to read vsync for dpy=%d :%s",
+ __FUNCTION__, dpy, strerror(errno));
+ continue;
+ }
+ // extract timestamp
+ if (!strncmp(vdata, "VSYNC=", strlen("VSYNC="))) {
+ timestamp[dpy] = strtoull(vdata + strlen("VSYNC="),
+ NULL, 0);
+ }
+ // send timestamp to SurfaceFlinger
+ ALOGD_IF (logvsync,
+ "%s: timestamp %llu sent to SF for dpy=%d",
+ __FUNCTION__, timestamp[dpy], dpy);
+ ctx->proc->vsync(ctx->proc, dpy, timestamp[dpy]);
}
}
+
} else {
ALOGE("%s: vsync poll failed errno: %s", __FUNCTION__,
- strerror(errno));
+ strerror(errno));
continue;
}
- } else {
- usleep(16666);
- cur_timestamp = systemTime();
- }
- // send timestamp to HAL
- if(ctx->vstate.enable) {
- ALOGD_IF (logvsync, "%s: timestamp %llu sent to HWC for %s",
- __FUNCTION__, cur_timestamp, "fb0");
- ctx->proc->vsync(ctx->proc, dpy, cur_timestamp);
- }
+ } while (true);
- } while (true);
- if(fb0_fd >= 0)
- close (fb0_fd);
+ } else {
+
+ //Fake vsync is used only when set explicitly through a property 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.
+ do {
+ usleep(16666);
+ timestamp[HWC_DISPLAY_PRIMARY] = systemTime();
+ ctx->proc->vsync(ctx->proc, HWC_DISPLAY_PRIMARY,
+ timestamp[HWC_DISPLAY_PRIMARY]);
+
+ } while (true);
+ }
+
+ for (int dpy = HWC_DISPLAY_PRIMARY; dpy <= HWC_DISPLAY_EXTERNAL; dpy++ ) {
+ if(fb_fd[dpy] >= 0)
+ close (fb_fd[dpy]);
+ }
return NULL;
}