Display and wfd synchronization during teardown

* Provide a binder interface call for wfd module
to inform display about the start/stop/pause/resume
of wfd session.

* This is needed for wfd-hdmi synchronization in
case of v4l2 wfd solution. If hdmi is plugged
in during v4l2 wfd session, display-hal waits in
uevent thread for wfd teardown notification from
wfd module, before going ahead with configuring
external display.

* For VDS WFD solution, display-hal waits in uevent
thread for wfd-teardown to be signalled from
the composition thread.

Change-Id: I9514cb5bc7ff81de0b5dd4cdf66d8286a64ba094
diff --git a/libhwcomposer/hwc_qclient.cpp b/libhwcomposer/hwc_qclient.cpp
index 5313795..976b23d 100644
--- a/libhwcomposer/hwc_qclient.cpp
+++ b/libhwcomposer/hwc_qclient.cpp
@@ -157,6 +157,9 @@
 }
 
 static void pauseWFD(hwc_context_t *ctx, uint32_t pause) {
+    /* TODO: Will remove pauseWFD once all the clients start using
+     * setWfdStatus to indicate the status of WFD display
+     */
     int dpy = HWC_DISPLAY_VIRTUAL;
     if(pause) {
         //WFD Pause
@@ -167,6 +170,24 @@
     }
 }
 
+static void setWfdStatus(hwc_context_t *ctx, uint32_t wfdStatus) {
+
+    ALOGD_IF(HWC_WFDDISPSYNC_LOG,
+             "%s: Received a binder call that WFD state is %s",
+             __FUNCTION__,getExternalDisplayState(wfdStatus));
+    int dpy = HWC_DISPLAY_VIRTUAL;
+
+    if(wfdStatus == EXTERNAL_OFFLINE) {
+        ctx->mWfdSyncLock.lock();
+        ctx->mWfdSyncLock.signal();
+        ctx->mWfdSyncLock.unlock();
+    } else if(wfdStatus == EXTERNAL_PAUSE) {
+        handle_pause(ctx, dpy);
+    } else if(wfdStatus == EXTERNAL_RESUME) {
+        handle_resume(ctx, dpy);
+    }
+}
+
 status_t QClient::notifyCallback(uint32_t command, const Parcel* inParcel,
         Parcel* outParcel) {
     status_t ret = NO_ERROR;
@@ -199,15 +220,17 @@
             break;
         case IQService::SET_HSIC_DATA:
             setHSIC(inParcel);
+            break;
         case IQService::PAUSE_WFD:
             pauseWFD(mHwcContext, inParcel->readInt32());
             break;
+        case IQService::SET_WFD_STATUS:
+            setWfdStatus(mHwcContext,inParcel->readInt32());
+            break;
         default:
             ret = NO_ERROR;
     }
     return ret;
 }
 
-
-
 }
diff --git a/libhwcomposer/hwc_uevents.cpp b/libhwcomposer/hwc_uevents.cpp
index e1f7827..fed6f3c 100644
--- a/libhwcomposer/hwc_uevents.cpp
+++ b/libhwcomposer/hwc_uevents.cpp
@@ -39,14 +39,6 @@
 #define HWC_UEVENT_SWITCH_STR  "change@/devices/virtual/switch/"
 #define HWC_UEVENT_THREAD_NAME "hwcUeventThread"
 
-/* External Display states */
-enum {
-    EXTERNAL_OFFLINE = 0,
-    EXTERNAL_ONLINE,
-    EXTERNAL_PAUSE,
-    EXTERNAL_RESUME
-};
-
 static void setup(hwc_context_t* ctx, int dpy)
 {
     ctx->mFBUpdate[dpy] = IFBUpdate::getObject(ctx, dpy);
@@ -145,9 +137,24 @@
             ctx->mVirtualonExtActive = false;
         }
     }
-    /* Wait for few frames for SF to tear down the WFD session. */
-    usleep(ctx->dpyAttr[HWC_DISPLAY_PRIMARY].vsync_period
-            * 2 / 1000);
+
+    if(ctx->mVDSEnabled) {
+        ctx->mWfdSyncLock.lock();
+        ALOGD_IF(HWC_WFDDISPSYNC_LOG,
+                 "%s: Waiting for wfd-teardown to be signalled",__FUNCTION__);
+        ctx->mWfdSyncLock.wait();
+        ALOGD_IF(HWC_WFDDISPSYNC_LOG,
+                 "%s: Teardown signalled. Completed waiting in uevent thread",
+                 __FUNCTION__);
+        ctx->mWfdSyncLock.unlock();
+    } else {
+        /*TODO: Remove this else block and have wait rather than usleep
+          once wfd module issues binder call on teardown.*/
+
+        /* For now, Wait for few frames for SF to tear down the WFD session.*/
+        usleep(ctx->dpyAttr[HWC_DISPLAY_PRIMARY].vsync_period
+               * 2 / 1000);
+    }
 }
 
 static void handle_uevent(hwc_context_t* ctx, const char* udata, int len)
