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