hwc: support WFD external display

Change-Id: I3d24ff1757e79c4b41ff6c6db17d0274cd665671
(cherry picked from commit f8942672226a7af26218aa8d325c1677ae2b83dd)
diff --git a/libexternal/external.cpp b/libexternal/external.cpp
index 28fbd58..f9ee94c 100644
--- a/libexternal/external.cpp
+++ b/libexternal/external.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright (C) 2010 The Android Open Source Project
  * Copyright (C) 2012, The Linux Foundation. All rights reserved.
@@ -37,35 +36,142 @@
 #include "hwc_utils.h"
 #include "external.h"
 #include "overlayUtils.h"
+#include "overlay.h"
 
 using namespace android;
 
 namespace qhwc {
 
+#define MAX_FRAME_BUFFER_NAME_SIZE      (80)
+#define MAX_DISPLAY_DEVICES             (3)
 
-#define DEVICE_ROOT "/sys/devices/virtual/graphics"
-#define DEVICE_NODE "fb1"
 
-#define SYSFS_EDID_MODES        DEVICE_ROOT "/" DEVICE_NODE "/edid_modes"
-#define SYSFS_HPD               DEVICE_ROOT "/" DEVICE_NODE "/hpd"
+const char* msmFbDevicePath[] = {  "/dev/graphics/fb1",
+                                   "/dev/graphics/fb2"};
 
+/*
+ * Updates extDeviceFbIndex Array with the correct frame buffer indices
+ * of avaiable external devices
+ *
+ */
+void ExternalDisplay::updateExtDispDevFbIndex()
+{
+    FILE *displayDeviceFP = NULL;
+    char fbType[MAX_FRAME_BUFFER_NAME_SIZE];
+    char msmFbTypePath[MAX_FRAME_BUFFER_NAME_SIZE];
+
+    for(int j = 1; j < MAX_DISPLAY_DEVICES; j++) {
+        sprintf (msmFbTypePath,"/sys/class/graphics/fb%d/msm_fb_type", j);
+        displayDeviceFP = fopen(msmFbTypePath, "r");
+        if(displayDeviceFP){
+            fread(fbType, sizeof(char), MAX_FRAME_BUFFER_NAME_SIZE,
+                    displayDeviceFP);
+            if(strncmp(fbType, "dtv panel", strlen("dtv panel")) == 0){
+                ALOGD_IF(DEBUG,"hdmi framebuffer index is %d",j);
+                mHdmiFbNum = j;
+            } else if(strncmp(fbType, "writeback panel",
+                                    strlen("writeback panel")) == 0){
+                ALOGD_IF(DEBUG,"wfd framebuffer index is %d",j);
+                mWfdFbNum = j;
+            }
+            fclose(displayDeviceFP);
+        }
+    }
+    ALOGD_IF(DEBUG,"%s: mHdmiFbNum: %d mWfdFbNum: %d ",__FUNCTION__,
+                                                       mHdmiFbNum, mWfdFbNum);
+}
+
+int ExternalDisplay::configureHDMIDisplay() {
+    openFrameBuffer(mHdmiFbNum);
+    if(mFd == -1)
+        return -1;
+    readResolution();
+    //Get the best mode and set
+    // TODO: Move this to activate
+    setResolution(getBestMode());
+    setDpyHdmiAttr();
+    setExternalDisplay(true, mHdmiFbNum);
+    return 0;
+}
+
+int ExternalDisplay::configureWFDDisplay() {
+    int ret = 0;
+    if(mConnectedFbNum == mHdmiFbNum) {
+        ALOGE("%s: Cannot process WFD connection while HDMI is active",
+                     __FUNCTION__);
+        return -1;
+    }
+    openFrameBuffer(mWfdFbNum);
+    if(mFd == -1)
+        return -1;
+    ret = ioctl(mFd, FBIOGET_VSCREENINFO, &mVInfo);
+    if(ret < 0) {
+        ALOGD("In %s: FBIOGET_VSCREENINFO failed Err Str = %s", __FUNCTION__,
+                strerror(errno));
+    }
+    setDpyWfdAttr();
+    setExternalDisplay(true, mWfdFbNum);
+    return 0;
+}
+
+int ExternalDisplay::teardownHDMIDisplay() {
+    if(mConnectedFbNum == mHdmiFbNum) {
+        // hdmi offline event..!
+        closeFrameBuffer();
+        resetInfo();
+        setExternalDisplay(false);
+    }
+    return 0;
+}
+
+int ExternalDisplay::teardownWFDDisplay() {
+    if(mConnectedFbNum == mWfdFbNum) {
+        // wfd offline event..!
+        closeFrameBuffer();
+        memset(&mVInfo, 0, sizeof(mVInfo));
+        setExternalDisplay(false);
+    }
+    return 0;
+}
+
+void ExternalDisplay::processUEventOnline(const char *str) {
+    const char *s1 = str + strlen("change@/devices/virtual/switch/");
+    if(!strncmp(s1,"hdmi",strlen(s1))) {
+        // hdmi online event..!
+        configureHDMIDisplay();
+    }else if(!strncmp(s1,"wfd",strlen(s1))) {
+        // wfd online event..!
+        configureWFDDisplay();
+    }
+}
+
+void ExternalDisplay::processUEventOffline(const char *str) {
+    const char *s1 = str + strlen("change@/devices/virtual/switch/");
+    if(!strncmp(s1,"hdmi",strlen(s1))) {
+        teardownHDMIDisplay();
+    }else if(!strncmp(s1,"wfd",strlen(s1))) {
+        teardownWFDDisplay();
+    }
+}
 
 ExternalDisplay::ExternalDisplay(hwc_context_t* ctx):mFd(-1),
-    mCurrentMode(-1), mExternalDisplay(0), mModeCount(0), mHwcContext(ctx)
+    mCurrentMode(-1), mConnected(0), mConnectedFbNum(0), mModeCount(0),
+    mHwcContext(ctx), mHdmiFbNum(-1), mWfdFbNum(-1)
 {
     memset(&mVInfo, 0, sizeof(mVInfo));
+    //Determine the fb index for external display devices.
+    updateExtDispDevFbIndex();
 }
 
 void ExternalDisplay::setEDIDMode(int resMode) {
     ALOGD_IF(DEBUG,"resMode=%d ", resMode);
-    int extDispType;
     {
         Mutex::Autolock lock(mExtDispLock);
-        extDispType = mExternalDisplay;
-        setExternalDisplay(0);
+        setExternalDisplay(false);
+        openFrameBuffer(mHdmiFbNum);
         setResolution(resMode);
     }
-    setExternalDisplay(extDispType);
+    setExternalDisplay(true, mHdmiFbNum);
 }
 
 void ExternalDisplay::setHPD(uint32_t startEnd) {
@@ -77,7 +183,7 @@
     ALOGD_IF(DEBUG,"ActionSafe w=%d h=%d", w, h);
     Mutex::Autolock lock(mExtDispLock);
     overlay::utils::ActionSafe::getInstance()->setDimension(w, h);
-    setExternalDisplay(mExternalDisplay);
+    setExternalDisplay(true, mHdmiFbNum);
 }
 
 int ExternalDisplay::getModeCount() const {
@@ -204,12 +310,16 @@
 
 bool ExternalDisplay::readResolution()
 {
-    int hdmiEDIDFile = open(SYSFS_EDID_MODES, O_RDONLY, 0);
+    char sysFsEDIDFilePath[255];
+    sprintf(sysFsEDIDFilePath , "/sys/devices/virtual/graphics/fb%d/edid_modes",
+            mHdmiFbNum);
+
+    int hdmiEDIDFile = open(sysFsEDIDFilePath, O_RDONLY, 0);
     int len = -1;
 
     if (hdmiEDIDFile < 0) {
         ALOGE("%s: edid_modes file '%s' not found",
-                 __FUNCTION__, SYSFS_EDID_MODES);
+                 __FUNCTION__, sysFsEDIDFilePath);
         return false;
     } else {
         len = read(hdmiEDIDFile, mEDIDs, sizeof(mEDIDs)-1);
@@ -217,7 +327,7 @@
                  __FUNCTION__, mEDIDs, len);
         if ( len <= 0) {
             ALOGE("%s: edid_modes file empty '%s'",
-                     __FUNCTION__, SYSFS_EDID_MODES);
+                     __FUNCTION__, sysFsEDIDFilePath);
         }
         else {
             while (len > 1 && isspace(mEDIDs[len-1]))
@@ -227,7 +337,7 @@
     }
     close(hdmiEDIDFile);
     if(len > 0) {
-        // GEt EDID modes from the EDID strings
+        // Get EDID modes from the EDID strings
         mModeCount = parseResolution(mEDIDs, mEDIDModes);
         ALOGD_IF(DEBUG, "%s: mModeCount = %d", __FUNCTION__,
                  mModeCount);
@@ -236,15 +346,16 @@
     return (strlen(mEDIDs) > 0);
 }
 
-bool ExternalDisplay::openFramebuffer()
+bool ExternalDisplay::openFrameBuffer(int fbNum)
 {
     if (mFd == -1) {
-        mFd = open("/dev/graphics/fb1", O_RDWR);
+        mFd = open(msmFbDevicePath[fbNum-1], O_RDWR);
         if (mFd < 0)
-            ALOGE("%s: /dev/graphics/fb1 not available", __FUNCTION__);
-    }
-    if(mHwcContext) {
-        mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].fd = mFd;
+            ALOGE("%s: %s is not available", __FUNCTION__,
+                                            msmFbDevicePath[fbNum-1]);
+        if(mHwcContext) {
+            mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].fd = mFd;
+        }
     }
     return (mFd > 0);
 }