@@ -166,7 +173,7 @@
 
     int switch_state = getConnectedState(udata, len);
 
-    ALOGE_IF(UEVENT_DEBUG,"%s: uevent recieved: %s switch state: %d",
+    ALOGE_IF(UEVENT_DEBUG,"%s: uevent received: %s switch state: %d",
              __FUNCTION__,udata, switch_state);
 
     switch(switch_state) {
diff --git a/libhwcomposer/hwc_utils.cpp b/libhwcomposer/hwc_utils.cpp
index 3f5d77a..54445aa 100644
--- a/libhwcomposer/hwc_utils.cpp
+++ b/libhwcomposer/hwc_utils.cpp
@@ -182,6 +182,7 @@
 void initContext(hwc_context_t *ctx)
 {
     openFramebufferDevice(ctx);
+    char value[PROPERTY_VALUE_MAX];
     ctx->mMDP.version = qdutils::MDPVersion::getInstance().getMDPVersion();
     ctx->mMDP.hasOverlay = qdutils::MDPVersion::getInstance().hasOverlay();
     ctx->mMDP.panel = qdutils::MDPVersion::getInstance().getPanelType();
@@ -223,7 +224,14 @@
     ctx->mMDPComp[HWC_DISPLAY_PRIMARY] =
          MDPComp::getObject(ctx, HWC_DISPLAY_PRIMARY);
     ctx->dpyAttr[HWC_DISPLAY_PRIMARY].connected = true;
-    ctx->mHWCVirtual = HWCVirtualBase::getObject();
+
+    ctx->mVDSEnabled = false;
+    if((property_get("persist.hwc.enable_vds", value, NULL) > 0)) {
+        if(atoi(value) != 0) {
+            ctx->mVDSEnabled = true;
+        }
+    }
+    ctx->mHWCVirtual = HWCVirtualBase::getObject(ctx->mVDSEnabled);
 
     for (uint32_t i = 0; i < HWC_NUM_DISPLAY_TYPES; i++) {
         ctx->mHwcDebug[i] = new HwcDebug(i);
@@ -260,7 +268,6 @@
 
     // Read the system property to determine if downscale feature is enabled.
     ctx->mMDPDownscaleEnabled = false;
-    char value[PROPERTY_VALUE_MAX];
     if(property_get("sys.hwc.mdp_downscale_enabled", value, "false")
             && !strcmp(value, "true")) {
         ctx->mMDPDownscaleEnabled = true;
@@ -639,6 +646,21 @@
     return extOrientation;
 }
 
+/* Get External State names */
+const char* getExternalDisplayState(uint32_t external_state) {
+    static const char* externalStates[EXTERNAL_MAXSTATES] = {0};
+    externalStates[EXTERNAL_OFFLINE] = STR(EXTERNAL_OFFLINE);
+    externalStates[EXTERNAL_ONLINE]  = STR(EXTERNAL_ONLINE);
+    externalStates[EXTERNAL_PAUSE]   = STR(EXTERNAL_PAUSE);
+    externalStates[EXTERNAL_RESUME]  = STR(EXTERNAL_RESUME);
+
+    if(external_state >= EXTERNAL_MAXSTATES) {
+        return "EXTERNAL_INVALID";
+    }
+
+    return externalStates[external_state];
+}
+
 bool isDownscaleRequired(hwc_layer_1_t const* layer) {
     hwc_rect_t displayFrame  = layer->displayFrame;
     hwc_rect_t sourceCrop = integerizeSourceCrop(layer->sourceCropf);
diff --git a/libhwcomposer/hwc_utils.h b/libhwcomposer/hwc_utils.h
index ed68988..726311b 100644
--- a/libhwcomposer/hwc_utils.h
+++ b/libhwcomposer/hwc_utils.h
@@ -39,6 +39,8 @@
 #define MAX_NUM_APP_LAYERS 32
 #define MIN_DISPLAY_XRES 200
 #define MIN_DISPLAY_YRES 200
+#define HWC_WFDDISPSYNC_LOG 0
+#define STR(f) #f;
 
 //Fwrd decls
 struct hwc_context_t;
@@ -154,6 +156,15 @@
     HWC_FORMAT_RB_SWAP = 0x00000040,
 };
 
+/* External Display states */
+enum {
+    EXTERNAL_OFFLINE = 0,
+    EXTERNAL_ONLINE,
+    EXTERNAL_PAUSE,
+    EXTERNAL_RESUME,
+    EXTERNAL_MAXSTATES
+};
+
 class LayerRotMap {
 public:
     LayerRotMap() { reset(); }
@@ -280,6 +291,9 @@
 // BufferMirrirMode(Sidesync)
 int getMirrorModeOrientation(hwc_context_t *ctx);
 
+/* Get External State names */
+const char* getExternalDisplayState(uint32_t external_state);
+
 // Handles wfd Pause and resume events
 void handle_pause(hwc_context_t *ctx, int dpy);
 void handle_resume(hwc_context_t *ctx, int dpy);
@@ -516,14 +530,21 @@
     //Used for SideSync feature
     //which overrides the mExtOrientation
     bool mBufferMirrorMode;
+    // Used to synchronize between WFD and Display modules
+    mutable Locker mWfdSyncLock;
+
     qhwc::LayerRotMap *mLayerRotMap[HWC_NUM_DISPLAY_TYPES];
     // Panel reset flag will be set if BTA check fails
     bool mPanelResetStatus;
     // number of active Displays
     int numActiveDisplays;
-    // Downscale feature switch, set via system the property
+    // Downscale feature switch, set via system property
     // sys.hwc.mdp_downscale_enabled
     bool mMDPDownscaleEnabled;
+    // Is WFD enabled through VDS solution ?
+    // This can be set via system property
+    // persist.hwc.enable_vds
+    bool mVDSEnabled;
     struct gpu_hint_info mGPUHintInfo;
 };
 
diff --git a/libhwcomposer/hwc_virtual.cpp b/libhwcomposer/hwc_virtual.cpp
index f68d08f..10ed9d1 100644
--- a/libhwcomposer/hwc_virtual.cpp
+++ b/libhwcomposer/hwc_virtual.cpp
@@ -35,19 +35,17 @@
 using namespace qhwc;
 using namespace overlay;
 
-HWCVirtualBase* HWCVirtualBase::getObject() {
-    char property[PROPERTY_VALUE_MAX];
+HWCVirtualBase* HWCVirtualBase::getObject(bool isVDSEnabled) {
 
-    if((property_get("persist.hwc.enable_vds", property, NULL) > 0)) {
-        if(atoi(property) != 0) {
-            ALOGD_IF(HWCVIRTUAL_LOG, "%s: VDS is enabled for Virtual display",
-                __FUNCTION__);
-            return new HWCVirtualVDS();
-        }
+    if(isVDSEnabled) {
+        ALOGD_IF(HWCVIRTUAL_LOG, "%s: VDS is enabled for Virtual display",
+                 __FUNCTION__);
+        return new HWCVirtualVDS();
+    } else {
+        ALOGD_IF(HWCVIRTUAL_LOG, "%s: V4L2 is enabled for Virtual display",
+                 __FUNCTION__);
+        return new HWCVirtualV4L2();
     }
-    ALOGD_IF(HWCVIRTUAL_LOG, "%s: V4L2 is enabled for Virtual display",
-            __FUNCTION__);
-    return new HWCVirtualV4L2();
 }
 
 void HWCVirtualVDS::init(hwc_context_t *ctx) {
@@ -84,6 +82,9 @@
         if(!Writeback::getInstance()->setSecure(false)) {
             ALOGE("Failure while attempting to reset WB session.");
         }
+        ctx->mWfdSyncLock.lock();
+        ctx->mWfdSyncLock.signal();
+        ctx->mWfdSyncLock.unlock();
     }
 }
 
diff --git a/libhwcomposer/hwc_virtual.h b/libhwcomposer/hwc_virtual.h
index 6bc3898..87004c3 100644
--- a/libhwcomposer/hwc_virtual.h
+++ b/libhwcomposer/hwc_virtual.h
@@ -33,7 +33,7 @@
     explicit HWCVirtualBase(){};
     virtual ~HWCVirtualBase(){};
     // instantiates and returns the pointer to VDS or V4L2 object.
-    static HWCVirtualBase* getObject();
+    static HWCVirtualBase* getObject(bool isVDSEnabled);
     virtual int prepare(hwc_composer_device_1 *dev,
                           hwc_display_contents_1_t* list) = 0;
     virtual int set(hwc_context_t *ctx, hwc_display_contents_1_t *list) = 0;