hwc: Extend partial frame update for split displays

Extends partial frame update support for split displays by calculating
changing region(ROI) independently for each half of the screen. In case of
only one half updating, display driver shuts down the DSI/MDP mixer control
of the non updating half.

Maintains two ROI's for each display. In case of Non-split
displays, only left ROI is updated.

Change-Id: I8a67fe40aac665a48b6f8a46beffb9f8027851b2
diff --git a/libhwcomposer/hwc.cpp b/libhwcomposer/hwc.cpp
index 0396c91..237cd39 100644
--- a/libhwcomposer/hwc.cpp
+++ b/libhwcomposer/hwc.cpp
@@ -604,8 +604,20 @@
             }
         }
 
-        if(!Overlay::displayCommit(ctx->dpyAttr[dpy].fd,
-                                            ctx->listStats[dpy].roi)) {
+        int lSplit = getLeftSplit(ctx, dpy);
+        qhwc::ovutils::Dim lRoi = qhwc::ovutils::Dim {
+            ctx->listStats[dpy].lRoi.left,
+            ctx->listStats[dpy].lRoi.top,
+            ctx->listStats[dpy].lRoi.right - ctx->listStats[dpy].lRoi.left,
+            ctx->listStats[dpy].lRoi.bottom - ctx->listStats[dpy].lRoi.top };
+
+        qhwc::ovutils::Dim rRoi = qhwc::ovutils::Dim {
+            ctx->listStats[dpy].rRoi.left - lSplit,
+            ctx->listStats[dpy].rRoi.top,
+            ctx->listStats[dpy].rRoi.right - ctx->listStats[dpy].rRoi.left,
+            ctx->listStats[dpy].rRoi.bottom - ctx->listStats[dpy].rRoi.top };
+
+        if(!Overlay::displayCommit(ctx->dpyAttr[dpy].fd, lRoi, rRoi)) {
             ALOGE("%s: display commit fail for %d dpy!", __FUNCTION__, dpy);
             ret = -1;
         }
diff --git a/libhwcomposer/hwc_mdpcomp.cpp b/libhwcomposer/hwc_mdpcomp.cpp
index 8a717c0..fab9a8b 100644
--- a/libhwcomposer/hwc_mdpcomp.cpp
+++ b/libhwcomposer/hwc_mdpcomp.cpp
@@ -42,7 +42,6 @@
 bool MDPComp::sDebugLogs = false;
 bool MDPComp::sEnabled = false;
 bool MDPComp::sEnableMixedMode = true;
-bool MDPComp::sEnablePartialFrameUpdate = false;
 int MDPComp::sMaxPipesPerMixer = MAX_PIPES_PER_MIXER;
 bool MDPComp::sEnable4k2kYUVSplit = false;
 bool MDPComp::sSrcSplitEnabled = false;
@@ -116,18 +115,6 @@
             sDebugLogs = true;
     }
 
-    // We read from drivers if panel supports partial updating
-    // and we enable partial update computations if supported.
-    // Keeping this property to disable partial update for
-    // debugging by setting below property to 0 & only 0.
-    property_get("persist.hwc.partialupdate", property, "-1");
-    if((atoi(property) != 0) &&
-        qdutils::MDPVersion::getInstance().isPartialUpdateEnabled()) {
-            sEnablePartialFrameUpdate = true;
-    }
-    ALOGE_IF(isDebug(), "%s: Partial Update applicable?: %d",__FUNCTION__,
-                                                    sEnablePartialFrameUpdate);
-
     sMaxPipesPerMixer = MAX_PIPES_PER_MIXER;
     if(property_get("debug.mdpcomp.maxpermixer", property, "-1") > 0) {
         int val = atoi(property);
@@ -421,23 +408,21 @@
     return ret;
 }
 