@@ -339,8 +450,6 @@
 {
     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__,
@@ -382,54 +491,49 @@
     }
 }
 
-void ExternalDisplay::setExternalDisplay(int connected)
+void ExternalDisplay::setExternalDisplay(bool connected, int extFbNum)
 {
-
     hwc_context_t* ctx = mHwcContext;
     if(ctx) {
-        ALOGD_IF(DEBUG, "%s: status = %d", __FUNCTION__,
-                 connected);
-        if(connected) {
-            readResolution();
-            //Get the best mode and set
-            // TODO: Move this to activate
-            setResolution(getBestMode());
-            setDpyAttr();
-            //enable hdmi vsync
-        } else {
-            // Disable the hdmi vsync
-            closeFrameBuffer();
-            resetInfo();
-        }
+        ALOGD_IF(DEBUG, "%s: connected = %d", __FUNCTION__, connected);
         // Store the external display
-        mExternalDisplay = connected;
-        const char* prop = (connected) ? "1" : "0";
-        // set system property
-        property_set("hw.hdmiON", prop);
+        mConnected = connected;
+        mConnectedFbNum = extFbNum;
+        mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].connected = connected;
+        // Update external fb number in Overlay context
+        overlay::Overlay::getInstance()->setExtFbNum(extFbNum);
     }
