hwc: enable vsync for external display

- wait for hdmi vsync when connected to hdmi
- add commit to call PANDISPLAY for updating ext display
- add functions to close fb and reset info

Change-Id: I49afbeb85935b111055a872b29a1f65d87ab72c9
Acked-by: Arun Kumar K.R <akumarkr@codeaurora.org>
diff --git a/libhwcomposer/hwc.cpp b/libhwcomposer/hwc.cpp
index c0bca9d..ba87f90 100644
--- a/libhwcomposer/hwc.cpp
+++ b/libhwcomposer/hwc.cpp
@@ -29,6 +29,7 @@
 #include "hwc_video.h"
 #include "hwc_uimirror.h"
 #include "hwc_copybit.h"
+#include "hwc_external.h"
 
 using namespace qhwc;
 
@@ -108,6 +109,10 @@
         case HWC_EVENT_VSYNC:
             if(ioctl(m->framebuffer->fd, MSMFB_OVERLAY_VSYNC_CTRL, &enabled) < 0)
                 ret = -errno;
+
+            if(ctx->mExtDisplay->getExternalDisplay()) {
+                ret = ctx->mExtDisplay->enableHDMIVsync(enabled);
+            }
            break;
         default:
             ret = -EINVAL;
@@ -150,6 +155,8 @@
         CopyBit::draw(ctx, list, (EGLDisplay)dpy, (EGLSurface)sur);
         EGLBoolean sucess = eglSwapBuffers((EGLDisplay)dpy, (EGLSurface)sur);
         UIMirrorOverlay::draw(ctx);
