Merge "hwc: Clean up scattered definitions of commonly used constants/values"
diff --git a/libgralloc/alloc_controller.cpp b/libgralloc/alloc_controller.cpp
index 7255391..0e80843 100644
--- a/libgralloc/alloc_controller.cpp
+++ b/libgralloc/alloc_controller.cpp
@@ -592,7 +592,7 @@
         //Planar
         case HAL_PIXEL_FORMAT_YV12:
             ystride = hnd->width;
-            cstride = hnd->width/2;
+            cstride = ALIGN(hnd->width/2, 16);
             ycbcr->y  = (void*)hnd->base;
             ycbcr->cr = (void*)(hnd->base + ystride * hnd->height);
             ycbcr->cb = (void*)(hnd->base + ystride * hnd->height +
diff --git a/libgralloc/fb_priv.h b/libgralloc/fb_priv.h
index 191aa2a..e2eba6a 100644
--- a/libgralloc/fb_priv.h
+++ b/libgralloc/fb_priv.h
@@ -43,9 +43,7 @@
     uint32_t numBuffers;
     uint32_t bufferMask;
     pthread_mutex_t lock;
-    private_handle_t *currentBuffer;
     struct fb_var_screeninfo info;
-    struct mdp_display_commit commit;
     struct fb_fix_screeninfo finfo;
     float xdpi;
     float ydpi;
diff --git a/libgralloc/framebuffer.cpp b/libgralloc/framebuffer.cpp
index ca11bea..0ebc3db 100644
--- a/libgralloc/framebuffer.cpp
+++ b/libgralloc/framebuffer.cpp
@@ -58,9 +58,10 @@
 
 struct fb_context_t {
     framebuffer_device_t  device;
+    //fd - which is returned on open
+    int fbFd;
 };
 
-
 static int fb_setSwapInterval(struct framebuffer_device_t* dev,
                               int interval)
 {
@@ -87,11 +88,12 @@
         reinterpret_cast<private_module_t*>(dev->common.module);
     private_handle_t *hnd = static_cast<private_handle_t*>
         (const_cast<native_handle_t*>(buffer));
+    fb_context_t *ctx = reinterpret_cast<fb_context_t*>(dev);
     const unsigned int offset = (unsigned int) (hnd->base -
             m->framebuffer->base);
     m->info.activate = FB_ACTIVATE_VBL;
     m->info.yoffset = (int)(offset / m->finfo.line_length);
-    if (ioctl(m->framebuffer->fd, FBIOPUT_VSCREENINFO, &m->info) == -1) {
+    if (ioctl(ctx->fbFd, FBIOPUT_VSCREENINFO, &m->info) == -1) {
         ALOGE("%s: FBIOPUT_VSCREENINFO for primary failed, str: %s",
                 __FUNCTION__, strerror(errno));
         return -errno;
@@ -110,8 +112,11 @@
     return 0;
 }
 
-int mapFrameBufferLocked(struct private_module_t* module)
+int mapFrameBufferLocked(framebuffer_device_t *dev)
 {
+    private_module_t* module =
+        reinterpret_cast<private_module_t*>(dev->common.module);
+    fb_context_t *ctx = reinterpret_cast<fb_context_t*>(dev);
     // already initialized...
     if (module->framebuffer) {
         return 0;
@@ -134,8 +139,6 @@
     if (fd < 0)
         return -errno;
 
-    memset(&module->commit, 0, sizeof(struct mdp_display_commit));
-
     struct fb_fix_screeninfo finfo;
     if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1) {
         close(fd);
@@ -329,34 +332,55 @@
     //adreno needs page aligned offsets. Align the fbsize to pagesize.
     unsigned int fbSize = roundUpToPageSize(finfo.line_length * info.yres)*
                     module->numBuffers;
-    module->framebuffer = new private_handle_t(fd, fbSize,
-                                        private_handle_t::PRIV_FLAGS_USES_ION,
-                                        BUFFER_TYPE_UI,
-                                        module->fbFormat, info.xres, info.yres);
     void* vaddr = mmap(0, fbSize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
     if (vaddr == MAP_FAILED) {
         ALOGE("Error mapping the framebuffer (%s)", strerror(errno));
         close(fd);
         return -errno;
     }
+    //store the framebuffer fd in the ctx
+    ctx->fbFd = fd;
+#ifdef MSMFB_METADATA_GET
+    memset(&metadata, 0 , sizeof(metadata));
+    metadata.op = metadata_op_get_ion_fd;
+    // get the ION fd for the framebuffer, as GPU needs ION fd
+    if (ioctl(fd, MSMFB_METADATA_GET, &metadata) == -1) {
+        ALOGE("Error getting ION fd (%s)", strerror(errno));
+        close(fd);
+        return -errno;
+    }
+    if(metadata.data.fbmem_ionfd < 0) {
+        ALOGE("Error: Ioctl returned invalid ION fd = %d",
+                                        metadata.data.fbmem_ionfd);
+        close(fd);
+        return -errno;
+    }
+    fd = metadata.data.fbmem_ionfd;
+#endif
+    // Create framebuffer handle using the ION fd
+    module->framebuffer = new private_handle_t(fd, fbSize,
+                                        private_handle_t::PRIV_FLAGS_USES_ION,
+                                        BUFFER_TYPE_UI,
+                                        module->fbFormat, info.xres, info.yres);
     module->framebuffer->base = uint64_t(vaddr);
     memset(vaddr, 0, fbSize);
     //Enable vsync
     int enable = 1;
-    ioctl(module->framebuffer->fd, MSMFB_OVERLAY_VSYNC_CTRL,
-             &enable);
+    ioctl(ctx->fbFd, MSMFB_OVERLAY_VSYNC_CTRL, &enable);
     return 0;
 }
 
-static int mapFrameBuffer(struct private_module_t* module)
+static int mapFrameBuffer(framebuffer_device_t *dev)
 {
     int err = -1;
     char property[PROPERTY_VALUE_MAX];
     if((property_get("debug.gralloc.map_fb_memory", property, NULL) > 0) &&
        (!strncmp(property, "1", PROPERTY_VALUE_MAX ) ||
         (!strncasecmp(property,"true", PROPERTY_VALUE_MAX )))) {
+        private_module_t* module =
+            reinterpret_cast<private_module_t*>(dev->common.module);
         pthread_mutex_lock(&module->lock);
-        err = mapFrameBufferLocked(module);
+        err = mapFrameBufferLocked(dev);
         pthread_mutex_unlock(&module->lock);
     }
     return err;
@@ -368,6 +392,11 @@
 {
     fb_context_t* ctx = (fb_context_t*)dev;
     if (ctx) {
+#ifdef MSMFB_METADATA_GET
+        if(ctx->fbFd >=0) {
+            close(ctx->fbFd);
+        }
+#endif
         //Hack until fbdev is removed. Framework could close this causing hwc a
         //pain.
         //free(ctx);
@@ -403,8 +432,8 @@
         dev->device.setUpdateRect   = 0;
         dev->device.compositionComplete = fb_compositionComplete;
 
-        private_module_t* m = (private_module_t*)module;
-        status = mapFrameBuffer(m);
+        status = mapFrameBuffer((framebuffer_device_t*)dev);
+        private_module_t* m = (private_module_t*)dev->device.common.module;
         if (status >= 0) {
             int stride = m->finfo.line_length / (m->info.bits_per_pixel >> 3);
             const_cast<uint32_t&>(dev->device.flags) = 0;
diff --git a/libgralloc/gpu.cpp b/libgralloc/gpu.cpp
index 4067050..9b98f1b 100644
--- a/libgralloc/gpu.cpp
+++ b/libgralloc/gpu.cpp
@@ -224,9 +224,11 @@
 
     // create a "fake" handle for it
     uint64_t vaddr = uint64_t(m->framebuffer->base);
+    // As GPU needs ION FD, the private handle is created
+    // using ION fd and ION flags are set
     private_handle_t* hnd = new private_handle_t(
         dup(m->framebuffer->fd), bufferSize,
-        private_handle_t::PRIV_FLAGS_USES_PMEM |
+        private_handle_t::PRIV_FLAGS_USES_ION |
         private_handle_t::PRIV_FLAGS_FRAMEBUFFER,
         BUFFER_TYPE_UI, m->fbFormat, m->info.xres,
         m->info.yres);
diff --git a/libgralloc/gralloc.cpp b/libgralloc/gralloc.cpp
index c1c61aa..c0aa8cb 100644
--- a/libgralloc/gralloc.cpp
+++ b/libgralloc/gralloc.cpp
@@ -91,7 +91,6 @@
     numBuffers: 0,
     bufferMask: 0,
     lock: PTHREAD_MUTEX_INITIALIZER,
-    currentBuffer: 0,
 };
 
 // Open Gralloc device
diff --git a/libhwcomposer/hwc.cpp b/libhwcomposer/hwc.cpp
index 267815f..3d3d138 100644
--- a/libhwcomposer/hwc.cpp
+++ b/libhwcomposer/hwc.cpp
@@ -147,7 +147,8 @@
                      * to BLOCK_MODE */
 
                     if (canUseRotator(ctx, dpy) &&
-                        has90Transform(layer) && isRotationDoable(ctx, hnd)) {
+                        (has90Transform(layer) || getRotDownscale(ctx, layer))
+                        && isRotationDoable(ctx, hnd)) {
                         if(not ctx->mOverlay->isDMAMultiplexingSupported()) {
                             if(ctx->mOverlay->isPipeTypeAttached(
                                              overlay::utils::OV_MDP_PIPE_DMA))
@@ -673,18 +674,12 @@
             ret = -1;
         }
 
-        int extOnlyLayerIndex =
-                ctx->listStats[dpy].extOnlyLayerIndex;
-
         private_handle_t *hnd = (private_handle_t *)fbLayer->handle;
-        if(extOnlyLayerIndex!= -1) {
-            hwc_layer_1_t *extLayer = &list->hwLayers[extOnlyLayerIndex];
-            hnd = (private_handle_t *)extLayer->handle;
-        } else if(copybitDone) {
+        if(copybitDone) {
             hnd = ctx->mCopyBit[dpy]->getCurrentRenderBuffer();
         }
 
-        if(hnd && !isYuvBuffer(hnd)) {
+        if(hnd) {
             if (!ctx->mFBUpdate[dpy]->draw(ctx, hnd)) {
                 ALOGE("%s: FBUpdate::draw fail!", __FUNCTION__);
                 ret = -1;
diff --git a/libhwcomposer/hwc_ad.cpp b/libhwcomposer/hwc_ad.cpp
index a26f659..f849450 100644
--- a/libhwcomposer/hwc_ad.cpp
+++ b/libhwcomposer/hwc_ad.cpp
@@ -76,11 +76,15 @@
             "/sys/class/graphics/fb%d/ad", wbFbNum);
     int adFd = open(wbFbPath, O_RDONLY);
     if(adFd >= 0) {
-        char opStr[4] = {'\0'};
-        if(read(adFd, opStr, strlen(opStr)) >= 0) {
+        char opStr[4];
+        ssize_t bytesRead = read(adFd, opStr, sizeof(opStr) - 1);
+        if(bytesRead > 0) {
+            opStr[bytesRead] = '\0';
             //Should return -1, 0 or 1
             ret = atoi(opStr);
             ALOGD_IF(DEBUG, "%s: Read %d from ad", __func__, ret);
+        } else if(bytesRead == 0) {
+            ALOGE("%s: ad node empty", __func__);
         } else {
             ALOGE("%s: Read from ad node failed with error %s", __func__,
                     strerror(errno));
@@ -210,7 +214,7 @@
                 ovutils::OV_MDP_SECURE_OVERLAY_SESSION);
     }
 
-    PipeArgs parg(mdpFlags, whf, ZORDER_0, IS_FG_OFF,
+    PipeArgs parg(mdpFlags, whf, ZORDER_0,
             ROT_FLAGS_NONE);
     hwc_rect_t dst = crop; //input same as output
 
diff --git a/libhwcomposer/hwc_fbupdate.cpp b/libhwcomposer/hwc_fbupdate.cpp
index 5014af9..7a2ff09 100644
--- a/libhwcomposer/hwc_fbupdate.cpp
+++ b/libhwcomposer/hwc_fbupdate.cpp
@@ -132,12 +132,6 @@
     bool ret = false;
     hwc_layer_1_t *layer = &list->hwLayers[list->numHwLayers - 1];
     if (LIKELY(ctx->mOverlay)) {
-        int extOnlyLayerIndex = ctx->listStats[mDpy].extOnlyLayerIndex;
-        // ext only layer present..
-        if(extOnlyLayerIndex != -1) {
-            layer = &list->hwLayers[extOnlyLayerIndex];
-            layer->compositionType = HWC_OVERLAY;
-        }
         overlay::Overlay& ov = *(ctx->mOverlay);
 
         ovutils::Whf info(mAlignedFBWidth, mAlignedFBHeight,
@@ -165,7 +159,6 @@
         }
 
         ovutils::eMdpFlags mdpFlags = ovutils::OV_MDP_BLEND_FG_PREMULT;
-        ovutils::eIsFg isFg = ovutils::IS_FG_OFF;
         ovutils::eZorder zOrder = static_cast<ovutils::eZorder>(fbZorder);
 
         hwc_rect_t sourceCrop = integerizeSourceCrop(layer->sourceCropf);
@@ -193,10 +186,8 @@
         // Dont do wormhole calculation when extDownscale is enabled on External
         if(ctx->listStats[mDpy].isDisplayAnimating && mDpy) {
             sourceCrop = layer->displayFrame;
-        } else if((!mDpy ||
-                  (mDpy && !extOrient
-                  && !ctx->dpyAttr[mDpy].mDownScaleMode))
-                  && (extOnlyLayerIndex == -1)) {
+        } else if((mDpy && !extOrient
+                  && !ctx->dpyAttr[mDpy].mDownScaleMode)) {
             if(ctx->mOverlay->isUIScalingOnExternalSupported() &&
                 !ctx->dpyAttr[mDpy].customFBSize) {
                 getNonWormholeRegion(list, sourceCrop);
@@ -218,7 +209,7 @@
         //For the mdp, since either we are pre-rotating or MDP does flips
         orient = ovutils::OVERLAY_TRANSFORM_0;
         transform = 0;
-        ovutils::PipeArgs parg(mdpFlags, info, zOrder, isFg,
+        ovutils::PipeArgs parg(mdpFlags, info, zOrder,
                                static_cast<ovutils::eRotFlags>(rotFlags),
                                ovutils::DEFAULT_PLANE_ALPHA,
                                (ovutils::eBlending)
@@ -285,12 +276,6 @@
     bool ret = false;
     hwc_layer_1_t *layer = &list->hwLayers[list->numHwLayers - 1];
     if (LIKELY(ctx->mOverlay)) {
-        /*  External only layer present */
-        int extOnlyLayerIndex = ctx->listStats[mDpy].extOnlyLayerIndex;
-        if(extOnlyLayerIndex != -1) {
-            layer = &list->hwLayers[extOnlyLayerIndex];
-            layer->compositionType = HWC_OVERLAY;
-        }
         ovutils::Whf info(mAlignedFBWidth, mAlignedFBHeight,
                           ovutils::getMdpFormat(HAL_PIXEL_FORMAT_RGBA_8888,
                                                 mTileEnabled));
@@ -333,7 +318,6 @@
             ovutils::PipeArgs pargL(mdpFlags,
                                     info,
                                     zOrder,
-                                    ovutils::IS_FG_OFF,
                                     ovutils::ROT_FLAGS_NONE,
                                     ovutils::DEFAULT_PLANE_ALPHA,
                                     (ovutils::eBlending)
@@ -369,7 +353,6 @@
             ovutils::PipeArgs pargR(mdpFlagsR,
                                     info,
                                     zOrder,
-                                    ovutils::IS_FG_OFF,
                                     ovutils::ROT_FLAGS_NONE,
                                     ovutils::DEFAULT_PLANE_ALPHA,
                                     (ovutils::eBlending)
@@ -424,13 +407,6 @@
 bool FBSrcSplit::configure(hwc_context_t *ctx, hwc_display_contents_1 *list,
         hwc_rect_t fbUpdatingRect, int fbZorder) {
     hwc_layer_1_t *layer = &list->hwLayers[list->numHwLayers - 1];
-    int extOnlyLayerIndex = ctx->listStats[mDpy].extOnlyLayerIndex;
-    // ext only layer present..
-    if(extOnlyLayerIndex != -1) {
-        layer = &list->hwLayers[extOnlyLayerIndex];
-        layer->compositionType = HWC_OVERLAY;
-    }
-
     overlay::Overlay& ov = *(ctx->mOverlay);
 
     ovutils::Whf info(mAlignedFBWidth,
@@ -444,7 +420,6 @@
     ovutils::PipeArgs parg(mdpFlags,
             info,
             zOrder,
-            ovutils::IS_FG_OFF,
             ovutils::ROT_FLAGS_NONE,
             ovutils::DEFAULT_PLANE_ALPHA,
             (ovutils::eBlending)
diff --git a/libhwcomposer/hwc_mdpcomp.cpp b/libhwcomposer/hwc_mdpcomp.cpp
index 863c69e..0d968c8 100644
--- a/libhwcomposer/hwc_mdpcomp.cpp
+++ b/libhwcomposer/hwc_mdpcomp.cpp
@@ -45,7 +45,7 @@
 bool MDPComp::sEnableMixedMode = true;
 int MDPComp::sSimulationFlags = 0;
 int MDPComp::sMaxPipesPerMixer = MAX_PIPES_PER_MIXER;
-bool MDPComp::sEnable4k2kYUVSplit = false;
+bool MDPComp::sEnableYUVsplit = false;
 bool MDPComp::sSrcSplitEnabled = false;
 MDPComp* MDPComp::getObject(hwc_context_t *ctx, const int& dpy) {
     if(qdutils::MDPVersion::getInstance().isSrcSplit()) {
@@ -156,10 +156,11 @@
     }
 
     if(!qdutils::MDPVersion::getInstance().isSrcSplit() &&
+        !qdutils::MDPVersion::getInstance().isRotDownscaleEnabled() &&
             property_get("persist.mdpcomp.4k2kSplit", property, "0") > 0 &&
             (!strncmp(property, "1", PROPERTY_VALUE_MAX) ||
             !strncasecmp(property,"true", PROPERTY_VALUE_MAX))) {
-        sEnable4k2kYUVSplit = true;
+        sEnableYUVsplit = true;
     }
 
     if ((property_get("persist.hwc.ptor.enable", property, NULL) > 0) &&
@@ -237,11 +238,12 @@
 }
 
 MDPComp::FrameInfo::FrameInfo() {
+    memset(&mdpToLayer, 0, sizeof(mdpToLayer));
     reset(0);
 }
 
 void MDPComp::FrameInfo::reset(const int& numLayers) {
-    for(int i = 0 ; i < MAX_PIPES_PER_MIXER && numLayers; i++ ) {
+    for(int i = 0; i < MAX_PIPES_PER_MIXER; i++) {
         if(mdpToLayer[i].pipeInfo) {
             delete mdpToLayer[i].pipeInfo;
             mdpToLayer[i].pipeInfo = NULL;
@@ -676,7 +678,9 @@
     const int numAppLayers = ctx->listStats[mDpy].numAppLayers;
     int priDispW = ctx->dpyAttr[HWC_DISPLAY_PRIMARY].xres;
 
-    if(sIdleFallBack && !ctx->listStats[mDpy].secureUI) {
+    // No Idle fall back, if secure display or secure RGB layers are present
+    if(sIdleFallBack && (!ctx->listStats[mDpy].secureUI &&
+                    !ctx->listStats[mDpy].secureRGBCount)) {
         ALOGD_IF(isDebug(), "%s: Idle fallback dpy %d",__FUNCTION__, mDpy);
         return false;
     }
@@ -773,7 +777,7 @@
     mCurrentFrame.mdpCount = mCurrentFrame.layerCount - mCurrentFrame.fbCount -
         mCurrentFrame.dropCount;
 
-    if(sEnable4k2kYUVSplit){
+    if(sEnableYUVsplit){
         adjustForSourceSplit(ctx, list);
     }
 
@@ -907,6 +911,11 @@
     private_handle_t *renderBuf = ctx->mCopyBit[mDpy]->getCurrentRenderBuffer();
     Whf layerWhf[numPTORLayersFound]; // To store w,h,f of PTOR layers
 
+    // Store the blending mode, planeAlpha, and transform of PTOR layers
+    int32_t blending[numPTORLayersFound];
+    uint8_t planeAlpha[numPTORLayersFound];
+    uint32_t transform[numPTORLayersFound];
+
     for(int j = 0; j < numPTORLayersFound; j++) {
         int index =  ctx->mPtorInfo.layerIndex[j];
 
@@ -925,6 +934,14 @@
         hnd->height = renderBuf->height;
         hnd->format = renderBuf->format;
 
+        // Store & update blending mode, planeAlpha and transform of PTOR layer
+        blending[j] = layer->blending;
+        planeAlpha[j] = layer->planeAlpha;
+        transform[j] = layer->transform;
+        layer->blending = HWC_BLENDING_NONE;
+        layer->planeAlpha = 0xFF;
+        layer->transform = 0;
+
         // Remove overlap from crop & displayFrame of below layers
         for (int i = 0; i < index && index !=-1; i++) {
             layer = &list->hwLayers[i];
@@ -964,13 +981,17 @@
         layer->sourceCropf.bottom = (float)sourceCrop[i].bottom;
     }
 
-    // Restore w,h,f of PTOR layers
+    // Restore w,h,f, blending attributes, and transform of PTOR layers
     for (int i = 0; i < numPTORLayersFound; i++) {
         int idx = ctx->mPtorInfo.layerIndex[i];
+        hwc_layer_1_t* layer = &list->hwLayers[idx];
         private_handle_t *hnd = (private_handle_t *)list->hwLayers[idx].handle;
         hnd->width = layerWhf[i].w;
         hnd->height = layerWhf[i].h;
         hnd->format = layerWhf[i].format;
+        layer->blending = blending[i];
+        layer->planeAlpha = planeAlpha[i];
+        layer->transform = transform[i];
     }
 
     if (!result) {
@@ -1029,6 +1050,8 @@
     }
 
     updateYUV(ctx, list, false /*secure only*/);
+    /* mark secure RGB layers for MDP comp */
+    updateSecureRGB(ctx, list);
     bool ret = markLayersForCaching(ctx, list); //sets up fbZ also
     if(!ret) {
         ALOGD_IF(isDebug(),"%s: batching failed, dpy %d",__FUNCTION__, mDpy);
@@ -1038,7 +1061,7 @@
 
     int mdpCount = mCurrentFrame.mdpCount;
 
-    if(sEnable4k2kYUVSplit){
+    if(sEnableYUVsplit){
         adjustForSourceSplit(ctx, list);
     }
 
@@ -1204,7 +1227,7 @@
     if(mCurrentFrame.fbCount)
         mCurrentFrame.fbZ = mCurrentFrame.mdpCount;
 
-    if(sEnable4k2kYUVSplit){
+    if(sEnableYUVsplit){
         adjustForSourceSplit(ctx, list);
     }
 
@@ -1219,6 +1242,64 @@
     return true;
 }
 
+/* if tryFullFrame fails, try to push all video and secure RGB layers to MDP */
+bool MDPComp::tryMDPOnlyLayers(hwc_context_t *ctx,
+        hwc_display_contents_1_t* list) {
+    const bool secureOnly = true;
+    return mdpOnlyLayersComp(ctx, list, not secureOnly) or
+            mdpOnlyLayersComp(ctx, list, secureOnly);
+
+}
+
+bool MDPComp::mdpOnlyLayersComp(hwc_context_t *ctx,
+        hwc_display_contents_1_t* list, bool secureOnly) {
+
+    if(sSimulationFlags & MDPCOMP_AVOID_MDP_ONLY_LAYERS)
+        return false;
+
+    /* Bail out if we are processing only secured video layers
+     * and we dont have any */
+    if(!isSecurePresent(ctx, mDpy) && secureOnly){
+        reset(ctx);
+        return false;
+    }
+
+    int numAppLayers = ctx->listStats[mDpy].numAppLayers;
+    mCurrentFrame.reset(numAppLayers);
+    mCurrentFrame.fbCount -= mCurrentFrame.dropCount;
+
+    updateYUV(ctx, list, secureOnly);
+    /* mark secure RGB layers for MDP comp */
+    updateSecureRGB(ctx, list);
+
+    if(mCurrentFrame.mdpCount == 0) {
+        reset(ctx);
+        return false;
+    }
+
+    /* find the maximum batch of layers to be marked for framebuffer */
+    bool ret = markLayersForCaching(ctx, list); //sets up fbZ also
+    if(!ret) {
+        ALOGD_IF(isDebug(),"%s: batching failed, dpy %d",__FUNCTION__, mDpy);
+        reset(ctx);
+        return false;
+    }
+
+    if(sEnableYUVsplit){
+        adjustForSourceSplit(ctx, list);
+    }
+
+    if(!postHeuristicsHandling(ctx, list)) {
+        ALOGD_IF(isDebug(), "post heuristic handling failed");
+        reset(ctx);
+        return false;
+    }
+
+    ALOGD_IF(sSimulationFlags,"%s: MDP_ONLY_LAYERS_COMP SUCCEEDED",
+             __FUNCTION__);
+    return true;
+}
+
 /* Checks for conditions where YUV layers cannot be bypassed */
 bool MDPComp::isYUVDoable(hwc_context_t* ctx, hwc_layer_1_t* layer) {
     if(isSkipLayer(layer)) {
@@ -1252,6 +1333,27 @@
     return true;
 }
 
+/* Checks for conditions where Secure RGB layers cannot be bypassed */
+bool MDPComp::isSecureRGBDoable(hwc_context_t* ctx, hwc_layer_1_t* layer) {
+    if(isSkipLayer(layer)) {
+        ALOGD_IF(isDebug(), "%s: Secure RGB layer marked SKIP dpy %d",
+            __FUNCTION__, mDpy);
+        return false;
+    }
+
+    if(isSecuring(ctx, layer)) {
+        ALOGD_IF(isDebug(), "%s: MDP securing is active", __FUNCTION__);
+        return false;
+    }
+
+    if(not isSupportedForMDPComp(ctx, layer)) {
+        ALOGD_IF(isDebug(), "%s: Unsupported secure RGB layer",
+            __FUNCTION__);
+        return false;
+    }
+    return true;
+}
+
 /* starts at fromIndex and check for each layer to find
  * if it it has overlapping with any Updating layer above it in zorder
  * till the end of the batch. returns true if it finds any intersection */
@@ -1469,6 +1571,32 @@
              mCurrentFrame.fbCount);
 }
 
+void MDPComp::updateSecureRGB(hwc_context_t* ctx,
+    hwc_display_contents_1_t* list) {
+    int nSecureRGBCount = ctx->listStats[mDpy].secureRGBCount;
+    for(int index = 0;index < nSecureRGBCount; index++){
+        int nSecureRGBIndex = ctx->listStats[mDpy].secureRGBIndices[index];
+        hwc_layer_1_t* layer = &list->hwLayers[nSecureRGBIndex];
+
+        if(!isSecureRGBDoable(ctx, layer)) {
+            if(!mCurrentFrame.isFBComposed[nSecureRGBIndex]) {
+                mCurrentFrame.isFBComposed[nSecureRGBIndex] = true;
+                mCurrentFrame.fbCount++;
+            }
+        } else {
+            if(mCurrentFrame.isFBComposed[nSecureRGBIndex]) {
+                mCurrentFrame.isFBComposed[nSecureRGBIndex] = false;
+                mCurrentFrame.fbCount--;
+            }
+        }
+    }
+
+    mCurrentFrame.mdpCount = mCurrentFrame.layerCount -
+            mCurrentFrame.fbCount - mCurrentFrame.dropCount;
+    ALOGD_IF(isDebug(),"%s: fb count: %d",__FUNCTION__,
+             mCurrentFrame.fbCount);
+}
+
 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};
@@ -1532,7 +1660,7 @@
             cur_pipe->zOrder = mdpNextZOrder++;
 
             private_handle_t *hnd = (private_handle_t *)layer->handle;
-            if(is4kx2kYuvBuffer(hnd) && sEnable4k2kYUVSplit){
+            if(isYUVSplitNeeded(hnd) && sEnableYUVsplit){
                 if(configure4k2kYuv(ctx, layer,
                             mCurrentFrame.mdpToLayer[mdpIndex])
                         != 0 ){
@@ -1690,7 +1818,10 @@
     if(isFrameDoable(ctx)) {
         generateROI(ctx, list);
 
-        mModeOn = tryFullFrame(ctx, list) || tryVideoOnly(ctx, list);
+        // if tryFullFrame fails, try to push all video and secure RGB layers
+        // to MDP for composition.
+        mModeOn = tryFullFrame(ctx, list) || tryMDPOnlyLayers(ctx, list) ||
+                  tryVideoOnly(ctx, list);
         if(mModeOn) {
             setMDPCompLayerFlags(ctx, list);
         } else {
@@ -1782,7 +1913,7 @@
                 mdpNextZOrder++;
                 hwc_layer_1_t* layer = &list->hwLayers[index];
                 private_handle_t *hnd = (private_handle_t *)layer->handle;
-                if(is4kx2kYuvBuffer(hnd)) {
+                if(isYUVSplitNeeded(hnd)) {
                     if(mdpNextZOrder <= mCurrentFrame.fbZ)
                         mCurrentFrame.fbZ += 1;
                     mdpNextZOrder++;
@@ -1804,13 +1935,12 @@
         *(static_cast<MdpPipeInfoNonSplit*>(PipeLayerPair.pipeInfo));
     eMdpFlags mdpFlags = OV_MDP_BACKEND_COMPOSITION;
     eZorder zOrder = static_cast<eZorder>(mdp_info.zOrder);
-    eIsFg isFg = IS_FG_OFF;
     eDest dest = mdp_info.index;
 
     ALOGD_IF(isDebug(),"%s: configuring: layer: %p z_order: %d dest_pipe: %d",
              __FUNCTION__, layer, zOrder, dest);
 
-    return configureNonSplit(ctx, layer, mDpy, mdpFlags, zOrder, isFg, dest,
+    return configureNonSplit(ctx, layer, mDpy, mdpFlags, zOrder, dest,
                            &PipeLayerPair.rot);
 }
 
@@ -1822,7 +1952,7 @@
 
         hwc_layer_1_t* layer = &list->hwLayers[index];
         private_handle_t *hnd = (private_handle_t *)layer->handle;
-        if(is4kx2kYuvBuffer(hnd) && sEnable4k2kYUVSplit){
+        if(isYUVSplitNeeded(hnd) && sEnableYUVsplit){
             if(allocSplitVGPipesfor4k2k(ctx, index)){
                 continue;
             }
@@ -1859,12 +1989,11 @@
     MdpYUVPipeInfo& mdp_info =
             *(static_cast<MdpYUVPipeInfo*>(PipeLayerPair.pipeInfo));
     eZorder zOrder = static_cast<eZorder>(mdp_info.zOrder);
-    eIsFg isFg = IS_FG_OFF;
     eMdpFlags mdpFlagsL = OV_MDP_BACKEND_COMPOSITION;
     eDest lDest = mdp_info.lIndex;
     eDest rDest = mdp_info.rIndex;
 
-    return configureSourceSplit(ctx, layer, mDpy, mdpFlagsL, zOrder, isFg,
+    return configureSourceSplit(ctx, layer, mDpy, mdpFlagsL, zOrder,
             lDest, rDest, &PipeLayerPair.rot);
 }
 
@@ -1902,7 +2031,7 @@
 
         int mdpIndex = mCurrentFrame.layerToMDP[i];
 
-        if(is4kx2kYuvBuffer(hnd) && sEnable4k2kYUVSplit)
+        if(isYUVSplitNeeded(hnd) && sEnableYUVsplit)
         {
             MdpYUVPipeInfo& pipe_info =
                 *(MdpYUVPipeInfo*)mCurrentFrame.mdpToLayer[mdpIndex].pipeInfo;
@@ -2001,7 +2130,7 @@
                 mdpNextZOrder++;
                 hwc_layer_1_t* layer = &list->hwLayers[index];
                 private_handle_t *hnd = (private_handle_t *)layer->handle;
-                if(is4kx2kYuvBuffer(hnd)) {
+                if(isYUVSplitNeeded(hnd)) {
                     hwc_rect_t dst = layer->displayFrame;
                     if((dst.left > lSplit) || (dst.right < lSplit)) {
                         mCurrentFrame.mdpCount += 1;
@@ -2062,7 +2191,7 @@
         private_handle_t *hnd = (private_handle_t *)layer->handle;
         hwc_rect_t dst = layer->displayFrame;
         const int lSplit = getLeftSplit(ctx, mDpy);
-        if(is4kx2kYuvBuffer(hnd) && sEnable4k2kYUVSplit){
+        if(isYUVSplitNeeded(hnd) && sEnableYUVsplit){
             if((dst.left > lSplit)||(dst.right < lSplit)){
                 if(allocSplitVGPipesfor4k2k(ctx, index)){
                     continue;
@@ -2092,12 +2221,11 @@
         MdpYUVPipeInfo& mdp_info =
                 *(static_cast<MdpYUVPipeInfo*>(PipeLayerPair.pipeInfo));
         eZorder zOrder = static_cast<eZorder>(mdp_info.zOrder);
-        eIsFg isFg = IS_FG_OFF;
         eMdpFlags mdpFlagsL = OV_MDP_BACKEND_COMPOSITION;
         eDest lDest = mdp_info.lIndex;
         eDest rDest = mdp_info.rIndex;
 
-        return configureSourceSplit(ctx, layer, mDpy, mdpFlagsL, zOrder, isFg,
+        return configureSourceSplit(ctx, layer, mDpy, mdpFlagsL, zOrder,
                 lDest, rDest, &PipeLayerPair.rot);
     }
     else{
@@ -2113,7 +2241,6 @@
     MdpPipeInfoSplit& mdp_info =
         *(static_cast<MdpPipeInfoSplit*>(PipeLayerPair.pipeInfo));
     eZorder zOrder = static_cast<eZorder>(mdp_info.zOrder);
-    eIsFg isFg = IS_FG_OFF;
     eMdpFlags mdpFlagsL = OV_MDP_BACKEND_COMPOSITION;
     eDest lDest = mdp_info.lIndex;
     eDest rDest = mdp_info.rIndex;
@@ -2121,7 +2248,7 @@
     ALOGD_IF(isDebug(),"%s: configuring: layer: %p z_order: %d dest_pipeL: %d"
              "dest_pipeR: %d",__FUNCTION__, layer, zOrder, lDest, rDest);
 
-    return configureSplit(ctx, layer, mDpy, mdpFlagsL, zOrder, isFg, lDest,
+    return configureSplit(ctx, layer, mDpy, mdpFlagsL, zOrder, lDest,
                             rDest, &PipeLayerPair.rot);
 }
 
@@ -2158,7 +2285,7 @@
 
         int mdpIndex = mCurrentFrame.layerToMDP[i];
 
-        if(is4kx2kYuvBuffer(hnd) && sEnable4k2kYUVSplit)
+        if(isYUVSplitNeeded(hnd) && sEnableYUVsplit)
         {
             MdpYUVPipeInfo& pipe_info =
                 *(MdpYUVPipeInfo*)mCurrentFrame.mdpToLayer[mdpIndex].pipeInfo;
@@ -2294,6 +2421,10 @@
     int dstWidth = dst.right - dst.left;
     int cropWidth = crop.right - crop.left;
 
+    //TODO Even if a 4k video is going to be rot-downscaled to dimensions under
+    //pipe line length, we are still using 2 pipes. This is fine just because
+    //this is source split where destination doesn't matter. Evaluate later to
+    //see if going through all the calcs to save a pipe is worth it
     if(dstWidth > mdpHw.getMaxMixerWidth() or
             cropWidth > mdpHw.getMaxMixerWidth() or
             (primarySplitAlways and (cropWidth > lSplit))) {
@@ -2327,14 +2458,12 @@
         *(static_cast<MdpPipeInfoSplit*>(PipeLayerPair.pipeInfo));
     Rotator **rot = &PipeLayerPair.rot;
     eZorder z = static_cast<eZorder>(mdp_info.zOrder);
-    eIsFg isFg = IS_FG_OFF;
     eDest lDest = mdp_info.lIndex;
     eDest rDest = mdp_info.rIndex;
     hwc_rect_t crop = integerizeSourceCrop(layer->sourceCropf);
     hwc_rect_t dst = layer->displayFrame;
     int transform = layer->transform;
     eTransform orient = static_cast<eTransform>(transform);
-    const int downscale = 0;
     int rotFlags = ROT_FLAGS_NONE;
     uint32_t format = ovutils::getMdpFormat(hnd->format, isTileRendered(hnd));
     Whf whf(getWidth(hnd), getHeight(hnd), format, hnd->size);
@@ -2350,21 +2479,22 @@
             whf.format = getMdpFormat(HAL_PIXEL_FORMAT_BGRX_8888);
     }
 
+    int downscale = getRotDownscale(ctx, layer);
     eMdpFlags mdpFlags = OV_MDP_BACKEND_COMPOSITION;
-    setMdpFlags(ctx, layer, mdpFlags, 0, transform);
+    setMdpFlags(ctx, layer, mdpFlags, downscale, transform);
 
     if(lDest != OV_INVALID && rDest != OV_INVALID) {
         //Enable overfetch
         setMdpFlags(mdpFlags, OV_MDSS_MDP_DUAL_PIPE);
     }
 
-    if(has90Transform(layer) && isRotationDoable(ctx, hnd)) {
+    if((has90Transform(layer) or downscale) and isRotationDoable(ctx, hnd)) {
         (*rot) = ctx->mRotMgr->getNext();
         if((*rot) == NULL) return -1;
         ctx->mLayerRotMap[mDpy]->add(layer, *rot);
         //If the video is using a single pipe, enable BWC
         if(rDest == OV_INVALID) {
-            BwcPM::setBwc(crop, dst, transform, mdpFlags);
+            BwcPM::setBwc(crop, dst, transform, downscale, mdpFlags);
         }
         //Configure rotator for pre-rotation
         if(configRotator(*rot, whf, crop, mdpFlags, orient, downscale) < 0) {
@@ -2372,7 +2502,7 @@
             return -1;
         }
         updateSource(orient, whf, crop, *rot);
-        rotFlags |= ROT_PREROTATED;
+        rotFlags |= ovutils::ROT_PREROTATED;
     }
 
     //If 2 pipes being used, divide layer into half, crop and dst
@@ -2385,14 +2515,24 @@
         cropR.left = cropL.right;
         sanitizeSourceCrop(cropL, cropR, hnd);
 
+        bool cropSwap = false;
         //Swap crops on H flip since 2 pipes are being used
         if((orient & OVERLAY_TRANSFORM_FLIP_H) && (*rot) == NULL) {
             hwc_rect_t tmp = cropL;
             cropL = cropR;
             cropR = tmp;
+            cropSwap = true;
         }
 
-        dstL.right = (dst.right + dst.left) / 2;
+        //cropSwap trick: If the src and dst widths are both odd, let us say
+        //2507, then splitting both into half would cause left width to be 1253
+        //and right 1254. If crop is swapped because of H flip, this will cause
+        //left crop width to be 1254, whereas left dst width remains 1253, thus
+        //inducing a scaling that is unaccounted for. To overcome that we add 1
+        //to the dst width if there is a cropSwap. So if the original width was
+        //2507, the left dst width will be 1254. Even if the original width was
+        //even for ex: 2508, the left dst width will still remain 1254.
+        dstL.right = (dst.right + dst.left + cropSwap) / 2;
         dstR.left = dstL.right;
     }
 
@@ -2402,7 +2542,7 @@
 
     //configure left pipe
     if(lDest != OV_INVALID) {
-        PipeArgs pargL(mdpFlags, whf, z, isFg,
+        PipeArgs pargL(mdpFlags, whf, z,
                 static_cast<eRotFlags>(rotFlags), layer->planeAlpha,
                 (ovutils::eBlending) getBlending(layer->blending));
 
@@ -2415,7 +2555,7 @@
 
     //configure right pipe
     if(rDest != OV_INVALID) {
-        PipeArgs pargR(mdpFlags, whf, z, isFg,
+        PipeArgs pargR(mdpFlags, whf, z,
                 static_cast<eRotFlags>(rotFlags),
                 layer->planeAlpha,
                 (ovutils::eBlending) getBlending(layer->blending));
diff --git a/libhwcomposer/hwc_mdpcomp.h b/libhwcomposer/hwc_mdpcomp.h
index 1ebe0bd..8c833c2 100644
--- a/libhwcomposer/hwc_mdpcomp.h
+++ b/libhwcomposer/hwc_mdpcomp.h
@@ -74,6 +74,7 @@
         MDPCOMP_AVOID_CACHE_MDP = 0x002,
         MDPCOMP_AVOID_LOAD_MDP = 0x004,
         MDPCOMP_AVOID_VIDEO_ONLY = 0x008,
+        MDPCOMP_AVOID_MDP_ONLY_LAYERS = 0x010,
     };
 
     /* mdp pipe data */
@@ -190,8 +191,14 @@
     bool tryVideoOnly(hwc_context_t *ctx, hwc_display_contents_1_t* list);
     bool videoOnlyComp(hwc_context_t *ctx, hwc_display_contents_1_t* list,
             bool secureOnly);
+    /* checks for conditions where only secure RGB and video can be bypassed */
+    bool tryMDPOnlyLayers(hwc_context_t *ctx, hwc_display_contents_1_t* list);
+    bool mdpOnlyLayersComp(hwc_context_t *ctx, hwc_display_contents_1_t* list,
+            bool secureOnly);
     /* checks for conditions where YUV layers cannot be bypassed */
     bool isYUVDoable(hwc_context_t* ctx, hwc_layer_1_t* layer);
+    /* checks for conditions where Secure RGB layers cannot be bypassed */
+    bool isSecureRGBDoable(hwc_context_t* ctx, hwc_layer_1_t* layer);
     /* 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);
@@ -217,6 +224,9 @@
         /* updates cache map with YUV info */
     void updateYUV(hwc_context_t* ctx, hwc_display_contents_1_t* list,
             bool secureOnly);
+    /* updates cache map with secure RGB info */
+    void updateSecureRGB(hwc_context_t* ctx,
+            hwc_display_contents_1_t* list);
     /* Validates if the GPU/MDP layer split chosen by a strategy is supported
      * by MDP.
      * Sets up MDP comp data structures to reflect covnversion from layers to
@@ -248,7 +258,7 @@
     struct FrameInfo mCurrentFrame;
     struct LayerCache mCachedFrame;
     //Enable 4kx2k yuv layer split
-    static bool sEnable4k2kYUVSplit;
+    static bool sEnableYUVsplit;
     bool mModeOn; // if prepare happened
     bool allocSplitVGPipesfor4k2k(hwc_context_t *ctx, int index);
 };
diff --git a/libhwcomposer/hwc_uevents.cpp b/libhwcomposer/hwc_uevents.cpp
index fed6f3c..94191b7 100644
--- a/libhwcomposer/hwc_uevents.cpp
+++ b/libhwcomposer/hwc_uevents.cpp
@@ -280,7 +280,7 @@
             if(dpy == HWC_DISPLAY_EXTERNAL ||
                ctx->mVirtualonExtActive) {
                 /* External display is HDMI or non-hybrid WFD solution */
-                ALOGE_IF(UEVENT_DEBUG, "%s: Sending EXTERNAL_OFFLINE ONLINE"
+                ALOGE_IF(UEVENT_DEBUG, "%s: Sending EXTERNAL ONLINE"
                          "hotplug event", __FUNCTION__);
                 ctx->proc->hotplug(ctx->proc,HWC_DISPLAY_EXTERNAL,
                                    EXTERNAL_ONLINE);
diff --git a/libhwcomposer/hwc_utils.cpp b/libhwcomposer/hwc_utils.cpp
index 5cecbaa..e57e573 100644
--- a/libhwcomposer/hwc_utils.cpp
+++ b/libhwcomposer/hwc_utils.cpp
@@ -835,12 +835,12 @@
     ctx->listStats[dpy].isSecurePresent = false;
     ctx->listStats[dpy].yuvCount = 0;
     char property[PROPERTY_VALUE_MAX];
-    ctx->listStats[dpy].extOnlyLayerIndex = -1;
     ctx->listStats[dpy].isDisplayAnimating = false;
     ctx->listStats[dpy].secureUI = false;
     ctx->listStats[dpy].yuv4k2kCount = 0;
     ctx->dpyAttr[dpy].mActionSafePresent = isActionSafePresent(ctx, dpy);
     ctx->listStats[dpy].renderBufIndexforABC = -1;
+    ctx->listStats[dpy].secureRGBCount = 0;
 
     resetROI(ctx, dpy);
 
@@ -868,6 +868,12 @@
 
         if (isSecureBuffer(hnd)) {
             ctx->listStats[dpy].isSecurePresent = true;
+            if(not isYuvBuffer(hnd)) {
+                // cache secureRGB layer parameters like we cache for YUV layers
+                int& secureRGBCount = ctx->listStats[dpy].secureRGBCount;
+                ctx->listStats[dpy].secureRGBIndices[secureRGBCount] = (int)i;
+                secureRGBCount++;
+            }
         }
 
         if (isSkipLayer(&list->hwLayers[i])) {
@@ -879,7 +885,7 @@
             ctx->listStats[dpy].yuvIndices[yuvCount] = (int)i;
             yuvCount++;
 
-            if(UNLIKELY(is4kx2kYuvBuffer(hnd))){
+            if(UNLIKELY(isYUVSplitNeeded(hnd))){
                 int& yuv4k2kCount = ctx->listStats[dpy].yuv4k2kCount;
                 ctx->listStats[dpy].yuv4k2kIndices[yuv4k2kCount] = (int)i;
                 yuv4k2kCount++;
@@ -888,10 +894,6 @@
         if(layer->blending == HWC_BLENDING_PREMULT)
             ctx->listStats[dpy].preMultipliedAlpha = true;
 
-
-        if(UNLIKELY(isExtOnly(hnd))){
-            ctx->listStats[dpy].extOnlyLayerIndex = (int)i;
-        }
     }
     if(ctx->listStats[dpy].yuvCount > 0) {
         if (property_get("hw.cabl.yuv", property, NULL) > 0) {
@@ -1324,7 +1326,9 @@
             rotData.acq_fen_fd_cnt = 1; //1 ioctl call per rot session
         }
         int ret = 0;
-        ret = ioctl(rotFd, MSMFB_BUFFER_SYNC, &rotData);
+        if(not ctx->mLayerRotMap[dpy]->isRotCached(i))
+            ret = ioctl(rotFd, MSMFB_BUFFER_SYNC, &rotData);
+
         if(ret < 0) {
             ALOGE("%s: ioctl MSMFB_BUFFER_SYNC failed for rot sync, err=%s",
                     __FUNCTION__, strerror(errno));
@@ -1475,22 +1479,20 @@
                 ovutils::OV_MDP_BLEND_FG_PREMULT);
     }
 
-    if(isYuvBuffer(hnd)) {
-        if(isSecureBuffer(hnd)) {
-            ovutils::setMdpFlags(mdpFlags,
-                    ovutils::OV_MDP_SECURE_OVERLAY_SESSION);
-        }
-        if(metadata && (metadata->operation & PP_PARAM_INTERLACED) &&
-                metadata->interlaced) {
-            ovutils::setMdpFlags(mdpFlags,
-                    ovutils::OV_MDP_DEINTERLACE);
-        }
+    if(metadata && (metadata->operation & PP_PARAM_INTERLACED) &&
+            metadata->interlaced) {
+        ovutils::setMdpFlags(mdpFlags,
+                ovutils::OV_MDP_DEINTERLACE);
+    }
+
+    // Mark MDP flags with SECURE_OVERLAY_SESSION for driver
+    if(isSecureBuffer(hnd)) {
+        ovutils::setMdpFlags(mdpFlags,
+                ovutils::OV_MDP_SECURE_OVERLAY_SESSION);
     }
 
     if(isSecureDisplayBuffer(hnd)) {
-        // Secure display needs both SECURE_OVERLAY and SECURE_DISPLAY_OV
-        ovutils::setMdpFlags(mdpFlags,
-                             ovutils::OV_MDP_SECURE_OVERLAY_SESSION);
+        // Mark MDP flags with SECURE_DISPLAY_OVERLAY_SESSION for driver
         ovutils::setMdpFlags(mdpFlags,
                              ovutils::OV_MDP_SECURE_DISPLAY_OVERLAY_SESSION);
     }
@@ -1574,7 +1576,7 @@
 
 int configColorLayer(hwc_context_t *ctx, hwc_layer_1_t *layer,
         const int& dpy, eMdpFlags& mdpFlags, eZorder& z,
-        eIsFg& isFg, const eDest& dest) {
+        const eDest& dest) {
 
     hwc_rect_t dst = layer->displayFrame;
     trimLayer(ctx, dpy, 0, dst, dst);
@@ -1590,7 +1592,7 @@
     if (layer->blending == HWC_BLENDING_PREMULT)
         ovutils::setMdpFlags(mdpFlags, ovutils::OV_MDP_BLEND_FG_PREMULT);
 
-    PipeArgs parg(mdpFlags, whf, z, isFg, static_cast<eRotFlags>(0),
+    PipeArgs parg(mdpFlags, whf, z, static_cast<eRotFlags>(0),
                   layer->planeAlpha,
                   (ovutils::eBlending) getBlending(layer->blending));
 
@@ -1633,16 +1635,57 @@
     crop.bottom = transformedCrop.y + transformedCrop.h;
 }
 
+int getRotDownscale(hwc_context_t *ctx, const hwc_layer_1_t *layer) {
+    if(not qdutils::MDPVersion::getInstance().isRotDownscaleEnabled()) {
+        return 0;
+    }
+
+    int downscale = 0;
+    hwc_rect_t crop = integerizeSourceCrop(layer->sourceCropf);
+    hwc_rect_t dst = layer->displayFrame;
+    private_handle_t *hnd = (private_handle_t *)layer->handle;
+
+    if(not hnd) {
+        return 0;
+    }
+
+    MetaData_t *metadata = (MetaData_t *)hnd->base_metadata;
+    bool isInterlaced = metadata && (metadata->operation & PP_PARAM_INTERLACED)
+                && metadata->interlaced;
+    int transform = layer->transform;
+    uint32_t format = ovutils::getMdpFormat(hnd->format, isTileRendered(hnd));
+
+    if(isYuvBuffer(hnd)) {
+        if(ctx->mMDP.version >= qdutils::MDP_V4_2 &&
+                ctx->mMDP.version < qdutils::MDSS_V5) {
+            downscale = Rotator::getDownscaleFactor(crop.right - crop.left,
+                    crop.bottom - crop.top, dst.right - dst.left,
+                    dst.bottom - dst.top, format, isInterlaced);
+        } else {
+            Dim adjCrop(crop.left, crop.top, crop.right - crop.left,
+                    crop.bottom - crop.top);
+            Dim pos(dst.left, dst.top, dst.right - dst.left,
+                    dst.bottom - dst.top);
+            if(transform & HAL_TRANSFORM_ROT_90) {
+                swap(adjCrop.w, adjCrop.h);
+            }
+            downscale = Rotator::getDownscaleFactor(adjCrop.w, adjCrop.h, pos.w,
+                    pos.h, format, isInterlaced);
+        }
+    }
+    return downscale;
+}
+
 int configureNonSplit(hwc_context_t *ctx, hwc_layer_1_t *layer,
         const int& dpy, eMdpFlags& mdpFlags, eZorder& z,
-        eIsFg& isFg, const eDest& dest, Rotator **rot) {
+        const eDest& dest, Rotator **rot) {
 
     private_handle_t *hnd = (private_handle_t *)layer->handle;
 
     if(!hnd) {
         if (layer->flags & HWC_COLOR_FILL) {
             // Configure Color layer
-            return configColorLayer(ctx, layer, dpy, mdpFlags, z, isFg, dest);
+            return configColorLayer(ctx, layer, dpy, mdpFlags, z, dest);
         }
         ALOGE("%s: layer handle is NULL", __FUNCTION__);
         return -1;
@@ -1654,7 +1697,6 @@
     hwc_rect_t dst = layer->displayFrame;
     int transform = layer->transform;
     eTransform orient = static_cast<eTransform>(transform);
-    int downscale = 0;
     int rotFlags = ovutils::ROT_FLAGS_NONE;
     uint32_t format = ovutils::getMdpFormat(hnd->format, isTileRendered(hnd));
     Whf whf(getWidth(hnd), getHeight(hnd), format, (uint32_t)hnd->size);
@@ -1668,42 +1710,30 @@
     }
 
     calcExtDisplayPosition(ctx, hnd, dpy, crop, dst, transform, orient);
-
-    if(isYuvBuffer(hnd) && ctx->mMDP.version >= qdutils::MDP_V4_2 &&
-       ctx->mMDP.version < qdutils::MDSS_V5) {
-        downscale =  getDownscaleFactor(
-            crop.right - crop.left,
-            crop.bottom - crop.top,
-            dst.right - dst.left,
-            dst.bottom - dst.top);
-        if(downscale) {
-            rotFlags = ROT_DOWNSCALE_ENABLED;
-        }
-    }
-
+    int downscale = getRotDownscale(ctx, layer);
     setMdpFlags(ctx, layer, mdpFlags, downscale, transform);
 
     //if 90 component or downscale, use rot
-    if((has90Transform(layer) && isRotationDoable(ctx, hnd)) || downscale) {
+    if((has90Transform(layer) or downscale) and isRotationDoable(ctx, hnd)) {
         *rot = ctx->mRotMgr->getNext();
         if(*rot == NULL) return -1;
         ctx->mLayerRotMap[dpy]->add(layer, *rot);
         // BWC is not tested for other formats So enable it only for YUV format
         if(!dpy && isYuvBuffer(hnd))
-            BwcPM::setBwc(crop, dst, transform, mdpFlags);
+            BwcPM::setBwc(crop, dst, transform, downscale, mdpFlags);
         //Configure rotator for pre-rotation
         if(configRotator(*rot, whf, crop, mdpFlags, orient, downscale) < 0) {
             ALOGE("%s: configRotator failed!", __FUNCTION__);
             return -1;
         }
         updateSource(orient, whf, crop, *rot);
-        rotFlags |= ovutils::ROT_PREROTATED;
+        rotFlags |= ROT_PREROTATED;
     }
 
     //For the mdp, since either we are pre-rotating or MDP does flips
     orient = OVERLAY_TRANSFORM_0;
     transform = 0;
-    PipeArgs parg(mdpFlags, whf, z, isFg,
+    PipeArgs parg(mdpFlags, whf, z,
                   static_cast<eRotFlags>(rotFlags), layer->planeAlpha,
                   (ovutils::eBlending) getBlending(layer->blending));
 
@@ -1745,7 +1775,7 @@
 
 int configureSplit(hwc_context_t *ctx, hwc_layer_1_t *layer,
         const int& dpy, eMdpFlags& mdpFlagsL, eZorder& z,
-        eIsFg& isFg, const eDest& lDest, const eDest& rDest,
+        const eDest& lDest, const eDest& rDest,
         Rotator **rot) {
     private_handle_t *hnd = (private_handle_t *)layer->handle;
     if(!hnd) {
@@ -1761,7 +1791,6 @@
     hwc_rect_t dst = layer->displayFrame;
     int transform = layer->transform;
     eTransform orient = static_cast<eTransform>(transform);
-    const int downscale = 0;
     int rotFlags = ROT_FLAGS_NONE;
     uint32_t format = ovutils::getMdpFormat(hnd->format, isTileRendered(hnd));
     Whf whf(getWidth(hnd), getHeight(hnd), format, (uint32_t)hnd->size);
@@ -1777,8 +1806,8 @@
     /* Calculate the external display position based on MDP downscale,
        ActionSafe, and extorientation features. */
     calcExtDisplayPosition(ctx, hnd, dpy, crop, dst, transform, orient);
-
-    setMdpFlags(ctx, layer, mdpFlagsL, 0, transform);
+    int downscale = getRotDownscale(ctx, layer);
+    setMdpFlags(ctx, layer, mdpFlagsL, downscale, transform);
 
     if(lDest != OV_INVALID && rDest != OV_INVALID) {
         //Enable overfetch
@@ -1792,7 +1821,7 @@
         whf.format = wb->getOutputFormat();
     }
 
-    if(has90Transform(layer) && isRotationDoable(ctx, hnd)) {
+    if((has90Transform(layer) or downscale) and isRotationDoable(ctx, hnd)) {
         (*rot) = ctx->mRotMgr->getNext();
         if((*rot) == NULL) return -1;
         ctx->mLayerRotMap[dpy]->add(layer, *rot);
@@ -1860,7 +1889,7 @@
 
     //configure left mixer
     if(lDest != OV_INVALID) {
-        PipeArgs pargL(mdpFlagsL, whf, z, isFg,
+        PipeArgs pargL(mdpFlagsL, whf, z,
                        static_cast<eRotFlags>(rotFlags), layer->planeAlpha,
                        (ovutils::eBlending) getBlending(layer->blending));
 
@@ -1873,7 +1902,7 @@
 
     //configure right mixer
     if(rDest != OV_INVALID) {
-        PipeArgs pargR(mdpFlagsR, whf, z, isFg,
+        PipeArgs pargR(mdpFlagsR, whf, z,
                        static_cast<eRotFlags>(rotFlags),
                        layer->planeAlpha,
                        (ovutils::eBlending) getBlending(layer->blending));
@@ -1891,7 +1920,7 @@
 
 int configureSourceSplit(hwc_context_t *ctx, hwc_layer_1_t *layer,
         const int& dpy, eMdpFlags& mdpFlagsL, eZorder& z,
-        eIsFg& isFg, const eDest& lDest, const eDest& rDest,
+        const eDest& lDest, const eDest& rDest,
         Rotator **rot) {
     private_handle_t *hnd = (private_handle_t *)layer->handle;
     if(!hnd) {
@@ -1928,7 +1957,7 @@
         ctx->mLayerRotMap[dpy]->add(layer, *rot);
         // BWC is not tested for other formats So enable it only for YUV format
         if(!dpy && isYuvBuffer(hnd))
-            BwcPM::setBwc(crop, dst, transform, mdpFlagsL);
+            BwcPM::setBwc(crop, dst, transform, downscale, mdpFlagsL);
         //Configure rotator for pre-rotation
         if(configRotator(*rot, whf, crop, mdpFlagsL, orient, downscale) < 0) {
             ALOGE("%s: configRotator failed!", __FUNCTION__);
@@ -1986,7 +2015,7 @@
 
     //configure left half
     if(lDest != OV_INVALID) {
-        PipeArgs pargL(mdpFlagsL, whf, lz, isFg,
+        PipeArgs pargL(mdpFlagsL, whf, lz,
                 static_cast<eRotFlags>(rotFlags), layer->planeAlpha,
                 (ovutils::eBlending) getBlending(layer->blending));
 
@@ -1999,7 +2028,7 @@
 
     //configure right half
     if(rDest != OV_INVALID) {
-        PipeArgs pargR(mdpFlagsR, whf, rz, isFg,
+        PipeArgs pargR(mdpFlagsR, whf, rz,
                 static_cast<eRotFlags>(rotFlags),
                 layer->planeAlpha,
                 (ovutils::eBlending) getBlending(layer->blending));
@@ -2175,9 +2204,12 @@
     return (eqBounds == 3);
 }
 
-void BwcPM::setBwc(const hwc_rect_t& crop,
-            const hwc_rect_t& dst, const int& transform,
-            ovutils::eMdpFlags& mdpFlags) {
+void BwcPM::setBwc(const hwc_rect_t& crop, const hwc_rect_t& dst,
+        const int& transform,const int& downscale,
+        ovutils::eMdpFlags& mdpFlags) {
+    //BWC not supported with rot-downscale
+    if(downscale) return;
+
     //Target doesnt support Bwc
     qdutils::MDPVersion& mdpHw = qdutils::MDPVersion::getInstance();
     if(!mdpHw.supportsBWC()) {
@@ -2211,14 +2243,14 @@
 }
 
 void LayerRotMap::add(hwc_layer_1_t* layer, Rotator *rot) {
-    if(mCount >= MAX_SESS) return;
+    if(mCount >= RotMgr::MAX_ROT_SESS) return;
     mLayer[mCount] = layer;
     mRot[mCount] = rot;
     mCount++;
 }
 
 void LayerRotMap::reset() {
-    for (int i = 0; i < MAX_SESS; i++) {
+    for (int i = 0; i < RotMgr::MAX_ROT_SESS; i++) {
         mLayer[i] = 0;
         mRot[i] = 0;
     }
@@ -2230,9 +2262,27 @@
     reset();
 }
 
+bool LayerRotMap::isRotCached(uint32_t index) const {
+    overlay::Rotator* rot = getRot(index);
+    hwc_layer_1_t* layer =  getLayer(index);
+
+    if(rot and layer and layer->handle) {
+        private_handle_t *hnd = (private_handle_t *)(layer->handle);
+        return (rot->isRotCached(hnd->fd,(uint32_t)(hnd->offset)));
+    }
+    return false;
+}
+
 void LayerRotMap::setReleaseFd(const int& fence) {
     for(uint32_t i = 0; i < mCount; i++) {
-        mRot[i]->setReleaseFd(dup(fence));
+        if(mRot[i] and mLayer[i] and mLayer[i]->handle) {
+            /* Ensure that none of the above (Rotator-instance,
+             * layer and layer-handle) are NULL*/
+            if(isRotCached(i))
+                mRot[i]->setPrevBufReleaseFd(dup(fence));
+            else
+                mRot[i]->setCurrBufReleaseFd(dup(fence));
+        }
     }
 }
 
diff --git a/libhwcomposer/hwc_utils.h b/libhwcomposer/hwc_utils.h
index 66fdc65..ca24205 100644
--- a/libhwcomposer/hwc_utils.h
+++ b/libhwcomposer/hwc_utils.h
@@ -29,7 +29,9 @@
 #include <gralloc_priv.h>
 #include <utils/String8.h>
 #include "qdMetaData.h"
+#include "mdp_version.h"
 #include <overlayUtils.h>
+#include <overlayRotator.h>
 #include <EGL/egl.h>
 
 
@@ -118,7 +120,6 @@
     //Video specific
     int yuvCount;
     int yuvIndices[MAX_NUM_APP_LAYERS];
-    int extOnlyLayerIndex;
     bool preMultipliedAlpha;
     int yuv4k2kIndices[MAX_NUM_APP_LAYERS];
     int yuv4k2kCount;
@@ -131,6 +132,9 @@
     hwc_rect_t rRoi;  //right ROI. Unused in single DSI panels.
     //App Buffer Composition index
     int  renderBufIndexforABC;
+    // Secure RGB specific
+    int secureRGBCount;
+    int secureRGBIndices[MAX_NUM_APP_LAYERS];
 };
 
 //PTOR Comp info
@@ -161,8 +165,8 @@
 };
 
 struct BwcPM {
-    static void setBwc(const hwc_rect_t& crop,
-            const hwc_rect_t& dst, const int& transform,
+    static void setBwc(const hwc_rect_t& crop, const hwc_rect_t& dst,
+            const int& transform, const int& downscale,
             ovutils::eMdpFlags& mdpFlags);
 };
 
@@ -190,7 +194,6 @@
 class LayerRotMap {
 public:
     LayerRotMap() { reset(); }
-    enum { MAX_SESS = 3 };
     void add(hwc_layer_1_t* layer, overlay::Rotator *rot);
     //Resets the mapping of layer to rotator
     void reset();
@@ -200,10 +203,11 @@
     uint32_t getCount() const;
     hwc_layer_1_t* getLayer(uint32_t index) const;
     overlay::Rotator* getRot(uint32_t index) const;
+    bool isRotCached(uint32_t index) const;
     void setReleaseFd(const int& fence);
 private:
-    hwc_layer_1_t* mLayer[MAX_SESS];
-    overlay::Rotator* mRot[MAX_SESS];
+    hwc_layer_1_t* mLayer[overlay::RotMgr::MAX_ROT_SESS];
+    overlay::Rotator* mRot[overlay::RotMgr::MAX_ROT_SESS];
     uint32_t mCount;
 };
 
@@ -351,7 +355,7 @@
 
 int configColorLayer(hwc_context_t *ctx, hwc_layer_1_t *layer, const int& dpy,
         ovutils::eMdpFlags& mdpFlags, ovutils::eZorder& z,
-        ovutils::eIsFg& isFg, const ovutils::eDest& dest);
+        const ovutils::eDest& dest);
 
 void updateSource(ovutils::eTransform& orient, ovutils::Whf& whf,
         hwc_rect_t& crop, overlay::Rotator *rot);
@@ -359,20 +363,20 @@
 //Routine to configure low resolution panels (<= 2048 width)
 int configureNonSplit(hwc_context_t *ctx, hwc_layer_1_t *layer, const int& dpy,
         ovutils::eMdpFlags& mdpFlags, ovutils::eZorder& z,
-        ovutils::eIsFg& isFg, const ovutils::eDest& dest,
+        const ovutils::eDest& dest,
         overlay::Rotator **rot);
 
 //Routine to configure high resolution panels (> 2048 width)
 int configureSplit(hwc_context_t *ctx, hwc_layer_1_t *layer, const int& dpy,
         ovutils::eMdpFlags& mdpFlags, ovutils::eZorder& z,
-        ovutils::eIsFg& isFg, const ovutils::eDest& lDest,
+        const ovutils::eDest& lDest,
         const ovutils::eDest& rDest, overlay::Rotator **rot);
 
 //Routine to split and configure high resolution YUV layer (> 2048 width)
 int configureSourceSplit(hwc_context_t *ctx, hwc_layer_1_t *layer,
         const int& dpy,
         ovutils::eMdpFlags& mdpFlags, ovutils::eZorder& z,
-        ovutils::eIsFg& isFg, const ovutils::eDest& lDest,
+        const ovutils::eDest& lDest,
         const ovutils::eDest& rDest, overlay::Rotator **rot);
 
 //On certain targets DMA pipes are used for rotation and they won't be available
@@ -387,6 +391,8 @@
 
 bool isDisplaySplit(hwc_context_t* ctx, int dpy);
 
+int getRotDownscale(hwc_context_t *ctx, const hwc_layer_1_t *layer);
+
 // Set the GPU hint flag to high for MIXED/GPU composition only for
 // first frame after MDP to GPU/MIXED mode transition.
 // Set the GPU hint to default if the current composition type is GPU
@@ -406,10 +412,11 @@
     return (hnd && (hnd->bufferType == BUFFER_TYPE_VIDEO));
 }
 
-// Returns true if the buffer is yuv
-static inline bool is4kx2kYuvBuffer(const private_handle_t* hnd) {
+// Returns true if the buffer is yuv and exceeds the mixer width
+static inline bool isYUVSplitNeeded(const private_handle_t* hnd) {
+    int maxMixerWidth = qdutils::MDPVersion::getInstance().getMaxMixerWidth();
     return (hnd && (hnd->bufferType == BUFFER_TYPE_VIDEO) &&
-            (hnd->width > 2048));
+            (hnd->width > maxMixerWidth));
 }
 
 // Returns true if the buffer is secure
@@ -430,11 +437,6 @@
     return (hnd && (private_handle_t::PRIV_FLAGS_HWC_LOCK & hnd->flags));
 }
 
-//Return true if buffer is for external display only
-static inline bool isExtOnly(const private_handle_t* hnd) {
-    return (hnd && (hnd->flags & private_handle_t::PRIV_FLAGS_EXTERNAL_ONLY));
-}
-
 //Return true if the buffer is intended for Secure Display
 static inline bool isSecureDisplayBuffer(const private_handle_t* hnd) {
     return (hnd && (hnd->flags & private_handle_t::PRIV_FLAGS_SECURE_DISPLAY));
diff --git a/libhwcomposer/hwc_virtual.cpp b/libhwcomposer/hwc_virtual.cpp
index f8f3b45..db43435 100644
--- a/libhwcomposer/hwc_virtual.cpp
+++ b/libhwcomposer/hwc_virtual.cpp
@@ -351,18 +351,12 @@
             ret = -1;
         }
 
-        int extOnlyLayerIndex =
-            ctx->listStats[dpy].extOnlyLayerIndex;
-
         private_handle_t *hnd = (private_handle_t *)fbLayer->handle;
-        if(extOnlyLayerIndex!= -1) {
-            hwc_layer_1_t *extLayer = &list->hwLayers[extOnlyLayerIndex];
-            hnd = (private_handle_t *)extLayer->handle;
-        } else if(copybitDone) {
+        if(copybitDone) {
             hnd = ctx->mCopyBit[dpy]->getCurrentRenderBuffer();
         }
 
-        if(hnd && !isYuvBuffer(hnd)) {
+        if(hnd) {
             if (!ctx->mFBUpdate[dpy]->draw(ctx, hnd)) {
                 ALOGE("%s: FBUpdate::draw fail!", __FUNCTION__);
                 ret = -1;
diff --git a/liboverlay/mdpWrapper.h b/liboverlay/mdpWrapper.h
index e24ad6a..9c38cdb 100644
--- a/liboverlay/mdpWrapper.h
+++ b/liboverlay/mdpWrapper.h
@@ -299,8 +299,8 @@
             s, ov.offset, ov.memory_id, ov.id, ov.flags, ov.priv);
 }
 inline void dump(const char* const s, const mdp_overlay& ov) {
-    ALOGE("%s mdp_overlay z=%d fg=%d alpha=%d mask=%d flags=0x%x id=%d",
-            s, ov.z_order, ov.is_fg, ov.alpha,
+    ALOGE("%s mdp_overlay z=%d alpha=%d mask=%d flags=0x%x id=%d",
+            s, ov.z_order, ov.alpha,
             ov.transp_mask, ov.flags, ov.id);
     dump("src", ov.src);
     dump("src_rect", ov.src_rect);
diff --git a/liboverlay/overlay.cpp b/liboverlay/overlay.cpp
index 46b6275..c016d3d 100644
--- a/liboverlay/overlay.cpp
+++ b/liboverlay/overlay.cpp
@@ -161,6 +161,8 @@
         return getPipe_8x16(pipeSpecs);
     } else if(MDPVersion::getInstance().is8x39()) {
         return getPipe_8x39(pipeSpecs);
+    } else if(MDPVersion::getInstance().is8994()) {
+        return getPipe_8994(pipeSpecs);
     }
 
     eDest dest = OV_INVALID;
@@ -255,6 +257,26 @@
     return getPipe_8x16(pipeSpecs);
 }
 
+utils::eDest Overlay::getPipe_8994(const PipeSpecs& pipeSpecs) {
+    //If DMA pipes need to be used in block mode for downscale, there could be
+    //cases where consecutive rounds need separate modes, which cannot be
+    //supported since we at least need 1 round in between where the DMA is
+    //unused
+    eDest dest = OV_INVALID;
+    if(pipeSpecs.formatClass == FORMAT_YUV) {
+        return nextPipe(OV_MDP_PIPE_VG, pipeSpecs.dpy, pipeSpecs.mixer);
+    } else {
+        dest = nextPipe(OV_MDP_PIPE_RGB, pipeSpecs.dpy, pipeSpecs.mixer);
+        if(dest == OV_INVALID) {
+            dest = nextPipe(OV_MDP_PIPE_VG, pipeSpecs.dpy, pipeSpecs.mixer);
+        }
+        if(dest == OV_INVALID and not pipeSpecs.needsScaling) {
+            dest = nextPipe(OV_MDP_PIPE_DMA, pipeSpecs.dpy, pipeSpecs.mixer);
+        }
+    }
+    return dest;
+}
+
 void Overlay::endAllSessions() {
     for(int i = 0; i < PipeBook::NUM_PIPES; i++) {
         if(mPipeBook[i].valid() && mPipeBook[i].mSession==PipeBook::START)
diff --git a/liboverlay/overlay.h b/liboverlay/overlay.h
index c8f68b0..4f5d56d 100644
--- a/liboverlay/overlay.h
+++ b/liboverlay/overlay.h
@@ -172,6 +172,7 @@
     utils::eDest getPipe_8x26(const PipeSpecs& pipeSpecs);
     utils::eDest getPipe_8x16(const PipeSpecs& pipeSpecs);
     utils::eDest getPipe_8x39(const PipeSpecs& pipeSpecs);
+    utils::eDest getPipe_8994(const PipeSpecs& pipeSpecs);
 
     /* Returns the handle to libscale.so's programScale function */
     static int (*getFnProgramScale())(struct mdp_overlay_list *);
diff --git a/liboverlay/overlayMdp.cpp b/liboverlay/overlayMdp.cpp
index 7f9f136..ddc40ee 100644
--- a/liboverlay/overlayMdp.cpp
+++ b/liboverlay/overlayMdp.cpp
@@ -96,7 +96,6 @@
     //TODO These calls should ideally be a part of setPipeParams API
     setFlags(args.mdpFlags);
     setZ(args.zorder);
-    setIsFg(args.isFg);
     setPlaneAlpha(args.planeAlpha);
     setBlending(args.blending);
 }
diff --git a/liboverlay/overlayMdp.h b/liboverlay/overlayMdp.h
index cb8e057..d415b9d 100644
--- a/liboverlay/overlayMdp.h
+++ b/liboverlay/overlayMdp.h
@@ -99,8 +99,6 @@
     void setFlags(int f);
     /* set z order */
     void setZ(utils::eZorder z);
-    /* set isFg flag */
-    void setIsFg(utils::eIsFg isFg);
     /* return a copy of src whf*/
     utils::Whf getSrcWhf() const;
     /* set plane alpha */
@@ -204,10 +202,6 @@
     mOVInfo.z_order = z;
 }
 
-inline void MdpCtrl::setIsFg(overlay::utils::eIsFg isFg) {
-    mOVInfo.is_fg = isFg;
-}
-
 inline void MdpCtrl::setPlaneAlpha(int planeAlpha) {
     mOVInfo.alpha = planeAlpha;
 }
diff --git a/liboverlay/overlayMdpRot.cpp b/liboverlay/overlayMdpRot.cpp
index bb985d7..d322897 100755
--- a/liboverlay/overlayMdpRot.cpp
+++ b/liboverlay/overlayMdpRot.cpp
@@ -17,6 +17,7 @@
  * limitations under the License.
 */
 
+#include <math.h>
 #include "overlayUtils.h"
 #include "overlayRotator.h"
 #include "gr.h"
@@ -36,10 +37,18 @@
 
 void MdpRot::setRotations(uint32_t r) { mRotImgInfo.rotations = (uint8_t)r; }
 
+int MdpRot::getSrcMemId() const {
+    return mRotDataInfo.src.memory_id;
+}
+
 int MdpRot::getDstMemId() const {
     return mRotDataInfo.dst.memory_id;
 }
 
+uint32_t MdpRot::getSrcOffset() const {
+    return mRotDataInfo.src.offset;
+}
+
 uint32_t MdpRot::getDstOffset() const {
     return mRotDataInfo.dst.offset;
 }
@@ -146,7 +155,6 @@
             mRotImgInfo.enable = 0;
             return false;
         }
-        save();
         mRotDataInfo.session_id = mRotImgInfo.session_id;
     }
     return true;
@@ -237,7 +245,10 @@
 }
 
 bool MdpRot::queueBuffer(int fd, uint32_t offset) {
-    if(enabled()) {
+    if(enabled() and (not isRotCached(fd,offset))) {
+        int prev_fd = getSrcMemId();
+        uint32_t prev_offset = getSrcOffset();
+
         mRotDataInfo.src.memory_id = fd;
         mRotDataInfo.src.offset = offset;
 
@@ -248,14 +259,17 @@
 
         mRotDataInfo.dst.offset =
                 mMem.mRotOffset[mMem.mCurrIndex];
-        mMem.mCurrIndex =
-                (mMem.mCurrIndex + 1) % mMem.mem.numBufs();
 
         if(!overlay::mdp_wrapper::rotate(mFd.getFD(), mRotDataInfo)) {
             ALOGE("MdpRot failed rotate");
             dump();
+            mRotDataInfo.src.memory_id = prev_fd;
+            mRotDataInfo.src.offset = prev_offset;
             return false;
         }
+        save();
+        mMem.mCurrIndex =
+                (mMem.mCurrIndex + 1) % mMem.mem.numBufs();
     }
     return true;
 }
@@ -274,4 +288,35 @@
     ovutils::getDump(buf, len, "MdpRotData", mRotDataInfo);
 }
 
+int MdpRot::getDownscaleFactor(const int& src_w, const int& src_h,
+        const int& dst_w, const int& dst_h, const uint32_t& /*mdpFormat*/,
+        const bool& /*isInterlaced*/) {
+    int dscale_factor = utils::ROT_DS_NONE;
+    // We need this check to engage the rotator whenever possible to assist MDP
+    // in performing video downscale.
+    // This saves bandwidth and avoids causing the driver to make too many panel
+    // -mode switches between BLT (writeback) and non-BLT (Direct) modes.
+    // Use-case: Video playback [with downscaling and rotation].
+    if (dst_w && dst_h)
+    {
+        float fDscale =  (float)(src_w * src_h) / (float)(dst_w * dst_h);
+        uint32_t dscale = (int)sqrtf(fDscale);
+
+        if(dscale < 2) {
+            // Down-scale to > 50% of orig.
+            dscale_factor = utils::ROT_DS_NONE;
+        } else if(dscale < 4) {
+            // Down-scale to between > 25% to <= 50% of orig.
+            dscale_factor = utils::ROT_DS_HALF;
+        } else if(dscale < 8) {
+            // Down-scale to between > 12.5% to <= 25% of orig.
+            dscale_factor = utils::ROT_DS_FOURTH;
+        } else {
+            // Down-scale to <= 12.5% of orig.
+            dscale_factor = utils::ROT_DS_EIGHTH;
+        }
+    }
+    return dscale_factor;
+}
+
 } // namespace overlay
diff --git a/liboverlay/overlayMdssRot.cpp b/liboverlay/overlayMdssRot.cpp
index 5783dcb..87e134a 100644
--- a/liboverlay/overlayMdssRot.cpp
+++ b/liboverlay/overlayMdssRot.cpp
@@ -17,6 +17,7 @@
  * limitations under the License.
 */
 
+#include <math.h>
 #include "overlayUtils.h"
 #include "overlayRotator.h"
 
@@ -50,10 +51,18 @@
 
 void MdssRot::setRotations(uint32_t flags) { mRotInfo.flags |= flags; }
 
+int MdssRot::getSrcMemId() const {
+    return mRotData.data.memory_id;
+}
+
 int MdssRot::getDstMemId() const {
     return mRotData.dst_data.memory_id;
 }
 
+uint32_t MdssRot::getSrcOffset() const {
+    return mRotData.data.offset;
+}
+
 uint32_t MdssRot::getDstOffset() const {
     return mRotData.dst_data.offset;
 }
@@ -80,6 +89,19 @@
 
 uint32_t MdssRot::getSessId() const { return mRotInfo.id; }
 
+void MdssRot::save() {
+    mLSRotInfo = mRotInfo;
+}
+
+bool MdssRot::rotConfChanged() const {
+    // 0 means same
+    if(0 == ::memcmp(&mRotInfo, &mLSRotInfo,
+                     sizeof (mdp_overlay))) {
+        return false;
+    }
+    return true;
+}
+
 bool MdssRot::init() {
     if(!utils::openDev(mFd, 0, Res::fbPath, O_RDWR)) {
         ALOGE("MdssRot failed to init fb0");
@@ -103,7 +125,8 @@
     mRotInfo.src_rect.h = crop.h;
 }
 
-void MdssRot::setDownscale(int /*ds*/) {
+void MdssRot::setDownscale(int downscale) {
+    mDownscale = downscale;
 }
 
 void MdssRot::setFlags(const utils::eMdpFlags& flags) {
@@ -128,19 +151,25 @@
 }
 
 bool MdssRot::commit() {
-    if (utils::isYuv(mRotInfo.src.format)) {
-        utils::normalizeCrop(mRotInfo.src_rect.x, mRotInfo.src_rect.w);
-        utils::normalizeCrop(mRotInfo.src_rect.y, mRotInfo.src_rect.h);
-        // For interlaced, crop.h should be 4-aligned
-        if ((mRotInfo.flags & utils::OV_MDP_DEINTERLACE) and
-                (mRotInfo.src_rect.h % 4))
-            mRotInfo.src_rect.h = utils::aligndown(mRotInfo.src_rect.h, 4);
-    }
+    Dim adjCrop(mRotInfo.src_rect.x,mRotInfo.src_rect.y,
+            mRotInfo.src_rect.w,mRotInfo.src_rect.h);
+    adjCrop = getFormatAdjustedCrop(adjCrop, mRotInfo.src.format,
+            mRotInfo.flags & utils::OV_MDP_DEINTERLACE);
+    adjCrop = getDownscaleAdjustedCrop(adjCrop, mDownscale);
+
+    mRotInfo.src_rect.x = adjCrop.x;
+    mRotInfo.src_rect.y = adjCrop.y;
+    mRotInfo.src_rect.w = adjCrop.w;
+    mRotInfo.src_rect.h = adjCrop.h;
 
     mRotInfo.dst_rect.x = 0;
     mRotInfo.dst_rect.y = 0;
-    mRotInfo.dst_rect.w = mRotInfo.src_rect.w;
-    mRotInfo.dst_rect.h = mRotInfo.src_rect.h;
+    mRotInfo.dst_rect.w = mDownscale ?
+            mRotInfo.src_rect.w / mDownscale : mRotInfo.src_rect.w;
+    mRotInfo.dst_rect.h = mDownscale ?
+            mRotInfo.src_rect.h / mDownscale : mRotInfo.src_rect.h;
+    //Clear for next round
+    mDownscale = 0;
 
     doTransform();
 
@@ -156,7 +185,10 @@
 }
 
 bool MdssRot::queueBuffer(int fd, uint32_t offset) {
-    if(enabled()) {
+    if(enabled() and (not isRotCached(fd,offset))) {
+        int prev_fd = getSrcMemId();
+        uint32_t prev_offset = getSrcOffset();
+
         mRotData.data.memory_id = fd;
         mRotData.data.offset = offset;
 
@@ -167,14 +199,17 @@
 
         mRotData.dst_data.offset =
                 mMem.mRotOffset[mMem.mCurrIndex];
-        mMem.mCurrIndex =
-                (mMem.mCurrIndex + 1) % mMem.mem.numBufs();
 
         if(!overlay::mdp_wrapper::play(mFd.getFD(), mRotData)) {
             ALOGE("MdssRot play failed!");
             dump();
+            mRotData.data.memory_id = prev_fd;
+            mRotData.data.offset = prev_offset;
             return false;
         }
+        save();
+        mMem.mCurrIndex =
+                (mMem.mCurrIndex + 1) % mMem.mem.numBufs();
     }
     return true;
 }
@@ -252,12 +287,14 @@
 
 void MdssRot::reset() {
     ovutils::memset0(mRotInfo);
+    ovutils::memset0(mLSRotInfo);
     ovutils::memset0(mRotData);
     mRotData.data.memory_id = -1;
     mRotInfo.id = MSMFB_NEW_REQUEST;
     ovutils::memset0(mMem.mRotOffset);
     mMem.mCurrIndex = 0;
     mOrientation = utils::OVERLAY_TRANSFORM_0;
+    mDownscale = 0;
 }
 
 void MdssRot::dump() const {
@@ -340,4 +377,54 @@
     return bufSize;
 }
 
+int MdssRot::getDownscaleFactor(const int& srcW, const int& srcH,
+        const int& dstW, const int& dstH, const uint32_t& mdpFormat,
+        const bool& isInterlaced) {
+    if(not srcW or not srcH or not dstW or not dstH or isInterlaced) return 0;
+
+    Dim crop(0, 0, srcW, srcH);
+    Dim adjCrop = getFormatAdjustedCrop(crop, mdpFormat,
+            false /*isInterlaced */);
+
+    uint32_t downscale = min((adjCrop.w / dstW), (adjCrop.h / dstH));
+    //Reduced to a power of 2
+    downscale = (uint32_t) powf(2.0f, floorf(log2f((float)downscale)));
+
+    if(downscale < 2 or downscale > 32) return 0;
+
+    //Allow only 1 line or pixel to be chopped off since the source needs to
+    //be aligned to downscale. Progressively try with smaller downscale to see
+    //if we can satisfy the threshold
+    //For YUV the loop shouldnt be needed, unless in exceptional cases
+    Dim dsAdjCrop = getDownscaleAdjustedCrop(adjCrop, downscale);
+    while(downscale > 2 and (adjCrop.w > dsAdjCrop.w or
+            adjCrop.h > dsAdjCrop.h)) {
+        downscale /= 2;
+        dsAdjCrop = getDownscaleAdjustedCrop(adjCrop, downscale);
+    }
+
+    if(not dsAdjCrop.w or not dsAdjCrop.h) return 0;
+    return downscale;
+}
+
+Dim MdssRot::getFormatAdjustedCrop(const Dim& crop,
+            const uint32_t& mdpFormat, const bool& isInterlaced) {
+    Dim adjCrop = crop;
+    if (isYuv(mdpFormat)) {
+        normalizeCrop(adjCrop.x, adjCrop.w);
+        normalizeCrop(adjCrop.y, adjCrop.h);
+        // For interlaced, crop.h should be 4-aligned
+        if (isInterlaced and (adjCrop.h % 4))
+            adjCrop.h = aligndown(adjCrop.h, 4);
+    }
+    return adjCrop;
+}
+
+Dim MdssRot::getDownscaleAdjustedCrop(const Dim& crop,
+        const uint32_t& downscale) {
+    uint32_t alignedSrcW = aligndown(crop.w, downscale * 2);
+    uint32_t alignedSrcH = aligndown(crop.h, downscale * 2);
+    return Dim(crop.x, crop.y, alignedSrcW, alignedSrcH);
+}
+
 } // namespace overlay
diff --git a/liboverlay/overlayRotator.cpp b/liboverlay/overlayRotator.cpp
index 0671b62..b55f06a 100644
--- a/liboverlay/overlayRotator.cpp
+++ b/liboverlay/overlayRotator.cpp
@@ -20,6 +20,7 @@
 #include "overlayRotator.h"
 #include "overlayUtils.h"
 #include "mdp_version.h"
+#include "sync/sync.h"
 #include "gr.h"
 
 namespace ovutils = overlay::utils;
@@ -28,6 +29,17 @@
 
 //============Rotator=========================
 
+Rotator::Rotator() {
+    char property[PROPERTY_VALUE_MAX];
+    mRotCacheDisabled = false;
+    if((property_get("debug.rotcache.disable", property, NULL) > 0) &&
+       (!strncmp(property, "1", PROPERTY_VALUE_MAX ) ||
+        (!strncasecmp(property,"true", PROPERTY_VALUE_MAX )))) {
+        /* Used in debugging to turnoff rotator caching */
+        mRotCacheDisabled = true;
+    }
+}
+
 Rotator::~Rotator() {}
 
 Rotator* Rotator::getRotator() {
@@ -42,6 +54,17 @@
     }
 }
 
+int Rotator::getDownscaleFactor(const int& srcW, const int& srcH,
+        const int& dstW, const int& dstH, const uint32_t& mdpFormat,
+        const bool& isInterlaced) {
+    if(getRotatorHwType() == TYPE_MDSS) {
+        return MdssRot::getDownscaleFactor(srcW, srcH, dstW, dstH,
+                mdpFormat, isInterlaced);
+    }
+    return MdpRot::getDownscaleFactor(srcW, srcH, dstW, dstH,
+            mdpFormat, isInterlaced);
+}
+
 uint32_t Rotator::calcOutputBufSize(const utils::Whf& destWhf) {
     //dummy aligned w & h.
     int alW = 0, alH = 0;
@@ -58,6 +81,20 @@
     return TYPE_MDP;
 }
 
+bool Rotator::isRotCached(int fd, uint32_t offset) const {
+    if(mRotCacheDisabled or rotConfChanged() or rotDataChanged(fd,offset))
+        return false;
+    return true;
+}
+
+bool Rotator::rotDataChanged(int fd, uint32_t offset) const {
+    /* fd and offset are the attributes of the current rotator input buffer.
+     * At this instance, getSrcMemId() and getSrcOffset() return the
+     * attributes of the previous rotator input buffer */
+    if( (fd == getSrcMemId()) and (offset == getSrcOffset()) )
+        return false;
+    return true;
+}
 
 //============RotMem=========================
 
@@ -86,7 +123,7 @@
     }
 }
 
-void RotMem::setReleaseFd(const int& fence) {
+void RotMem::setCurrBufReleaseFd(const int& fence) {
     int ret = 0;
 
     if(mRelFence[mCurrIndex] >= 0) {
@@ -104,6 +141,20 @@
     mRelFence[mCurrIndex] = fence;
 }
 
+void RotMem::setPrevBufReleaseFd(const int& fence) {
+    uint32_t numRotBufs = mem.numBufs();
+    uint32_t prevIndex = (mCurrIndex + numRotBufs - 1) % (numRotBufs);
+
+    if(mRelFence[prevIndex] >= 0) {
+        /* No need of any wait as nothing will be written into this
+         * buffer by the rotator (this func is called when rotator is
+         * in cache mode) */
+        ::close(mRelFence[prevIndex]);
+    }
+
+    mRelFence[prevIndex] = fence;
+}
+
 //============RotMgr=========================
 RotMgr * RotMgr::sRotMgr = NULL;
 
diff --git a/liboverlay/overlayRotator.h b/liboverlay/overlayRotator.h
index 64387cd..e045b44 100644
--- a/liboverlay/overlayRotator.h
+++ b/liboverlay/overlayRotator.h
@@ -35,7 +35,6 @@
 #include "mdpWrapper.h"
 #include "overlayUtils.h"
 #include "overlayMem.h"
-#include "sync/sync.h"
 
 namespace overlay {
 
@@ -53,7 +52,8 @@
     bool close();
     bool valid() { return mem.valid(); }
     uint32_t size() const { return mem.bufSz(); }
-    void setReleaseFd(const int& fence);
+    void setCurrBufReleaseFd(const int& fence);
+    void setPrevBufReleaseFd(const int& fence);
 
     // rotator data info dst offset
     uint32_t mRotOffset[ROT_NUM_BUFS];
@@ -73,9 +73,20 @@
     virtual void setFlags(const utils::eMdpFlags& flags) = 0;
     virtual void setTransform(const utils::eTransform& rot) = 0;
     virtual bool commit() = 0;
+    /* return true if the current rotator state is cached */
+    virtual bool isRotCached(int fd, uint32_t offset) const;
+    /* return true if current rotator config is same as the last round*/
+    virtual bool rotConfChanged() const = 0;
+    /* return true if the current rotator input buffer fd and offset
+     * are same as the last round */
+    virtual bool rotDataChanged(int fd, uint32_t offset) const;
     virtual void setDownscale(int ds) = 0;
+    /* returns the src buffer of the rotator for the previous/current round,
+     * depending on when it is called(before/after the queuebuffer)*/
+    virtual int getSrcMemId() const = 0;
     //Mem id and offset should be retrieved only after rotator kickoff
     virtual int getDstMemId() const = 0;
+    virtual uint32_t getSrcOffset() const = 0;
     virtual uint32_t getDstOffset() const = 0;
     //Destination width, height, format, position should be retrieved only after
     //rotator configuration is committed via commit API
@@ -86,16 +97,29 @@
     virtual bool queueBuffer(int fd, uint32_t offset) = 0;
     virtual void dump() const = 0;
     virtual void getDump(char *buf, size_t len) const = 0;
-    void setReleaseFd(const int& fence) { mMem.setReleaseFd(fence); }
+    inline void setCurrBufReleaseFd(const int& fence) {
+        mMem.setCurrBufReleaseFd(fence);
+    }
+    inline void setPrevBufReleaseFd(const int& fence) {
+        mMem.setPrevBufReleaseFd(fence);
+    }
     static Rotator *getRotator();
+    /* Returns downscale by successfully applying constraints
+     * Returns 0 if target doesnt support rotator downscaling
+     * or if any of the constraints are not met
+     */
+    static int getDownscaleFactor(const int& srcW, const int& srcH,
+            const int& dstW, const int& dstH, const uint32_t& mdpFormat,
+            const bool& isInterlaced);
 
 protected:
     /* Rotator memory manager */
     RotMem mMem;
-    explicit Rotator() {}
+    Rotator();
     static uint32_t calcOutputBufSize(const utils::Whf& destWhf);
 
 private:
+    bool mRotCacheDisabled;
     /*Returns rotator h/w type */
     static int getRotatorHwType();
     friend class RotMgr;
@@ -113,8 +137,11 @@
     virtual void setFlags(const utils::eMdpFlags& flags);
     virtual void setTransform(const utils::eTransform& rot);
     virtual bool commit();
+    virtual bool rotConfChanged() const;
     virtual void setDownscale(int ds);
+    virtual int getSrcMemId() const;
     virtual int getDstMemId() const;
+    virtual uint32_t getSrcOffset() const;
     virtual uint32_t getDstOffset() const;
     virtual uint32_t getDstFormat() const;
     virtual utils::Whf getDstWhf() const;
@@ -137,15 +164,22 @@
     void doTransform();
     /* reset underlying data, basically memset 0 */
     void reset();
-    /* return true if current rotator config is different
-     * than last known config */
-    bool rotConfChanged() const;
     /* save mRotImgInfo to be last known good config*/
     void save();
     /* Calculates the rotator's o/p buffer size post the transform calcs and
      * knowing the o/p format depending on whether fastYuv is enabled or not */
     uint32_t calcOutputBufSize();
 
+    /* Applies downscale by taking areas
+     * Returns a log(downscale)
+     * Constraints applied:
+     * - downscale should be a power of 2
+     * - Max downscale is 1/8
+     */
+    static int getDownscaleFactor(const int& srcW, const int& srcH,
+            const int& dstW, const int& dstH, const uint32_t& mdpFormat,
+            const bool& isInterlaced);
+
     /* rot info*/
     msm_rotator_img_info mRotImgInfo;
     /* Last saved rot info*/
@@ -158,6 +192,9 @@
     OvFD mFd;
 
     friend Rotator* Rotator::getRotator();
+    friend int Rotator::getDownscaleFactor(const int& srcW, const int& srcH,
+            const int& dstW, const int& dstH, const uint32_t& mdpFormat,
+            const bool& isInterlaced);
 };
 
 /*
@@ -172,8 +209,11 @@
     virtual void setFlags(const utils::eMdpFlags& flags);
     virtual void setTransform(const utils::eTransform& rot);
     virtual bool commit();
+    virtual bool rotConfChanged() const;
     virtual void setDownscale(int ds);
+    virtual int getSrcMemId() const;
     virtual int getDstMemId() const;
+    virtual uint32_t getSrcOffset() const;
     virtual uint32_t getDstOffset() const;
     virtual uint32_t getDstFormat() const;
     virtual utils::Whf getDstWhf() const;
@@ -196,14 +236,37 @@
     void doTransform();
     /* reset underlying data, basically memset 0 */
     void reset();
+    /* save mRotInfo to be last known good config*/
+    void save();
     /* Calculates the rotator's o/p buffer size post the transform calcs and
      * knowing the o/p format depending on whether fastYuv is enabled or not */
     uint32_t calcOutputBufSize();
     // Calculate the compressed o/p buffer size for BWC
     uint32_t calcCompressedBufSize(const utils::Whf& destWhf);
 
+     /* Caller's responsibility to swap srcW, srcH if there is a 90 transform
+      * Returns actual downscale (not a log value)
+      * Constraints applied:
+      * - downscale should be a power of 2
+      * - Max downscale is 1/32
+      * - Equal downscale is applied in both directions
+      * - {srcW, srcH} mod downscale = 0
+      * - Interlaced content is not supported
+      */
+    static int getDownscaleFactor(const int& srcW, const int& srcH,
+            const int& dstW, const int& dstH, const uint32_t& mdpFormat,
+            const bool& isInterlaced);
+
+    static utils::Dim getFormatAdjustedCrop(const utils::Dim& crop,
+            const uint32_t& mdpFormat, const bool& isInterlaced);
+
+    static utils::Dim getDownscaleAdjustedCrop(const utils::Dim& crop,
+            const uint32_t& downscale);
+
     /* MdssRot info structure */
-    mdp_overlay   mRotInfo;
+    mdp_overlay mRotInfo;
+    /* Last saved MdssRot info structure*/
+    mdp_overlay mLSRotInfo;
     /* MdssRot data structure */
     msmfb_overlay_data mRotData;
     /* Orientation */
@@ -212,8 +275,12 @@
     OvFD mFd;
     /* Enable/Disable Mdss Rot*/
     bool mEnabled;
+    int mDownscale;
 
     friend Rotator* Rotator::getRotator();
+    friend int Rotator::getDownscaleFactor(const int& srcW, const int& srcH,
+            const int& dstW, const int& dstH, const uint32_t& mdpFormat,
+            const bool& isInterlaced);
 };
 
 // Holder of rotator objects. Manages lifetimes
diff --git a/liboverlay/overlayUtils.cpp b/liboverlay/overlayUtils.cpp
index 0e063ab..f9ee326 100644
--- a/liboverlay/overlayUtils.cpp
+++ b/liboverlay/overlayUtils.cpp
@@ -248,36 +248,6 @@
     return retTrans;
 }
 
-int getDownscaleFactor(const int& src_w, const int& src_h,
-        const int& dst_w, const int& dst_h) {
-    int dscale_factor = utils::ROT_DS_NONE;
-    // We need this check to engage the rotator whenever possible to assist MDP
-    // in performing video downscale.
-    // This saves bandwidth and avoids causing the driver to make too many panel
-    // -mode switches between BLT (writeback) and non-BLT (Direct) modes.
-    // Use-case: Video playback [with downscaling and rotation].
-    if (dst_w && dst_h)
-    {
-        float fDscale =  (float)(src_w * src_h) / (float)(dst_w * dst_h);
-        uint32_t dscale = (int)sqrtf(fDscale);
-
-        if(dscale < 2) {
-            // Down-scale to > 50% of orig.
-            dscale_factor = utils::ROT_DS_NONE;
-        } else if(dscale < 4) {
-            // Down-scale to between > 25% to <= 50% of orig.
-            dscale_factor = utils::ROT_DS_HALF;
-        } else if(dscale < 8) {
-            // Down-scale to between > 12.5% to <= 25% of orig.
-            dscale_factor = utils::ROT_DS_FOURTH;
-        } else {
-            // Down-scale to <= 12.5% of orig.
-            dscale_factor = utils::ROT_DS_EIGHTH;
-        }
-    }
-    return dscale_factor;
-}
-
 void getDecimationFactor(const int& src_w, const int& src_h,
         const int& dst_w, const int& dst_h, uint8_t& horzDeci,
         uint8_t& vertDeci) {
@@ -285,15 +255,16 @@
     vertDeci = 0;
     float horDscale = ceilf((float)src_w / (float)dst_w);
     float verDscale = ceilf((float)src_h / (float)dst_h);
+    qdutils::MDPVersion& mdpHw = qdutils::MDPVersion::getInstance();
 
     //Next power of 2, if not already
     horDscale = powf(2.0f, ceilf(log2f(horDscale)));
     verDscale = powf(2.0f, ceilf(log2f(verDscale)));
 
-    //Since MDP can do 1/4 dscale and has better quality, split the task
+    //Since MDP can do downscale and has better quality, split the task
     //between decimator and MDP downscale
-    horDscale /= 4.0f;
-    verDscale /= 4.0f;
+    horDscale /= (float)mdpHw.getMaxMDPDownscale();
+    verDscale /= (float)mdpHw.getMaxMDPDownscale();
 
     if((int)horDscale)
         horzDeci = (uint8_t)log2f(horDscale);
@@ -301,7 +272,7 @@
     if((int)verDscale)
         vertDeci = (uint8_t)log2f(verDscale);
 
-    if(src_w > 2048) {
+    if(src_w > mdpHw.getMaxMixerWidth()) {
         //If the client sends us something > what a layer mixer supports
         //then it means it doesn't want to use split-pipe but wants us to
         //decimate. A minimum decimation of 2 will ensure that the width is
@@ -338,9 +309,9 @@
         const mdp_overlay& ov) {
     char str[256] = {'\0'};
     snprintf(str, 256,
-            "%s id=%d z=%d fg=%d alpha=%d mask=%d flags=0x%x H.Deci=%d,"
+            "%s id=%d z=%d alpha=%d mask=%d flags=0x%x H.Deci=%d,"
             "V.Deci=%d\n",
-            prefix, ov.id, ov.z_order, ov.is_fg, ov.alpha,
+            prefix, ov.id, ov.z_order, ov.alpha,
             ov.transp_mask, ov.flags, ov.horz_deci, ov.vert_deci);
     strlcat(buf, str, len);
     getDump(buf, len, "\tsrc", ov.src);
diff --git a/liboverlay/overlayUtils.h b/liboverlay/overlayUtils.h
index 530377b..2b8e303 100644
--- a/liboverlay/overlayUtils.h
+++ b/liboverlay/overlayUtils.h
@@ -210,15 +210,6 @@
     ROT_DS_EIGHTH = 3,
 };
 
-/* The values for is_fg flag for control alpha and transp
- * IS_FG_OFF means is_fg = 0
- * IS_FG_SET means is_fg = 1
- */
-enum eIsFg {
-    IS_FG_OFF = 0,
-    IS_FG_SET = 1
-};
-
 /*
  * Various mdp flags like PIPE SHARE, DEINTERLACE etc...
  * kernel/common/linux/msm_mdp.h
@@ -321,19 +312,17 @@
 struct PipeArgs {
     PipeArgs() : mdpFlags(OV_MDP_FLAGS_NONE),
         zorder(Z_SYSTEM_ALLOC),
-        isFg(IS_FG_OFF),
         rotFlags(ROT_FLAGS_NONE),
         planeAlpha(DEFAULT_PLANE_ALPHA),
         blending(OVERLAY_BLENDING_COVERAGE){
     }
 
     PipeArgs(eMdpFlags f, Whf _whf,
-            eZorder z, eIsFg fg, eRotFlags r,
+            eZorder z, eRotFlags r,
             int pA = DEFAULT_PLANE_ALPHA, eBlending b = OVERLAY_BLENDING_COVERAGE) :
         mdpFlags(f),
         whf(_whf),
         zorder(z),
-        isFg(fg),
         rotFlags(r),
         planeAlpha(pA),
         blending(b){
@@ -342,7 +331,6 @@
     eMdpFlags mdpFlags; // for mdp_overlay flags
     Whf whf;
     eZorder zorder; // stage number
-    eIsFg isFg; // control alpha & transp
     eRotFlags rotFlags;
     int planeAlpha;
     eBlending blending;
@@ -379,8 +367,6 @@
 int getMdpFormat(int format);
 int getMdpFormat(int format, bool tileEnabled);
 int getHALFormat(int mdpFormat);
-int getDownscaleFactor(const int& src_w, const int& src_h,
-        const int& dst_w, const int& dst_h);
 void getDecimationFactor(const int& src_w, const int& src_h,
         const int& dst_w, const int& dst_h, uint8_t& horzDeci,
         uint8_t& vertDeci);
@@ -399,6 +385,10 @@
     T c(a); a=b; b=c;
 }
 
+template<typename T> inline T max(T a, T b) { return (a > b) ? a : b; }
+
+template<typename T> inline T min(T a, T b) { return (a < b) ? a : b; }
+
 inline int alignup(int value, int a) {
     //if align = 0, return the value. Else, do alignment.
     return a ? ((((value - 1) / a) + 1) * a) : value;
diff --git a/liboverlay/overlayWriteback.cpp b/liboverlay/overlayWriteback.cpp
index 08af00f..de18e55 100644
--- a/liboverlay/overlayWriteback.cpp
+++ b/liboverlay/overlayWriteback.cpp
@@ -101,8 +101,12 @@
 
 bool Writeback::stopSession() {
     if(mFd.valid()) {
+        if(!Overlay::displayCommit(mFd.getFD())) {
+            ALOGE("%s: displayCommit failed", __func__);
+            return false;
+        }
         if(!mdp_wrapper::wbStopTerminate(mFd.getFD())) {
-            ALOGE("%s failed", __func__);
+            ALOGE("%s: wbStopTerminate failed", __func__);
             return false;
         }
     } else {
diff --git a/libqdutils/mdp_version.cpp b/libqdutils/mdp_version.cpp
index a064317..108313b 100644
--- a/libqdutils/mdp_version.cpp
+++ b/libqdutils/mdp_version.cpp
@@ -89,8 +89,8 @@
     mVGPipes = 0;
     mDMAPipes = 0;
     mFeatures = 0;
-    mMDPUpscale = 0;
-    mMDPDownscale = 0;
+    mMDPUpscale = 1;
+    mMDPDownscale = 1;
     mMacroTileEnabled = false;
     mLowBw = 0;
     mHighBw = 0;
@@ -321,6 +321,10 @@
         fclose(sysfsFd);
     }
 
+    if(mMDPVersion >= qdutils::MDP_V4_2 and mMDPVersion < qdutils::MDSS_V5) {
+        mRotDownscale = true;
+    }
+
     if(mSourceSplit) {
         memset(sysfsPath, 0, sizeof(sysfsPath));
         snprintf(sysfsPath , sizeof(sysfsPath),