-    return;
+}
+
+int ExternalDisplay::getExtFbNum(int &fbNum) {
+    int ret = -1;
+    if(mConnected) {
+        fbNum = mConnectedFbNum;
+        ret = 0;
+    }
+    return ret;
 }
 
 bool ExternalDisplay::writeHPDOption(int userOption) const
 {
     bool ret = true;
-    int hdmiHPDFile = open(SYSFS_HPD,O_RDWR, 0);
+    char sysFsHPDFilePath[255];
+    sprintf(sysFsHPDFilePath ,"/sys/devices/virtual/graphics/fb%d/hpd",
+                                mHdmiFbNum);
+    int hdmiHPDFile = open(sysFsHPDFilePath,O_RDWR, 0);
     if (hdmiHPDFile < 0) {
-        ALOGE("%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__,
+                                sysFsHPDFilePath, hdmiHPDFile, strerror(errno));
         ret = false;
     } else {
         int err = -1;
-        ALOGD_IF(DEBUG, "%s: option = %d", __FUNCTION__,
-                 userOption);
+        ALOGD_IF(DEBUG, "%s: option = %d", __FUNCTION__, userOption);
         if(userOption)
             err = write(hdmiHPDFile, "1", 2);
         else
             err = write(hdmiHPDFile, "0" , 2);
         if (err <= 0) {
-            ALOGE("%s: file write failed '%s'",
-                     __FUNCTION__, SYSFS_HPD);
+            ALOGE("%s: file write failed '%s'", __FUNCTION__, sysFsHPDFilePath);
             ret = false;
         }
         close(hdmiHPDFile);
@@ -439,25 +543,32 @@
 
 /*
  * commits the changes to the external display
- * mExternalDisplay has the mixer number(1-> HDMI 2-> WFD)
  */
 bool ExternalDisplay::post()
 {
     if(mFd == -1)
         return false;
-
     struct mdp_display_commit ext_commit;
     ext_commit.flags |= MDP_DISPLAY_COMMIT_OVERLAY;
     if (ioctl(mFd, MSMFB_DISPLAY_COMMIT, &ext_commit) == -1) {
-         ALOGE("%s: MSMFB_DISPLAY_COMMIT for external failed, str: %s",
-                                                __FUNCTION__, strerror(errno));
-         return false;
+        ALOGE("%s: MSMFB_DISPLAY_COMMIT for external failed, str: %s",
+                __FUNCTION__, strerror(errno));
+        return false;
     }
-
     return true;
 }
 
-void ExternalDisplay::setDpyAttr() {
+void ExternalDisplay::setDpyWfdAttr() {
+    if(mHwcContext) {
+        mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].xres = mVInfo.xres;
+        mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].yres = mVInfo.yres;
+        mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].vsync_period =
+                1000000000l /60;
+        ALOGD_IF(DEBUG,"%s: wfd...connected..!",__FUNCTION__);
+    }
+}
+
+void ExternalDisplay::setDpyHdmiAttr() {
     int width = 0, height = 0, fps = 0;
     getAttrForMode(width, height, fps);
     if(mHwcContext) {
@@ -469,8 +580,7 @@
     }
 }
 
-void ExternalDisplay::getAttrForMode(int& width, int& height,
-int& fps) {
+void ExternalDisplay::getAttrForMode(int& width, int& height, int& fps) {
     switch (mCurrentMode) {
         case m640x480p60_4_3:
             width = 640;
@@ -528,4 +638,3 @@
 }
 
 };
-