+        if(ctx->mExtDisplay->getExternalDisplay())
+           ctx->mExtDisplay->commit();
     } else {
         ctx->mOverlay->setState(ovutils::OV_CLOSED);
         ctx->qbuf->unlockAllPrevious();
diff --git a/libhwcomposer/hwc_external.cpp b/libhwcomposer/hwc_external.cpp
index 3507134..b5e1e89 100644
--- a/libhwcomposer/hwc_external.cpp
+++ b/libhwcomposer/hwc_external.cpp
@@ -42,22 +42,21 @@
 #define DEVICE_ROOT "/sys/devices/virtual/graphics"
 #define DEVICE_NODE "fb1"
 
-#define SYSFS_CONNECTED         DEVICE_ROOT "/" DEVICE_NODE "/connected"
 #define SYSFS_EDID_MODES        DEVICE_ROOT "/" DEVICE_NODE "/edid_modes"
 #define SYSFS_HPD               DEVICE_ROOT "/" DEVICE_NODE "/hpd"
 
 
-ExternalDisplay::ExternalDisplay(hwc_context_t* ctx):fd(-1),
-    mCurrentID(-1), mHwcContext(ctx)
+ExternalDisplay::ExternalDisplay(hwc_context_t* ctx):mFd(-1),
+    mCurrentMode(-1), mHwcContext(ctx)
 {
+    memset(&mVInfo, 0, sizeof(mVInfo));
     //Enable HPD for HDMI
     writeHPDOption(1);
 }
 
 ExternalDisplay::~ExternalDisplay()
 {
-    if (fd > 0)
-        close(fd);
+    closeFrameBuffer();
 }
 
 struct disp_mode_timing_type {
@@ -143,7 +142,7 @@
     {m1920x1080p30_16_9, 1920, 1080,  88,  44, 148,  4, 5, 36,  74250, false},
 };
 
-int ExternalDisplay::parseResolution(char* edidStr, int* edidModes, int len)
+int ExternalDisplay::parseResolution(char* edidStr, int* edidModes)
 {
     char delim = ',';
     int count = 0;
@@ -152,27 +151,25 @@
     // Ex: 16,4,5,3,32,34,1
     // Parse this string to get mode(int)
     start = (char*) edidStr;
-    for(int i=0; i<len; i++) {
-        edidModes[i] = (int) strtol(start, &end, 10);
-        if(*end != delim) {
-            // return as we reached end of string
-            return count;
-        }
+    end = &delim;
+    while(*end == delim) {
+        edidModes[count] = (int) strtol(start, &end, 10);
         start = end+1;
         count++;
     }
+    ALOGD_IF(DEBUG, "In %s: count = %d", __FUNCTION__, count);
+    for (int i = 0; i < count; i++)
+        ALOGD_IF(DEBUG, "Mode[%d] = %d", i, edidModes[i]);
     return count;
 }
+
 bool ExternalDisplay::readResolution()
 {
     int hdmiEDIDFile = open(SYSFS_EDID_MODES, O_RDONLY, 0);
     int len = -1;
 
-    memset(mEDIDs, 0, sizeof(mEDIDs));
-    memset(mEDIDModes, 0, sizeof(mEDIDModes));
-    mModeCount = 0;
     if (hdmiEDIDFile < 0) {
-        ALOGD_IF(DEBUG, "%s: edid_modes file '%s' not found",
+        ALOGE("%s: edid_modes file '%s' not found",
                  __FUNCTION__, SYSFS_EDID_MODES);
         return false;
     } else {
@@ -180,7 +177,7 @@
         ALOGD_IF(DEBUG, "%s: EDID string: %s length = %d",
                  __FUNCTION__, mEDIDs, len);
         if ( len <= 0) {
-            ALOGD_IF(DEBUG, "%s: edid_modes file empty '%s'",
+            ALOGE("%s: edid_modes file empty '%s'",
                      __FUNCTION__, SYSFS_EDID_MODES);
         }
         else {
@@ -192,7 +189,7 @@
     close(hdmiEDIDFile);
     if(len > 0) {
         // GEt EDID modes from the EDID strings
-        mModeCount = parseResolution(mEDIDs, mEDIDModes, len);
+        mModeCount = parseResolution(mEDIDs, mEDIDModes);
         ALOGD_IF(DEBUG, "%s: mModeCount = %d", __FUNCTION__,
                  mModeCount);
     }
@@ -202,15 +199,33 @@
 
 bool ExternalDisplay::openFramebuffer()
 {
-    if (fd == -1) {
-        fd = open("/dev/graphics/fb1", O_RDWR);
-        if (fd < 0)
-            ALOGD_IF(DEBUG, "%s: /dev/graphics/fb1 not available"
-                     "\n", __FUNCTION__);
+    if (mFd == -1) {
+        mFd = open("/dev/graphics/fb1", O_RDWR);
+        if (mFd < 0)
+            ALOGE("%s: /dev/graphics/fb1 not available", __FUNCTION__);
     }
-    return (fd > 0);
+    return (mFd > 0);
 }
 
+bool ExternalDisplay::closeFrameBuffer()
+{
+    int ret = 0;
+    if(mFd > 0) {
+        ret = close(mFd);
+        mFd = -1;
+    }
+    return (ret == 0);
+}
+
+// clears the vinfo, edid, best modes
+void ExternalDisplay::resetInfo()
+{
+    memset(&mVInfo, 0, sizeof(mVInfo));
+    memset(mEDIDs, 0, sizeof(mEDIDs));
+    memset(mEDIDModes, 0, sizeof(mEDIDModes));
+    mModeCount = 0;
+    mCurrentMode = -1;
+}
 
 int ExternalDisplay::getModeOrder(int mode)
 {
@@ -278,10 +293,23 @@
 void ExternalDisplay::setResolution(int ID)
 {
     struct fb_var_screeninfo info;
+    int ret = 0;
     if (!openFramebuffer())
         return;
+    ret = ioctl(mFd, FBIOGET_VSCREENINFO, &mVInfo);
+    if(ret < 0) {
+        ALOGD("In %s: FBIOGET_VSCREENINFO failed Err Str = %s", __FUNCTION__,
+                                                            strerror(errno));
+    }
+
+    ALOGD_IF(DEBUG, "%s: GET Info<ID=%d %dx%d (%d,%d,%d),"
+            "(%d,%d,%d) %dMHz>", __FUNCTION__,
+            mVInfo.reserved[3], mVInfo.xres, mVInfo.yres,
+            mVInfo.right_margin, mVInfo.hsync_len, mVInfo.left_margin,
+            mVInfo.lower_margin, mVInfo.vsync_len, mVInfo.upper_margin,
+            mVInfo.pixclock/1000/1000);
     //If its a valid mode and its a new ID - update var_screeninfo
-    if ((isValidMode(ID)) && mCurrentID != ID) {
+    if ((isValidMode(ID)) && mCurrentMode != ID) {
         const struct disp_mode_timing_type *mode =
             &supported_video_mode_lut[0];
         unsigned count =  sizeof(supported_video_mode_lut)/sizeof
@@ -292,30 +320,38 @@
             if (cur->video_format == ID)
                 mode = cur;
         }
-        ioctl(fd, FBIOGET_VSCREENINFO, &info);
-        ALOGD_IF(DEBUG, "%s: GET Info<ID=%d %dx%d (%d,%d,%d),"
-                 "(%d,%d,%d) %dMHz>", __FUNCTION__,
-                 info.reserved[3], info.xres, info.yres,
-                 info.right_margin, info.hsync_len, info.left_margin,
-                 info.lower_margin, info.vsync_len, info.upper_margin,
-                 info.pixclock/1000/1000);
-        mode->set_info(info);
-        ALOGD_IF(DEBUG, "%s: SET Info<ID=%d => Info<ID=%d %dx%d"
+        mode->set_info(mVInfo);
+        ALOGD_IF(DEBUG, "%s: SET Info<ID=%d => Info<ID=%d %dx %d"
                  "(%d,%d,%d), (%d,%d,%d) %dMHz>", __FUNCTION__, ID,
-                 info.reserved[3], info.xres, info.yres,
-                 info.right_margin, info.hsync_len, info.left_margin,
-                 info.lower_margin, info.vsync_len, info.upper_margin,
-                 info.pixclock/1000/1000);
-        info.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_ALL | FB_ACTIVATE_FORCE;
-        ioctl(fd, FBIOPUT_VSCREENINFO, &info);
-        mCurrentID = ID;
+                 mVInfo.reserved[3], mVInfo.xres, mVInfo.yres,
+                 mVInfo.right_margin, mVInfo.hsync_len, mVInfo.left_margin,
+                 mVInfo.lower_margin, mVInfo.vsync_len, mVInfo.upper_margin,
+                 mVInfo.pixclock/1000/1000);
+        mVInfo.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_ALL | FB_ACTIVATE_FORCE;
+        ret = ioctl(mFd, FBIOPUT_VSCREENINFO, &mVInfo);
+        if(ret < 0) {
+            ALOGD("In %s: FBIOPUT_VSCREENINFO failed Err Str = %s",
+                                                 __FUNCTION__, strerror(errno));
+        }
+        mCurrentMode = ID;
     }
     //Powerup
-    ioctl(fd, FBIOBLANK, FB_BLANK_UNBLANK);
-    ioctl(fd, FBIOGET_VSCREENINFO, &info);
+    ret = ioctl(mFd, FBIOBLANK, FB_BLANK_UNBLANK);
+    if(ret < 0) {
+        ALOGD("In %s: FBIOBLANK failed Err Str = %s", __FUNCTION__,
+                                                            strerror(errno));
+    }
+    ret = ioctl(mFd, FBIOGET_VSCREENINFO, &mVInfo);
+    if(ret < 0) {
+        ALOGD("In %s: FBIOGET_VSCREENINFO failed Err Str = %s", __FUNCTION__,
+                                                            strerror(errno));
+    }
     //Pan_Display
-    ioctl(fd, FBIOPAN_DISPLAY, &info);
-    property_set("hw.hdmiON", "1");
+    ret = ioctl(mFd, FBIOPAN_DISPLAY, &mVInfo);
+    if(ret < 0) {
+        ALOGD("In %s: FBIOPAN_DISPLAY  failed Err Str = %s", __FUNCTION__,
+                                                            strerror(errno));
+    }
 }
 
 
@@ -324,7 +360,7 @@
     return mExternalDisplay;
 }
 
-void ExternalDisplay::setExternalDisplayStatus(int connected)
+void ExternalDisplay::setExternalDisplay(int connected)
 {
 
     hwc_context_t* ctx = mHwcContext;
@@ -336,15 +372,23 @@
             //Get the best mode and set
             // TODO: DO NOT call this for WFD
             setResolution(getBestMode());
+            //enable hdmi vsync
+            enableHDMIVsync(connected);
         } else {
-            close(fd);
+            // Disable the hdmi vsync
+            enableHDMIVsync(connected);
+            closeFrameBuffer();
+            resetInfo();
         }
         // Store the external display
         mExternalDisplay = connected;
+        const char* prop = (connected) ? "1" : "0";
+        // set system property
+        property_set("hw.hdmiON", prop);
         //Invalidate
         hwc_procs* proc = (hwc_procs*)ctx->device.reserved_proc[0];
         if(!proc) {
-            ALOGD_IF(DEBUG, "%s: HWC proc not registered",
+            ALOGE("%s: HWC proc not registered",
                      __FUNCTION__);
         } else {
             /* Trigger redraw */
@@ -360,8 +404,9 @@
     bool ret = true;
     int hdmiHPDFile = open(SYSFS_HPD,O_RDWR, 0);
     if (hdmiHPDFile < 0) {
-        ALOGD_IF(DEBUG, "%s: state file '%s' not found : ret%d"
-                 "err str: %s",  __FUNCTION__, SYSFS_HPD, hdmiHPDFile, strerror(errno));
+        ALOGE("%s: state file '%s' not found : ret%d"
+                           "err str: %s",  __FUNCTION__, SYSFS_HPD, hdmiHPDFile,
+                           strerror(errno));
         ret = false;
     } else {
         int err = -1;
@@ -372,7 +417,7 @@
         else
             err = write(hdmiHPDFile, "0" , 2);
         if (err <= 0) {
-            ALOGD_IF(DEBUG, "%s: file write failed '%s'",
+            ALOGE("%s: file write failed '%s'",
                      __FUNCTION__, SYSFS_HPD);
             ret = false;
         }
@@ -380,5 +425,30 @@
     }
     return ret;
 }
+
+bool ExternalDisplay::commit()
+{
+    if(mFd == -1) {
+        return false;
+    } else if(ioctl(mFd, FBIOPUT_VSCREENINFO, &mVInfo) == -1) {
+         ALOGE("%s: FBIOPUT_VSCREENINFO failed, str: %s", __FUNCTION__,
+                                                          strerror(errno));
+         return false;
+    }
+    return true;
+}
+
+int ExternalDisplay::enableHDMIVsync(int enable)
+{
+    if(mFd > 0) {
+        int ret = ioctl(mFd, MSMFB_OVERLAY_VSYNC_CTRL, &enable);
+        if (ret<0) {
+            ALOGE("%s: enabling HDMI vsync failed, str: %s", __FUNCTION__,
+                                                            strerror(errno));
+        }
+    }
+    return -errno;
+}
+
 };
 
diff --git a/libhwcomposer/hwc_external.h b/libhwcomposer/hwc_external.h
index 9e5f455..55773c0 100644
--- a/libhwcomposer/hwc_external.h
+++ b/libhwcomposer/hwc_external.h
@@ -21,6 +21,8 @@
 #ifndef HWC_EXTERNAL_DISPLAY_H
 #define HWC_EXTERNAL_DISPLAY_H
 
+#include <fb_priv.h>
+
 struct hwc_context_t;
 
 namespace qhwc {
@@ -43,26 +45,31 @@
     ExternalDisplay(hwc_context_t* ctx);
     ~ExternalDisplay();
     int getExternalDisplay() const;
-    void setExternalDisplayStatus(int connected);
+    void setExternalDisplay(int connected);
+    bool commit();
+    int enableHDMIVsync(int enable);
 
     private:
     bool readResolution();
-    int parseResolution(char* edidStr, int* edidModes, int len);
+    int parseResolution(char* edidStr, int* edidModes);
     void setResolution(int ID);
     bool openFramebuffer();
+    bool closeFrameBuffer();
     bool writeHPDOption(int userOption) const;
     bool isValidMode(int ID);
     void handleUEvent(char* str, int len);
     int getModeOrder(int mode);
     int getBestMode();
+    void resetInfo();
 
-    int fd;
+    int mFd;
     int mExternalDisplay;
-    int mCurrentID;
+    int mCurrentMode;
     char mEDIDs[128];
     int mEDIDModes[64];
     int mModeCount;
     hwc_context_t *mHwcContext;
+    fb_var_screeninfo mVInfo;
 };
 
 }; //qhwc
diff --git a/libhwcomposer/hwc_uevents.cpp b/libhwcomposer/hwc_uevents.cpp
index a1df74b..f58fbd3 100644
--- a/libhwcomposer/hwc_uevents.cpp
+++ b/libhwcomposer/hwc_uevents.cpp
@@ -30,21 +30,30 @@
 
 namespace qhwc {
 
-const char* MSMFB_DEVICE_CHANGE = "change@/devices/virtual/graphics/fb0";
+const char* MSMFB_DEVICE_FB0 = "change@/devices/virtual/graphics/fb0";
+const char* MSMFB_DEVICE_FB1 = "change@/devices/virtual/graphics/fb1";
 const char* MSMFB_HDMI_NODE = "fb1";
 
 static void handle_uevent(hwc_context_t* ctx, const char* udata, int len)
 {
-    int vsync;
+    int vsync = 0;
     char* hdmi;
     int64_t timestamp = 0;
     const char *str = udata;
-    hwc_procs* proc = reinterpret_cast<hwc_procs*>(ctx->device.reserved_proc[0]);
+    hwc_procs* proc = (hwc_procs*)ctx->device.reserved_proc[0];
+    int hdmiconnected = ctx->mExtDisplay->getExternalDisplay();
 
-    vsync = !strncmp(str, MSMFB_DEVICE_CHANGE, strlen(MSMFB_DEVICE_CHANGE));
-    hdmi = strcasestr(str, MSMFB_HDMI_NODE);
-    if(!vsync && !hdmi)
+    if(!strcasestr(str, "@/devices/virtual/graphics/fb")) {
+        ALOGD_IF(DEBUG, "%s: Not Ext Disp Event ", __FUNCTION__);
         return;
+    }
+
+    if(hdmiconnected)
+        vsync = !strncmp(str, MSMFB_DEVICE_FB1, strlen(MSMFB_DEVICE_FB1));
+    else
+        vsync = !strncmp(str, MSMFB_DEVICE_FB0, strlen(MSMFB_DEVICE_FB0));
+
+    hdmi = strcasestr(str, MSMFB_HDMI_NODE);
     if(vsync) {
         str += strlen(str) + 1;
         while(*str) {
@@ -56,6 +65,7 @@
             if(str - udata >= len)
                 break;
         }
+        return;
     }
 
     if(hdmi) {
@@ -72,7 +82,7 @@
         } else if(!(strncmp(str,"offline@",strlen("offline@")))) {
             connected = 0;
         }
-        ctx->mExtDisplay->setExternalDisplayStatus(connected);
+        ctx->mExtDisplay->setExternalDisplay(connected);
     }
 
 }
diff --git a/libhwcomposer/hwc_uimirror.cpp b/libhwcomposer/hwc_uimirror.cpp
index 9cf91af..3bbe30e 100644
--- a/libhwcomposer/hwc_uimirror.cpp
+++ b/libhwcomposer/hwc_uimirror.cpp
@@ -33,7 +33,7 @@
 int getDeviceOrientation(hwc_context_t* ctx,  hwc_layer_list_t *list) {
     int orientation =  list->hwLayers[0].transform;
     if(!ctx) {
-         ALOGD_IF(HWC_UI_MIRROR, "In %s: ctx is NULL!!", __FUNCTION__);
+         ALOGE("In %s: ctx is NULL!!", __FUNCTION__);
         return -1;
     }
     for(size_t i=0; i <= list->numHwLayers;i++ )
@@ -60,6 +60,12 @@
 bool UIMirrorOverlay::prepare(hwc_context_t *ctx, hwc_layer_list_t *list) {
     sState = ovutils::OV_CLOSED;
     sIsUiMirroringOn = false;
+
+    if(!ctx->hasOverlay) {
+       ALOGD_IF(HWC_UI_MIRROR, "%s, this hw doesnt support mirroring",
+                                                               __FUNCTION__);
+       return false;
+    }
     // If external display is connected
     if(ctx->mExtDisplay->getExternalDisplay()) {
         sState = ovutils::OV_UI_MIRROR;
@@ -95,7 +101,7 @@
                 dest = ovutils::OV_PIPE0;
             }
 
-            ovutils::eMdpFlags mdpFlags = ovutils::OV_MDP_MEMORY_ID_TYPE_FB;
+            ovutils::eMdpFlags mdpFlags = ovutils::OV_MDP_FLAGS_NONE;
             /* - TODO: Secure content
                if (hnd->flags & private_handle_t::PRIV_FLAGS_SECURE_BUFFER) {
                ovutils::setMdpFlags(mdpFlags,
@@ -160,20 +166,20 @@
                 if (!ov.queueBuffer(m->framebuffer->fd, m->currentOffset,
                                                            ovutils::OV_PIPE0)) {
                     ALOGE("%s: queueBuffer failed for external", __FUNCTION__);
+                    ret = false;
                 }
                 break;
             case ovutils::OV_2D_TRUE_UI_MIRROR:
                 if (!ov.queueBuffer(m->framebuffer->fd, m->currentOffset,
                                                            ovutils::OV_PIPE2)) {
                     ALOGE("%s: queueBuffer failed for external", __FUNCTION__);
+                    ret = false;
                 }
                 break;
 
         default:
             break;
         }
-        // TODO:
-        // Call PANDISPLAY ioctl here to kickoff
     }
     return ret;
 }