-/*
- * 1) Identify layers that are not visible in the updating ROI and drop them
- * from composition.
- * 2) If we have a scaling layers which needs cropping against generated ROI.
- * Reset ROI to full resolution.
- */
-bool MDPComp::validateAndApplyROI(hwc_context_t *ctx,
-                               hwc_display_contents_1_t* list, hwc_rect_t roi) {
+void MDPCompNonSplit::trimAgainstROI(hwc_context_t *ctx, hwc_rect_t& fbRect) {
+    hwc_rect_t roi = ctx->listStats[mDpy].lRoi;
+    fbRect = getIntersection(fbRect, roi);
+}
+
+/* 1) Identify layers that are not visible or lying outside the updating ROI and
+ *    drop them from composition.
+ * 2) If we have a scaling layer which needs cropping against generated
+ *    ROI, reset ROI to full resolution. */
+bool MDPCompNonSplit::validateAndApplyROI(hwc_context_t *ctx,
+        hwc_display_contents_1_t* list) {
     int numAppLayers = ctx->listStats[mDpy].numAppLayers;
-
-    if(!isValidRect(roi))
-        return false;
-
-    hwc_rect_t visibleRect = roi;
+    hwc_rect_t visibleRect = ctx->listStats[mDpy].lRoi;
 
     for(int i = numAppLayers - 1; i >= 0; i--){
-
         if(!isValidRect(visibleRect)) {
             mCurrentFrame.drop[i] = true;
             mCurrentFrame.dropCount++;
@@ -445,24 +430,16 @@
         }
 
         const hwc_layer_1_t* layer =  &list->hwLayers[i];
-
         hwc_rect_t dstRect = layer->displayFrame;
-        hwc_rect_t srcRect = integerizeSourceCrop(layer->sourceCropf);
-
         hwc_rect_t res  = getIntersection(visibleRect, dstRect);
 
-        int res_w = res.right - res.left;
-        int res_h = res.bottom - res.top;
-        int dst_w = dstRect.right - dstRect.left;
-        int dst_h = dstRect.bottom - dstRect.top;
-
         if(!isValidRect(res)) {
             mCurrentFrame.drop[i] = true;
             mCurrentFrame.dropCount++;
-        }else {
+        } else {
             /* Reset frame ROI when any layer which needs scaling also needs ROI
              * cropping */
-            if((res_w != dst_w || res_h != dst_h) && needsScaling (layer)) {
+            if(!isSameRect(res, dstRect) && needsScaling (layer)) {
                 ALOGI("%s: Resetting ROI due to scaling", __FUNCTION__);
                 memset(&mCurrentFrame.drop, 0, sizeof(mCurrentFrame.drop));
                 mCurrentFrame.dropCount = 0;
@@ -477,52 +454,167 @@
     return true;
 }
 
-bool MDPComp::canDoPartialUpdate(hwc_context_t *ctx,
-                               hwc_display_contents_1_t* list){
-    if(!qdutils::MDPVersion::getInstance().isPartialUpdateEnabled() || mDpy ||
-       isSkipPresent(ctx, mDpy) || (list->flags & HWC_GEOMETRY_CHANGED)||
-       isDisplaySplit(ctx, mDpy)) {
-        return false;
-    }
-    return true;
-}
-
-void MDPComp::generateROI(hwc_context_t *ctx, hwc_display_contents_1_t* list) {
+/* Calculate ROI for the frame by accounting all the layer's dispalyFrame which
+ * are updating. If DirtyRegion is applicable, calculate it by accounting all
+ * the changing layer's dirtyRegion. */
+void MDPCompNonSplit::generateROI(hwc_context_t *ctx,
+        hwc_display_contents_1_t* list) {
     int numAppLayers = ctx->listStats[mDpy].numAppLayers;
-
-    if(!canDoPartialUpdate(ctx, list))
+    if(!canPartialUpdate(ctx, list))
         return;
 
     struct hwc_rect roi = (struct hwc_rect){0, 0, 0, 0};
+    hwc_rect fullFrame = (struct hwc_rect) {0, 0,(int)ctx->dpyAttr[mDpy].xres,
+        (int)ctx->dpyAttr[mDpy].yres};
+
     for(int index = 0; index < numAppLayers; index++ ) {
         hwc_layer_1_t* layer = &list->hwLayers[index];
         if ((mCachedFrame.hnd[index] != layer->handle) ||
-            isYuvBuffer((private_handle_t *)layer->handle)) {
+                isYuvBuffer((private_handle_t *)layer->handle)) {
             hwc_rect_t updatingRect = layer->displayFrame;
+
 #ifdef QCOM_BSP
-            if(!needsScaling(layer))
+            if(!needsScaling(layer) && !layer->transform)
                 updatingRect =  layer->dirtyRect;
 #endif
+
             roi = getUnion(roi, updatingRect);
         }
     }
 
-    hwc_rect fullFrame = (struct hwc_rect) {0, 0,(int)ctx->dpyAttr[mDpy].xres,
-        (int)ctx->dpyAttr[mDpy].yres};
+    /* No layer is updating. Still SF wants a refresh.*/
+    if(!isValidRect(roi))
+        return;
 
     // Align ROI coordinates to panel restrictions
-    roi = sanitizeROI(roi, fullFrame);
+    roi = getSanitizeROI(roi, fullFrame);
 
-    if(!validateAndApplyROI(ctx, list, roi))
-        roi = fullFrame;
-
-    ctx->listStats[mDpy].roi.x = roi.left;
-    ctx->listStats[mDpy].roi.y = roi.top;
-    ctx->listStats[mDpy].roi.w = roi.right - roi.left;
-    ctx->listStats[mDpy].roi.h = roi.bottom - roi.top;
+    ctx->listStats[mDpy].lRoi = roi;
+    if(!validateAndApplyROI(ctx, list))
+        resetROI(ctx, mDpy);
 
     ALOGD_IF(isDebug(),"%s: generated ROI: [%d, %d, %d, %d]", __FUNCTION__,
-                               roi.left, roi.top, roi.right, roi.bottom);
+            ctx->listStats[mDpy].lRoi.left, ctx->listStats[mDpy].lRoi.top,
+            ctx->listStats[mDpy].lRoi.right, ctx->listStats[mDpy].lRoi.bottom);
+}
+
+void MDPCompSplit::trimAgainstROI(hwc_context_t *ctx, hwc_rect_t& fbRect) {
+    hwc_rect l_roi = ctx->listStats[mDpy].lRoi;
+    hwc_rect r_roi = ctx->listStats[mDpy].rRoi;
+
+    hwc_rect_t l_fbRect = getIntersection(fbRect, l_roi);
+    hwc_rect_t r_fbRect = getIntersection(fbRect, r_roi);
+    fbRect = getUnion(l_fbRect, r_fbRect);
+}
+/* 1) Identify layers that are not visible or lying outside BOTH the updating
+ *    ROI's and drop them from composition. If a layer is spanning across both
+ *    the halves of the screen but needed by only ROI, the non-contributing
+ *    half will not be programmed for MDP.
+ * 2) If we have a scaling layer which needs cropping against generated
+ *    ROI, reset ROI to full resolution. */
+bool MDPCompSplit::validateAndApplyROI(hwc_context_t *ctx,
+        hwc_display_contents_1_t* list) {
+
+    int numAppLayers = ctx->listStats[mDpy].numAppLayers;
+
+    hwc_rect_t visibleRectL = ctx->listStats[mDpy].lRoi;
+    hwc_rect_t visibleRectR = ctx->listStats[mDpy].rRoi;
+
+    for(int i = numAppLayers - 1; i >= 0; i--){
+        if(!isValidRect(visibleRectL) && !isValidRect(visibleRectR))
+        {
+            mCurrentFrame.drop[i] = true;
+            mCurrentFrame.dropCount++;
+            continue;
+        }
+
+        const hwc_layer_1_t* layer =  &list->hwLayers[i];
+        hwc_rect_t dstRect = layer->displayFrame;
+
+        hwc_rect_t l_res  = getIntersection(visibleRectL, dstRect);
+        hwc_rect_t r_res  = getIntersection(visibleRectR, dstRect);
+        hwc_rect_t res = getUnion(l_res, r_res);
+
+        if(!isValidRect(l_res) && !isValidRect(r_res)) {
+            mCurrentFrame.drop[i] = true;
+            mCurrentFrame.dropCount++;
+        } else {
+            /* Reset frame ROI when any layer which needs scaling also needs ROI
+             * cropping */
+            if(!isSameRect(res, dstRect) && needsScaling (layer)) {
+                memset(&mCurrentFrame.drop, 0, sizeof(mCurrentFrame.drop));
+                mCurrentFrame.dropCount = 0;
+                return false;
+            }
+
+            if (layer->blending == HWC_BLENDING_NONE) {
+                visibleRectL = deductRect(visibleRectL, l_res);
+                visibleRectR = deductRect(visibleRectR, r_res);
+            }
+        }
+    }
+    return true;
+}
+/* Calculate ROI for the frame by accounting all the layer's dispalyFrame which
+ * are updating. If DirtyRegion is applicable, calculate it by accounting all
+ * the changing layer's dirtyRegion. */
+void MDPCompSplit::generateROI(hwc_context_t *ctx,
+        hwc_display_contents_1_t* list) {
+    if(!canPartialUpdate(ctx, list))
+        return;
+
+    int numAppLayers = ctx->listStats[mDpy].numAppLayers;
+    int lSplit = getLeftSplit(ctx, mDpy);
+
+    int hw_h = (int)ctx->dpyAttr[mDpy].yres;
+    int hw_w = (int)ctx->dpyAttr[mDpy].xres;
+
+    struct hwc_rect l_frame = (struct hwc_rect){0, 0, lSplit, hw_h};
+    struct hwc_rect r_frame = (struct hwc_rect){lSplit, 0, hw_w, hw_h};
+
+    struct hwc_rect l_roi = (struct hwc_rect){0, 0, 0, 0};
+    struct hwc_rect r_roi = (struct hwc_rect){0, 0, 0, 0};
+
+    for(int index = 0; index < numAppLayers; index++ ) {
+        hwc_layer_1_t* layer = &list->hwLayers[index];
+        if ((mCachedFrame.hnd[index] != layer->handle) ||
+                isYuvBuffer((private_handle_t *)layer->handle)) {
+            hwc_rect_t updatingRect = layer->displayFrame;
+
+#ifdef QCOM_BSP
+            if(!needsScaling(layer) && !layer->transform)
+                updatingRect =  layer->dirtyRect;
+#endif
+
+            hwc_rect_t l_dst  = getIntersection(l_frame, updatingRect);
+            if(isValidRect(l_dst))
+                l_roi = getUnion(l_roi, l_dst);
+
+            hwc_rect_t r_dst  = getIntersection(r_frame, updatingRect);
+            if(isValidRect(r_dst))
+                r_roi = getUnion(r_roi, r_dst);
+        }
+    }
+
+    /* No layer is updating. Still SF wants a refresh. */
+    if(!isValidRect(l_roi) && !isValidRect(r_roi))
+        return;
+
+    l_roi = getSanitizeROI(l_roi, l_frame);
+    r_roi = getSanitizeROI(r_roi, r_frame);
+
+    ctx->listStats[mDpy].lRoi = l_roi;
+    ctx->listStats[mDpy].rRoi = r_roi;
+
+    if(!validateAndApplyROI(ctx, list))
+        resetROI(ctx, mDpy);
+
+    ALOGD_IF(isDebug(),"%s: generated L_ROI: [%d, %d, %d, %d]"
+            "R_ROI: [%d, %d, %d, %d]", __FUNCTION__,
+            ctx->listStats[mDpy].lRoi.left, ctx->listStats[mDpy].lRoi.top,
+            ctx->listStats[mDpy].lRoi.right, ctx->listStats[mDpy].lRoi.bottom,
+            ctx->listStats[mDpy].rRoi.left, ctx->listStats[mDpy].rRoi.top,
+            ctx->listStats[mDpy].rRoi.right, ctx->listStats[mDpy].rRoi.bottom);
 }
 
 /* Checks for conditions where all the layers marked for MDP comp cannot be
@@ -799,6 +891,16 @@
     return true;
 }
 
+bool MDPComp::canPartialUpdate(hwc_context_t *ctx,
+        hwc_display_contents_1_t* list){
+    if(!qdutils::MDPVersion::getInstance().isPartialUpdateEnabled() ||
+            isSkipPresent(ctx, mDpy) || (list->flags & HWC_GEOMETRY_CHANGED) ||
+            mDpy ) {
+        return false;
+    }
+    return true;
+}
+
 bool MDPComp::tryVideoOnly(hwc_context_t *ctx,
         hwc_display_contents_1_t* list) {
     const bool secureOnly = true;
@@ -1093,7 +1195,8 @@
              mCurrentFrame.fbCount);
 }
 
-hwc_rect_t MDPComp::getUpdatingFBRect(hwc_display_contents_1_t* list){
+hwc_rect_t MDPComp::getUpdatingFBRect(hwc_context_t *ctx,
+        hwc_display_contents_1_t* list){
     hwc_rect_t fbRect = (struct hwc_rect){0, 0, 0, 0};
 
     /* Update only the region of FB needed for composition */
@@ -1104,6 +1207,7 @@
             fbRect = getUnion(fbRect, dst);
         }
     }
+    trimAgainstROI(ctx, fbRect);
     return fbRect;
 }
 
@@ -1124,7 +1228,7 @@
 
     //Configure framebuffer first if applicable
     if(mCurrentFrame.fbZ >= 0) {
-        hwc_rect_t fbRect = getUpdatingFBRect(list);
+        hwc_rect_t fbRect = getUpdatingFBRect(ctx, list);
         if(!ctx->mFBUpdate[mDpy]->prepare(ctx, list, fbRect, mCurrentFrame.fbZ))
         {
             ALOGD_IF(isDebug(), "%s configure framebuffer failed",
@@ -1276,6 +1380,7 @@
         if(tryFullFrame(ctx, list) || tryVideoOnly(ctx, list)) {
             setMDPCompLayerFlags(ctx, list);
         } else {
+            resetROI(ctx, mDpy);
             reset(ctx);
             memset(&mCurrentFrame.drop, 0, sizeof(mCurrentFrame.drop));
             mCurrentFrame.dropCount = 0;
@@ -1587,13 +1692,17 @@
     pipeSpecs.mixer = Overlay::MIXER_LEFT;
     pipeSpecs.fb = false;
 
-    if (dst.left < lSplit) {
+    // Acquire pipe only for the updating half
+    hwc_rect_t l_roi = ctx->listStats[mDpy].lRoi;
+    hwc_rect_t r_roi = ctx->listStats[mDpy].rRoi;
+
+    if (dst.left < lSplit && isValidRect(getIntersection(dst, l_roi))) {
         pipe_info.lIndex = ctx->mOverlay->getPipe(pipeSpecs);
         if(pipe_info.lIndex == ovutils::OV_INVALID)
             return false;
     }
 
-    if(dst.right > lSplit) {
+    if(dst.right > lSplit && isValidRect(getIntersection(dst, r_roi))) {
         pipeSpecs.mixer = Overlay::MIXER_RIGHT;
         pipe_info.rIndex = ctx->mOverlay->getPipe(pipeSpecs);
         if(pipe_info.rIndex == ovutils::OV_INVALID)
diff --git a/libhwcomposer/hwc_mdpcomp.h b/libhwcomposer/hwc_mdpcomp.h
index 8186fff..d974875 100644
--- a/libhwcomposer/hwc_mdpcomp.h
+++ b/libhwcomposer/hwc_mdpcomp.h
@@ -142,6 +142,15 @@
     /* configures 4kx2k yuv layer*/
     virtual int configure4k2kYuv(hwc_context_t *ctx, hwc_layer_1_t *layer,
             PipeLayerPair& PipeLayerPair) = 0;
+    /* generates ROI based on the modified area of the frame */
+    virtual void generateROI(hwc_context_t *ctx,
+            hwc_display_contents_1_t* list) = 0;
+    /* validates the ROI generated for fallback conditions */
+    virtual bool validateAndApplyROI(hwc_context_t *ctx,
+            hwc_display_contents_1_t* list) = 0;
+    /* Trims fbRect calculated against ROI generated */
+    virtual void trimAgainstROI(hwc_context_t *ctx, hwc_rect_t& fbRect) = 0;
+
     /* set/reset flags for MDPComp */
     void setMDPCompLayerFlags(hwc_context_t *ctx,
                               hwc_display_contents_1_t* list);
@@ -173,11 +182,6 @@
     /* checks if MDP/MDSS can process current list w.r.to HW limitations
      * All peculiar HW limitations should go here */
     bool hwLimitationsCheck(hwc_context_t* ctx, hwc_display_contents_1_t* list);
-    /* generates ROI based on the modified area of the frame */
-    void generateROI(hwc_context_t *ctx, hwc_display_contents_1_t* list);
-    bool validateAndApplyROI(hwc_context_t *ctx, hwc_display_contents_1_t* list,
-                             hwc_rect_t roi);
-
     /* Is debug enabled */
     static bool isDebug() { return sDebugLogs ? true : false; };
     /* Is feature enabled */
@@ -212,14 +216,14 @@
     void reset(hwc_context_t *ctx);
     bool isSupportedForMDPComp(hwc_context_t *ctx, hwc_layer_1_t* layer);
     bool resourceCheck();
-    hwc_rect_t getUpdatingFBRect(hwc_display_contents_1_t* list);
-    bool canDoPartialUpdate(hwc_context_t *ctx, hwc_display_contents_1_t* list);
+    hwc_rect_t getUpdatingFBRect(hwc_context_t *ctx,
+            hwc_display_contents_1_t* list);
+    /* checks for conditions to enable partial udpate */
+    bool canPartialUpdate(hwc_context_t *ctx, hwc_display_contents_1_t* list);
 
     int mDpy;
     static bool sEnabled;
     static bool sEnableMixedMode;
-    /* Enables Partial frame composition */
-    static bool sEnablePartialFrameUpdate;
     static bool sDebugLogs;
     static bool sIdleFallBack;
     /* Handles the timeout event from kernel, if the value is set to true */
@@ -262,6 +266,14 @@
     /* configures 4kx2k yuv layer to 2 VG pipes*/
     virtual int configure4k2kYuv(hwc_context_t *ctx, hwc_layer_1_t *layer,
             PipeLayerPair& PipeLayerPair);
+    /* generates ROI based on the modified area of the frame */
+    virtual void generateROI(hwc_context_t *ctx,
+            hwc_display_contents_1_t* list);
+    /* validates the ROI generated for fallback conditions */
+    virtual bool validateAndApplyROI(hwc_context_t *ctx,
+            hwc_display_contents_1_t* list);
+    /* Trims fbRect calculated against ROI generated */
+    virtual void trimAgainstROI(hwc_context_t *ctx, hwc_rect_t& fbRect);
 };
 
 class MDPCompSplit : public MDPComp {
@@ -287,7 +299,6 @@
     /* allocates pipes to selected candidates */
     virtual bool allocLayerPipes(hwc_context_t *ctx,
                                  hwc_display_contents_1_t* list);
-
 private:
     /* Increments mdpCount if 4k2k yuv layer split is enabled.
      * updates framebuffer z order if fb lies above source-split layer */
@@ -297,6 +308,14 @@
     /* configures 4kx2k yuv layer*/
     virtual int configure4k2kYuv(hwc_context_t *ctx, hwc_layer_1_t *layer,
             PipeLayerPair& PipeLayerPair);
+    /* generates ROI based on the modified area of the frame */
+    virtual void generateROI(hwc_context_t *ctx,
+            hwc_display_contents_1_t* list);
+    /* validates the ROI generated for fallback conditions */
+    virtual bool validateAndApplyROI(hwc_context_t *ctx,
+            hwc_display_contents_1_t* list);
+    /* Trims fbRect calculated against ROI generated */
+    virtual void trimAgainstROI(hwc_context_t *ctx, hwc_rect_t& fbRect);
 };
 
 class MDPCompSrcSplit : public MDPCompSplit {
diff --git a/libhwcomposer/hwc_utils.cpp b/libhwcomposer/hwc_utils.cpp
index 4cc8243..b5a7629 100644
--- a/libhwcomposer/hwc_utils.cpp
+++ b/libhwcomposer/hwc_utils.cpp
@@ -834,13 +834,13 @@
     char property[PROPERTY_VALUE_MAX];
     ctx->listStats[dpy].extOnlyLayerIndex = -1;
     ctx->listStats[dpy].isDisplayAnimating = false;
-    ctx->listStats[dpy].roi = ovutils::Dim(0, 0,
-                      (int)ctx->dpyAttr[dpy].xres, (int)ctx->dpyAttr[dpy].yres);
     ctx->listStats[dpy].secureUI = false;
     ctx->listStats[dpy].yuv4k2kCount = 0;
     ctx->mViewFrame[dpy] = (hwc_rect_t){0, 0, 0, 0};
     ctx->dpyAttr[dpy].mActionSafePresent = isActionSafePresent(ctx, dpy);
 
+    resetROI(ctx, dpy);
+
     // Calculate view frame of ext display from primary resolution
     // and primary device orientation.
     ctx->mViewFrame[dpy] = calculateDisplayViewFrame(ctx, dpy);
@@ -1070,6 +1070,12 @@
     return isValidRect(irect);
 }
 
+bool isSameRect(const hwc_rect& rect1, const hwc_rect& rect2)
+{
+   return ((rect1.left == rect2.left) && (rect1.top == rect2.top) &&
+           (rect1.right == rect2.right) && (rect1.bottom == rect2.bottom));
+}
+
 bool isValidRect(const hwc_rect& rect)
 {
    return ((rect.bottom > rect.top) && (rect.right > rect.left)) ;
@@ -2164,7 +2170,20 @@
     }
 }
 
-hwc_rect_t sanitizeROI(struct hwc_rect roi, hwc_rect boundary)
+void resetROI(hwc_context_t *ctx, const int dpy) {
+    const int fbXRes = (int)ctx->dpyAttr[dpy].xres;
+    const int fbYRes = (int)ctx->dpyAttr[dpy].yres;
+    if(isDisplaySplit(ctx, dpy)) {
+        const int lSplit = getLeftSplit(ctx, dpy);
+        ctx->listStats[dpy].lRoi = (struct hwc_rect){0, 0, lSplit, fbYRes};
+        ctx->listStats[dpy].rRoi = (struct hwc_rect){lSplit, 0, fbXRes, fbYRes};
+    } else  {
+        ctx->listStats[dpy].lRoi = (struct hwc_rect){0, 0,fbXRes, fbYRes};
+        ctx->listStats[dpy].rRoi = (struct hwc_rect){0, 0, 0, 0};
+    }
+}
+
+hwc_rect_t getSanitizeROI(struct hwc_rect roi, hwc_rect boundary)
 {
    if(!isValidRect(roi))
       return roi;
@@ -2176,6 +2195,7 @@
    const int TOP_ALIGN = qdutils::MDPVersion::getInstance().getTopAlign();
    const int HEIGHT_ALIGN = qdutils::MDPVersion::getInstance().getHeightAlign();
    const int MIN_WIDTH = qdutils::MDPVersion::getInstance().getMinROIWidth();
+   const int MIN_HEIGHT = qdutils::MDPVersion::getInstance().getMinROIHeight();
 
    /* Align to minimum width recommended by the panel */
    if((t_roi.right - t_roi.left) < MIN_WIDTH) {
@@ -2185,7 +2205,18 @@
            t_roi.right = t_roi.left + MIN_WIDTH;
    }
 
+  /* Align to minimum height recommended by the panel */
+   if((t_roi.bottom - t_roi.top) < MIN_HEIGHT) {
+       if((t_roi.top + MIN_HEIGHT) > boundary.bottom)
+           t_roi.top = t_roi.bottom - MIN_HEIGHT;
+       else
+           t_roi.bottom = t_roi.top + MIN_HEIGHT;
+   }
+
    /* Align left and width to meet panel restrictions */
+   if(LEFT_ALIGN)
+       t_roi.left = t_roi.left - (t_roi.left % LEFT_ALIGN);
+
    if(WIDTH_ALIGN) {
        int width = t_roi.right - t_roi.left;
        width = WIDTH_ALIGN * ((width + (WIDTH_ALIGN - 1)) / WIDTH_ALIGN);
@@ -2194,13 +2225,17 @@
        if(t_roi.right > boundary.right) {
            t_roi.right = boundary.right;
            t_roi.left = t_roi.right - width;
+
+           if(LEFT_ALIGN)
+               t_roi.left = t_roi.left - (t_roi.left % LEFT_ALIGN);
        }
    }
 
-   if(LEFT_ALIGN)
-       t_roi.left = t_roi.left - (t_roi.left % LEFT_ALIGN);
 
    /* Align top and height to meet panel restrictions */
+   if(TOP_ALIGN)
+       t_roi.top = t_roi.top - (t_roi.top % TOP_ALIGN);
+
    if(HEIGHT_ALIGN) {
        int height = t_roi.bottom - t_roi.top;
        height = HEIGHT_ALIGN *  ((height + (HEIGHT_ALIGN - 1)) / HEIGHT_ALIGN);
@@ -2209,11 +2244,12 @@
        if(t_roi.bottom > boundary.bottom) {
            t_roi.bottom = boundary.bottom;
            t_roi.top = t_roi.bottom - height;
+
+           if(TOP_ALIGN)
+               t_roi.top = t_roi.top - (t_roi.top % TOP_ALIGN);
        }
    }
 
-   if(TOP_ALIGN)
-       t_roi.top = t_roi.top - (t_roi.top % TOP_ALIGN);
 
    return t_roi;
 }
diff --git a/libhwcomposer/hwc_utils.h b/libhwcomposer/hwc_utils.h
index 0883a51..234804c 100644
--- a/libhwcomposer/hwc_utils.h
+++ b/libhwcomposer/hwc_utils.h
@@ -123,9 +123,10 @@
     // Notifies hwcomposer about the start and end of animation
     // This will be set to true during animation, otherwise false.
     bool isDisplayAnimating;
-    ovutils::Dim roi;
     bool secureUI; // Secure display layer
     bool isSecurePresent;
+    hwc_rect_t lRoi;  //left ROI
+    hwc_rect_t rRoi;  //right ROI. Unused in single DSI panels.
 };
 
 struct LayerProp {
@@ -260,6 +261,7 @@
 int getExtOrientation(hwc_context_t* ctx);
 bool isValidRect(const hwc_rect_t& rect);
 hwc_rect_t deductRect(const hwc_rect_t& rect1, const hwc_rect_t& rect2);
+bool isSameRect(const hwc_rect& rect1, const hwc_rect& rect2);
 hwc_rect_t getIntersection(const hwc_rect_t& rect1, const hwc_rect_t& rect2);
 hwc_rect_t getUnion(const hwc_rect_t& rect1, const hwc_rect_t& rect2);
 void optimizeLayerRects(const hwc_display_contents_1_t *list);
@@ -295,8 +297,11 @@
 /* Get External State names */
 const char* getExternalDisplayState(uint32_t external_state);
 
+// Resets display ROI to full panel resoluion
+void resetROI(hwc_context_t *ctx, const int dpy);
+
 // Aligns updating ROI to panel restrictions
-hwc_rect_t sanitizeROI(struct hwc_rect roi, hwc_rect boundary);
+hwc_rect_t getSanitizeROI(struct hwc_rect roi, hwc_rect boundary);
 
 // Handles wfd Pause and resume events
 void handle_pause(hwc_context_t *ctx, int dpy);
diff --git a/liboverlay/overlay.cpp b/liboverlay/overlay.cpp
index d53b46d..879dde4 100644
--- a/liboverlay/overlay.cpp
+++ b/liboverlay/overlay.cpp
@@ -471,19 +471,24 @@
 }
 
 bool Overlay::displayCommit(const int& fd) {
-    utils::Dim roi;
-    return displayCommit(fd, roi);
+    utils::Dim lRoi, rRoi;
+    return displayCommit(fd, lRoi, rRoi);
 }
 
-bool Overlay::displayCommit(const int& fd, const utils::Dim& roi) {
+bool Overlay::displayCommit(const int& fd, const utils::Dim& lRoi,
+        const utils::Dim& rRoi) {
     //Commit
     struct mdp_display_commit info;
     memset(&info, 0, sizeof(struct mdp_display_commit));
     info.flags = MDP_DISPLAY_COMMIT_OVERLAY;
-    info.roi.x = roi.x;
-    info.roi.y = roi.y;
-    info.roi.w = roi.w;
-    info.roi.h = roi.h;
+    info.l_roi.x = lRoi.x;
+    info.l_roi.y = lRoi.y;
+    info.l_roi.w = lRoi.w;
+    info.l_roi.h = lRoi.h;
+    info.r_roi.x = rRoi.x;
+    info.r_roi.y = rRoi.y;
+    info.r_roi.w = rRoi.w;
+    info.r_roi.h = rRoi.h;
 
     if(!mdp_wrapper::displayCommit(fd, info)) {
         ALOGE("%s: commit failed", __func__);
diff --git a/liboverlay/overlay.h b/liboverlay/overlay.h
index 455f547..8c0205c 100644
--- a/liboverlay/overlay.h
+++ b/liboverlay/overlay.h
@@ -149,8 +149,12 @@
     static int getDMAMode();
     /* Returns the framebuffer node backing up the display */
     static int getFbForDpy(const int& dpy);
-    static bool displayCommit(const int& fd, const utils::Dim& roi);
+
     static bool displayCommit(const int& fd);
+    /* Overloads display commit with ROI's of each halves.
+     * Single interface panels will only update left ROI. */
+    static bool displayCommit(const int& fd, const utils::Dim& lRoi,
+                              const utils::Dim& rRoi);
 
 private:
     /* Ctor setup */
diff --git a/libqdutils/mdp_version.h b/libqdutils/mdp_version.h
index cace430..fa137c0 100644
--- a/libqdutils/mdp_version.h
+++ b/libqdutils/mdp_version.h
@@ -128,10 +128,7 @@
     bool supportsMacroTile();
     int getLeftSplit() { return mSplit.left(); }
     int getRightSplit() { return mSplit.right(); }
-    bool isPartialUpdateEnabled() {
-        return mPanelInfo.mPartialUpdateEnable &&
-                mPanelInfo.mType == MIPI_CMD_PANEL;
-    }
+    bool isPartialUpdateEnabled() { return mPanelInfo.mPartialUpdateEnable; }
     int getLeftAlign() { return mPanelInfo.mLeftAlign; }
     int getWidthAlign() { return mPanelInfo.mWidthAlign; }
     int getTopAlign() { return mPanelInfo.mTopAlign; }