Merge "hwc: Do not enable swaprect when rotation is involved"
diff --git a/displayengine/include/utils/constants.h b/displayengine/include/utils/constants.h
index 447b22a..8630d45 100644
--- a/displayengine/include/utils/constants.h
+++ b/displayengine/include/utils/constants.h
@@ -57,6 +57,8 @@
 #define ROUND_UP_ALIGN_DOWN(value, a) FLOAT(FloorToMultipleOf(UINT32(value + 0.5f), UINT32(a)))
 #define ROUND_UP_ALIGN_UP(value, a) FLOAT(CeilToMultipleOf(UINT32(value + 0.5f), UINT32(a)))
 
+#define IDLE_TIMEOUT_DEFAULT_MS 70
+
 template <class T>
 inline void Swap(T &a, T &b) {
   T c(a);
diff --git a/displayengine/libs/core/res_config.cpp b/displayengine/libs/core/res_config.cpp
index 1b5bb6d..27a4e41 100644
--- a/displayengine/libs/core/res_config.cpp
+++ b/displayengine/libs/core/res_config.cpp
@@ -92,9 +92,30 @@
   HWDisplayAttributes &display_attributes = display_resource_ctx->display_attributes;
   HWPipeInfo *left_pipe = &layer_config->left_pipe;
   HWPipeInfo *right_pipe = &layer_config->right_pipe;
+  float src_width = src_rect.right - src_rect.left;
+  float dst_width = dst_rect.right - dst_rect.left;
+  float src_height = src_rect.bottom - src_rect.top;
+  float dst_height = dst_rect.bottom - dst_rect.top;
+  float left_mixer_width = FLOAT(display_attributes.split_left);
 
-  if ((src_rect.right - src_rect.left) > kMaxSourcePipeWidth ||
-      (dst_rect.right - dst_rect.left) > kMaxInterfaceWidth || hw_res_info_.always_src_split) {
+  uint8_t decimation = 0;
+  if (CalculateDecimation((src_height / dst_height), &decimation) != kErrorNone) {
+    return kErrorNotSupported;
+  }
+  // Adjust source height to consider decimation
+  src_height /= powf(2.0f, decimation);
+
+  // No need to include common factors in clock calculation of pipe & mixer
+  float pipe_clock = MAX(dst_width, (dst_width * src_height / dst_height));
+  float mixer_clock = left_mixer_width;
+
+  // Layer cannot qualify for SrcSplit if source or destination width exceeds max pipe width.
+  // For perf/power optimization, even if "always_src_split" is enabled, use 2 pipes only if:
+  // 1. Source width is greater than split_left (left_mixer_width)
+  // 2. Pipe clock exceeds the mixer clock
+  if ((src_width > kMaxSourcePipeWidth) || (dst_width > kMaxSourcePipeWidth) ||
+      (hw_res_info_.always_src_split && ((src_width > left_mixer_width) ||
+      (pipe_clock > mixer_clock)))) {
     SplitRect(transform.flip_horizontal, src_rect, dst_rect, &left_pipe->src_roi,
               &left_pipe->dst_roi, &right_pipe->src_roi, &right_pipe->dst_roi, align_x);
     left_pipe->valid = true;
@@ -503,7 +524,6 @@
 }
 
 DisplayError ResManager::SetDecimationFactor(HWPipeInfo *pipe) {
-  float max_down_scale = FLOAT(hw_res_info_.max_scale_down);
   float src_h = pipe->src_roi.bottom - pipe->src_roi.top;
   float dst_h = pipe->dst_roi.bottom - pipe->dst_roi.top;
   float down_scale_h = src_h / dst_h;
@@ -512,28 +532,17 @@
   float dst_w = pipe->dst_roi.right - pipe->dst_roi.left;
   float down_scale_w = src_w / dst_w;
 
-
   pipe->horizontal_decimation = 0;
   pipe->vertical_decimation = 0;
 
-  // TODO(user): Need to check for the maximum downscale limit for decimation and return error
-  if (!hw_res_info_.has_decimation && ((down_scale_w > max_down_scale) ||
-      (down_scale_h > max_down_scale))) {
-    DLOGV("Downscaling exceeds the maximum MDP downscale limit and decimation not enabled");
+  if (CalculateDecimation(down_scale_w, &pipe->horizontal_decimation) != kErrorNone) {
     return kErrorNotSupported;
   }
 
-  if ((down_scale_w <= max_down_scale) && (down_scale_h <= max_down_scale)) {
-    return kErrorNone;
+  if (CalculateDecimation(down_scale_h, &pipe->vertical_decimation) != kErrorNone) {
+    return kErrorNotSupported;
   }
 
-  // Decimation is the remaining downscale factor after doing max SDE downscale.
-  // In SDE, decimation is supported in powers of 2.
-  // For ex: If a pipe needs downscale of 8 but max_down_scale is 4
-  // So decimation = powf(2.0, ceilf(log2f(8) - log2f(4))) = powf(2.0, 1.0) = 2
-  pipe->horizontal_decimation = UINT8(ceilf(log2f(down_scale_w) - log2f(max_down_scale)));
-  pipe->vertical_decimation = UINT8(ceilf(log2f(down_scale_h) - log2f(max_down_scale)));
-
   DLOGI_IF(kTagResources, "horizontal_decimation %d, vertical_decimation %d",
            pipe->horizontal_decimation, pipe->vertical_decimation);
 
@@ -638,4 +647,23 @@
   return error;
 }
 
+DisplayError ResManager::CalculateDecimation(float downscale, uint8_t* decimation) {
+  float max_down_scale = FLOAT(hw_res_info_.max_scale_down);
+
+  if (downscale <= max_down_scale) {
+    *decimation = 0;
+    return kErrorNone;
+  } else if (!hw_res_info_.has_decimation) {
+    DLOGE("Downscaling exceeds the maximum MDP downscale limit but decimation not enabled");
+    return kErrorNotSupported;
+  }
+
+  // Decimation is the remaining downscale factor after doing max SDE downscale.
+  // In SDE, decimation is supported in powers of 2.
+  // For ex: If a pipe needs downscale of 8 but max_down_scale is 4
+  // So decimation = powf(2.0, ceilf(log2f(8 / 4))) = powf(2.0, 1.0) = 2
+  *decimation = UINT8(ceilf(log2f(downscale / max_down_scale)));
+  return kErrorNone;
+}
+
 }  // namespace sde
diff --git a/displayengine/libs/core/res_manager.cpp b/displayengine/libs/core/res_manager.cpp
index dbb41e5..14cf11f 100644
--- a/displayengine/libs/core/res_manager.cpp
+++ b/displayengine/libs/core/res_manager.cpp
@@ -54,8 +54,6 @@
 
   DisplayError error = kErrorNone;
 
-  // TODO(user): Remove this. Disable src_split as kernel not supported yet
-  hw_res_info_.is_src_split = false;
   num_pipe_ = hw_res_info_.num_vig_pipe + hw_res_info_.num_rgb_pipe + hw_res_info_.num_dma_pipe;
 
   if (num_pipe_ > kPipeIdMax) {
diff --git a/displayengine/libs/core/res_manager.h b/displayengine/libs/core/res_manager.h
index be785c1..d21984e 100644
--- a/displayengine/libs/core/res_manager.h
+++ b/displayengine/libs/core/res_manager.h
@@ -208,6 +208,7 @@
                                HWPipeInfo *left_pipe, HWPipeInfo *right_pipe,
                                uint32_t align_x, uint32_t align_y);
   void ResourceStateLog(void);
+  DisplayError CalculateDecimation(float downscale, uint8_t* decimation);
 
   Locker locker_;
   HWResourceInfo hw_res_info_;
diff --git a/displayengine/libs/utils/debug_android.cpp b/displayengine/libs/utils/debug_android.cpp
index 7503999..96339df 100644
--- a/displayengine/libs/utils/debug_android.cpp
+++ b/displayengine/libs/utils/debug_android.cpp
@@ -29,6 +29,7 @@
 
 #include <stdlib.h>
 #include <utils/debug.h>
+#include <utils/constants.h>
 #include <cutils/log.h>
 #include <cutils/properties.h>
 
@@ -67,7 +68,7 @@
     return atoi(property);
   }
 
-  return 0;
+  return IDLE_TIMEOUT_DEFAULT_MS;
 }
 
 bool Debug::IsRotatorDownScaleDisabled() {
diff --git a/libhwcomposer/hwc.cpp b/libhwcomposer/hwc.cpp
index 928f3b8..f3b34c3 100644
--- a/libhwcomposer/hwc.cpp
+++ b/libhwcomposer/hwc.cpp
@@ -226,18 +226,11 @@
 
 }
 
-static void scaleDisplayFrame(hwc_context_t *ctx, int dpy,
-                            hwc_display_contents_1_t *list) {
-    uint32_t origXres = ctx->dpyAttr[dpy].xres;
-    uint32_t origYres = ctx->dpyAttr[dpy].yres;
-    uint32_t newXres = ctx->dpyAttr[dpy].xres_new;
-    uint32_t newYres = ctx->dpyAttr[dpy].yres_new;
-    float xresRatio = (float)origXres / (float)newXres;
-    float yresRatio = (float)origYres / (float)newYres;
+static void scaleDisplayFrame(hwc_display_contents_1_t *list, float xresRatio,
+        float yresRatio) {
     for (size_t i = 0; i < list->numHwLayers; i++) {
         hwc_layer_1_t *layer = &list->hwLayers[i];
         hwc_rect_t& displayFrame = layer->displayFrame;
-        hwc_rect_t sourceCrop = integerizeSourceCrop(layer->sourceCropf);
         uint32_t layerWidth = displayFrame.right - displayFrame.left;
         uint32_t layerHeight = displayFrame.bottom - displayFrame.top;
         displayFrame.left = (int)(xresRatio * (float)displayFrame.left);
@@ -249,6 +242,35 @@
     }
 }
 
+static void handleFbScaling(hwc_context_t *ctx, int dpy,
+        hwc_display_contents_1_t *list) {
+    //We could switch to a config that does not lead to fb scaling, but
+    //we need to update older display frames and ratios.
+    if (ctx->dpyAttr[dpy].fbScaling or ctx->dpyAttr[dpy].configSwitched) {
+        uint32_t xresPanel = ctx->dpyAttr[dpy].xres;
+        uint32_t yresPanel = ctx->dpyAttr[dpy].yres;
+        uint32_t xresFB = ctx->dpyAttr[dpy].xresFB;
+        uint32_t yresFB = ctx->dpyAttr[dpy].yresFB;
+        float xresRatio = (float)xresPanel / (float)xresFB;
+        float yresRatio = (float)yresPanel / (float)yresFB;
+        if(list->flags & HWC_GEOMETRY_CHANGED) {
+            //In case of geometry changes f/w resets displays frames w.r.t to
+            //FB's dimensions. So any config switch is automatically absorbed.
+            scaleDisplayFrame(list, xresRatio, yresRatio);
+        } else if (ctx->dpyAttr[dpy].configSwitched) {
+            //If there is a primary panel resolution switch without a geometry
+            //change we need to scale-back the previous ratio used and then use
+            //the current ratio. i.e use current ratio / prev ratio
+            scaleDisplayFrame(list,
+                    xresRatio / ctx->dpyAttr[dpy].fbWidthScaleRatio,
+                    yresRatio / ctx->dpyAttr[dpy].fbHeightScaleRatio);
+        }
+        ctx->dpyAttr[dpy].configSwitched = false;
+        ctx->dpyAttr[dpy].fbWidthScaleRatio = xresRatio;
+        ctx->dpyAttr[dpy].fbHeightScaleRatio = yresRatio;
+    }
+}
+
 static int hwc_prepare_primary(hwc_composer_device_1 *dev,
         hwc_display_contents_1_t *list) {
     ATRACE_CALL();
@@ -277,9 +299,7 @@
             ctx->dpyAttr[dpy].isActive = true;
         }
 
-        if (ctx->dpyAttr[dpy].customFBSize &&
-                list->flags & HWC_GEOMETRY_CHANGED)
-            scaleDisplayFrame(ctx, dpy, list);
+        handleFbScaling(ctx, dpy, list);
 
         reset_layer_prop(ctx, dpy, (int)list->numHwLayers - 1);
         setListStats(ctx, list, dpy);
@@ -449,7 +469,13 @@
             value = FB_BLANK_POWERDOWN;
             break;
         case HWC_POWER_MODE_DOZE:
+            // FB_BLANK_NORMAL is being used here to map to doze mode
+            // This definition is specific to our fbdev implementation
+            value = FB_BLANK_NORMAL;
+            break;
         case HWC_POWER_MODE_DOZE_SUSPEND:
+            // FB_BLANK_VSYNC_SUSPEND is being used here to map to doze_suspend
+            // This definition is specific to our fbdev implementation
             value = FB_BLANK_VSYNC_SUSPEND;
             break;
         case HWC_POWER_MODE_NORMAL:
@@ -485,7 +511,8 @@
                 ctx->mHPDEnabled = true;
             }
 
-            ctx->dpyAttr[dpy].isActive = not(mode == HWC_POWER_MODE_OFF);
+            ctx->dpyAttr[dpy].isActive = not(mode == HWC_POWER_MODE_OFF ||
+                    mode == HWC_POWER_MODE_DOZE_SUSPEND);
         }
         //Deliberate fall through since there is no explicit power mode for
         //virtual displays.
@@ -834,8 +861,8 @@
                     hotPluggable ? refresh : ctx->dpyAttr[disp].vsync_period;
             break;
         case HWC_DISPLAY_WIDTH:
-            if (ctx->dpyAttr[disp].customFBSize)
-                values[i] = ctx->dpyAttr[disp].xres_new;
+            if (ctx->dpyAttr[disp].fbScaling)
+                values[i] = ctx->dpyAttr[disp].xresFB;
             else
                 values[i] = hotPluggable ? xres : ctx->dpyAttr[disp].xres;
 
@@ -843,8 +870,8 @@
                     values[i]);
             break;
         case HWC_DISPLAY_HEIGHT:
-            if (ctx->dpyAttr[disp].customFBSize)
-                values[i] = ctx->dpyAttr[disp].yres_new;
+            if (ctx->dpyAttr[disp].fbScaling)
+                values[i] = ctx->dpyAttr[disp].yresFB;
             else
                 values[i] = hotPluggable ? yres : ctx->dpyAttr[disp].yres;
             ALOGD("%s disp = %d, height = %d",__FUNCTION__, disp,
@@ -879,6 +906,10 @@
     dumpsys_log(aBuf, "  DynRefreshRate=%d\n",
                 ctx->dpyAttr[HWC_DISPLAY_PRIMARY].dynRefreshRate);
     for(int dpy = 0; dpy < HWC_NUM_DISPLAY_TYPES; dpy++) {
+        if(dpy == HWC_DISPLAY_PRIMARY)
+            dumpsys_log(aBuf, "Dpy %d: FB Scale Ratio w %.1f, h %.1f\n", dpy,
+                    ctx->dpyAttr[dpy].fbWidthScaleRatio,
+                    ctx->dpyAttr[dpy].fbHeightScaleRatio);
         if(ctx->mMDPComp[dpy])
             ctx->mMDPComp[dpy]->dump(aBuf, ctx);
     }
diff --git a/libhwcomposer/hwc_fbupdate.cpp b/libhwcomposer/hwc_fbupdate.cpp
index ef83008..692ce29 100644
--- a/libhwcomposer/hwc_fbupdate.cpp
+++ b/libhwcomposer/hwc_fbupdate.cpp
@@ -49,11 +49,9 @@
     unsigned int size = 0;
     uint32_t xres = ctx->dpyAttr[mDpy].xres;
     uint32_t yres = ctx->dpyAttr[mDpy].yres;
-    if (ctx->dpyAttr[dpy].customFBSize) {
-        //GPU will render and compose at new resolution
-        //So need to have FB at new resolution
-        xres = ctx->dpyAttr[mDpy].xres_new;
-        yres = ctx->dpyAttr[mDpy].yres_new;
+    if (ctx->dpyAttr[dpy].fbScaling) {
+        xres = ctx->dpyAttr[mDpy].xresFB;
+        yres = ctx->dpyAttr[mDpy].yresFB;
     }
     getBufferAttributes((int)xres, (int)yres,
             HAL_PIXEL_FORMAT_RGBA_8888,
@@ -168,7 +166,7 @@
 
         // No FB update optimization on (1) Custom FB resolution,
         // (2) External Mirror mode, (3) External orientation
-        if(!ctx->dpyAttr[mDpy].customFBSize && !ctx->mBufferMirrorMode
+        if(!ctx->dpyAttr[mDpy].fbScaling && !ctx->mBufferMirrorMode
            && !ctx->mExtOrientation) {
             sourceCrop = fbUpdatingRect;
             displayFrame = fbUpdatingRect;
@@ -191,7 +189,7 @@
         } else if((mDpy && !extOrient
                   && !ctx->dpyAttr[mDpy].mMDPScalingMode)) {
             if(ctx->mOverlay->isUIScalingOnExternalSupported() &&
-                !ctx->dpyAttr[mDpy].customFBSize) {
+                !ctx->dpyAttr[mDpy].fbScaling) {
                 getNonWormholeRegion(list, sourceCrop);
                 displayFrame = sourceCrop;
             }
@@ -300,7 +298,7 @@
 
         // No FB update optimization on (1) Custom FB resolution,
         // (2) External Mirror mode, (3) External orientation
-        if(!ctx->dpyAttr[mDpy].customFBSize && !ctx->mBufferMirrorMode
+        if(!ctx->dpyAttr[mDpy].fbScaling && !ctx->mBufferMirrorMode
            && !ctx->mExtOrientation) {
             sourceCrop = fbUpdatingRect;
             displayFrame = fbUpdatingRect;
@@ -319,7 +317,7 @@
         } else if((mDpy && !extOrient
                   && !ctx->dpyAttr[mDpy].mMDPScalingMode)) {
             if(!qdutils::MDPVersion::getInstance().is8x26() &&
-                !ctx->dpyAttr[mDpy].customFBSize) {
+                !ctx->dpyAttr[mDpy].fbScaling) {
                 getNonWormholeRegion(list, sourceCrop);
                 displayFrame = sourceCrop;
             }
@@ -466,7 +464,7 @@
 
     // No FB update optimization on (1) Custom FB resolution,
     // (2) External Mirror mode, (3) External orientation
-    if(!ctx->dpyAttr[mDpy].customFBSize && !ctx->mBufferMirrorMode
+    if(!ctx->dpyAttr[mDpy].fbScaling && !ctx->mBufferMirrorMode
        && !ctx->mExtOrientation) {
         sourceCrop = fbUpdatingRect;
         displayFrame = fbUpdatingRect;
@@ -487,7 +485,7 @@
     } else if((mDpy && !extOrient
               && !ctx->dpyAttr[mDpy].mMDPScalingMode)) {
         if(!qdutils::MDPVersion::getInstance().is8x26() &&
-            !ctx->dpyAttr[mDpy].customFBSize) {
+            !ctx->dpyAttr[mDpy].fbScaling) {
             getNonWormholeRegion(list, sourceCrop);
             displayFrame = sourceCrop;
         }
diff --git a/libhwcomposer/hwc_qclient.cpp b/libhwcomposer/hwc_qclient.cpp
index 487ec77..9b1f81d 100644
--- a/libhwcomposer/hwc_qclient.cpp
+++ b/libhwcomposer/hwc_qclient.cpp
@@ -121,13 +121,8 @@
         Parcel* outParcel) {
     int dpy = inParcel->readInt32();
     outParcel->writeInt32(ctx->dpyAttr[dpy].vsync_period);
-    if (ctx->dpyAttr[dpy].customFBSize) {
-        outParcel->writeInt32(ctx->dpyAttr[dpy].xres_new);
-        outParcel->writeInt32(ctx->dpyAttr[dpy].yres_new);
-    } else {
-        outParcel->writeInt32(ctx->dpyAttr[dpy].xres);
-        outParcel->writeInt32(ctx->dpyAttr[dpy].yres);
-    }
+    outParcel->writeInt32(ctx->dpyAttr[dpy].xres);
+    outParcel->writeInt32(ctx->dpyAttr[dpy].yres);
     outParcel->writeFloat(ctx->dpyAttr[dpy].xdpi);
     outParcel->writeFloat(ctx->dpyAttr[dpy].ydpi);
     //XXX: Need to check what to return for HDMI
@@ -355,6 +350,121 @@
     }
 }
 
+static status_t setActiveConfig(hwc_context_t* ctx, const Parcel *inParcel,
+        Parcel *outParcel) {
+    uint32_t index = inParcel->readInt32();
+    int dpy = inParcel->readInt32();
+    //Currently only primary supported
+    if(dpy > HWC_DISPLAY_PRIMARY) {
+        return BAD_VALUE;
+    }
+
+    Configs *configs = Configs::getInstance();
+    if(configs == NULL) {
+        ALOGE("%s(): Unable to acquire a Configs instance", __FUNCTION__);
+        return INVALID_OPERATION;
+    }
+
+    if(configs->getActiveConfig() == index) {
+        ALOGI("%s(): Config %u is already set", __FUNCTION__, index);
+        return ALREADY_EXISTS;
+    }
+
+    ctx->mDrawLock.lock();
+    //Updates the necessary sysfs nodes and reads split info again which is
+    //needed to reinitialize composition resources.
+    if(configs->setActiveConfig(index) == false) {
+        ALOGE("%s(): Failed to set config %u", __FUNCTION__, index);
+        ctx->mDrawLock.unlock();
+        return UNKNOWN_ERROR;
+    }
+
+    qdutils::DisplayAttributes attr = configs->getAttributes(index);
+
+    ctx->dpyAttr[dpy].xres = attr.xres;
+    ctx->dpyAttr[dpy].yres = attr.yres;
+
+    ctx->dpyAttr[dpy].fbScaling = ((ctx->dpyAttr[dpy].xres !=
+            ctx->dpyAttr[dpy].xresFB) || (ctx->dpyAttr[dpy].yres !=
+            ctx->dpyAttr[dpy].yresFB));
+
+    destroyCompositionResources(ctx, dpy);
+    initCompositionResources(ctx, dpy);
+    ctx->dpyAttr[dpy].configSwitched = true;
+    ctx->mDrawLock.unlock();
+    ctx->proc->invalidate(ctx->proc);
+    return NO_ERROR;
+}
+
+static status_t getActiveConfig(hwc_context_t* ctx, const Parcel *inParcel,
+        Parcel *outParcel) {
+    Locker::Autolock _sl(ctx->mDrawLock);
+    int dpy = inParcel->readInt32();
+    //Currently only primary supported
+    if(dpy > HWC_DISPLAY_PRIMARY) {
+        return BAD_VALUE;
+    }
+
+    Configs *configs = Configs::getInstance();
+    if(configs == NULL) {
+        ALOGE("%s(): Unable to acquire a Configs instance", __FUNCTION__);
+        return INVALID_OPERATION;
+    }
+
+    outParcel->writeInt32(configs->getActiveConfig());
+    return NO_ERROR;
+}
+
+static status_t getConfigCount(hwc_context_t* ctx, const Parcel *inParcel,
+        Parcel *outParcel) {
+    Locker::Autolock _sl(ctx->mDrawLock);
+    int dpy = inParcel->readInt32();
+    //Currently only primary supported
+    if(dpy > HWC_DISPLAY_PRIMARY) {
+        return BAD_VALUE;
+    }
+
+    Configs *configs = Configs::getInstance();
+    if(configs == NULL) {
+        ALOGE("%s(): Unable to acquire a Configs instance", __FUNCTION__);
+        return INVALID_OPERATION;
+    }
+
+    outParcel->writeInt32(configs->getConfigCount());
+    return NO_ERROR;
+}
+
+static status_t getDisplayAttributesForConfig(hwc_context_t* ctx,
+        const Parcel *inParcel, Parcel *outParcel) {
+    Locker::Autolock _sl(ctx->mDrawLock);
+    uint32_t index = inParcel->readInt32();
+    int dpy = inParcel->readInt32();
+    //Currently only primary supported
+    if(dpy > HWC_DISPLAY_PRIMARY) {
+        return BAD_VALUE;
+    }
+
+    Configs *configs = Configs::getInstance();
+    if(configs == NULL) {
+        ALOGE("%s(): Unable to acquire a Configs instance", __FUNCTION__);
+        return INVALID_OPERATION;
+    }
+
+    //xres, yres are used from the Config class, we assume for now that the
+    //other params are the same. This might change in the future.
+    outParcel->writeInt32(ctx->dpyAttr[dpy].vsync_period);
+
+    qdutils::DisplayAttributes attr = configs->getAttributes(index);
+    outParcel->writeInt32(attr.xres);
+    outParcel->writeInt32(attr.yres);
+
+    outParcel->writeFloat(ctx->dpyAttr[dpy].xdpi);
+    outParcel->writeFloat(ctx->dpyAttr[dpy].ydpi);
+    outParcel->writeInt32(ctx->mMDP.panel);
+
+    return NO_ERROR;
+}
+
 status_t QClient::notifyCallback(uint32_t command, const Parcel* inParcel,
         Parcel* outParcel) {
     status_t ret = NO_ERROR;
@@ -418,6 +528,19 @@
         case IQService::SET_S3D_MODE:
             setS3DMode(mHwcContext, inParcel->readInt32());
             break;
+        case IQService::SET_ACTIVE_CONFIG:
+            ret = setActiveConfig(mHwcContext, inParcel, outParcel);
+            break;
+        case IQService::GET_ACTIVE_CONFIG:
+            ret = getActiveConfig(mHwcContext, inParcel, outParcel);
+            break;
+        case IQService::GET_CONFIG_COUNT:
+            ret = getConfigCount(mHwcContext, inParcel, outParcel);
+            break;
+        case IQService::GET_DISPLAY_ATTRIBUTES_FOR_CONFIG:
+            ret = getDisplayAttributesForConfig(mHwcContext, inParcel,
+                    outParcel);
+            break;
         default:
             ret = NO_ERROR;
     }
diff --git a/libhwcomposer/hwc_utils.cpp b/libhwcomposer/hwc_utils.cpp
index 9c94b2f..d162fb5 100644
--- a/libhwcomposer/hwc_utils.cpp
+++ b/libhwcomposer/hwc_utils.cpp
@@ -93,33 +93,38 @@
             (xres < MIN_DISPLAY_XRES || yres < MIN_DISPLAY_YRES));
 }
 
-void changeResolution(hwc_context_t *ctx, int xres_orig, int yres_orig,
-                      int width, int height) {
+static void handleFbScaling(hwc_context_t *ctx, int xresPanel, int yresPanel,
+        int width, int height) {
+    const int dpy = HWC_DISPLAY_PRIMARY;
     //Store original display resolution.
-    ctx->dpyAttr[HWC_DISPLAY_PRIMARY].xres_new = xres_orig;
-    ctx->dpyAttr[HWC_DISPLAY_PRIMARY].yres_new = yres_orig;
-    ctx->dpyAttr[HWC_DISPLAY_PRIMARY].customFBSize = false;
+    ctx->dpyAttr[dpy].xresFB = xresPanel;
+    ctx->dpyAttr[dpy].yresFB = yresPanel;
+    ctx->dpyAttr[dpy].fbScaling = false;
     char property[PROPERTY_VALUE_MAX] = {'\0'};
     char *yptr = NULL;
     if (property_get("debug.hwc.fbsize", property, NULL) > 0) {
         yptr = strcasestr(property,"x");
         if(yptr) {
-            int xres_new = atoi(property);
-            int yres_new = atoi(yptr + 1);
-            if (isValidResolution(ctx,xres_new,yres_new) &&
-                xres_new != xres_orig && yres_new != yres_orig) {
-                ctx->dpyAttr[HWC_DISPLAY_PRIMARY].xres_new = xres_new;
-                ctx->dpyAttr[HWC_DISPLAY_PRIMARY].yres_new = yres_new;
-                ctx->dpyAttr[HWC_DISPLAY_PRIMARY].customFBSize = true;
+            int xresFB = atoi(property);
+            int yresFB = atoi(yptr + 1);
+            if (isValidResolution(ctx, xresFB, yresFB) &&
+                xresFB != xresPanel && yresFB != yresPanel) {
+                ctx->dpyAttr[dpy].xresFB = xresFB;
+                ctx->dpyAttr[dpy].yresFB = yresFB;
+                ctx->dpyAttr[dpy].fbScaling = true;
 
-                //Caluculate DPI according to changed resolution.
-                float xdpi = ((float)xres_new * 25.4f) / (float)width;
-                float ydpi = ((float)yres_new * 25.4f) / (float)height;
-                ctx->dpyAttr[HWC_DISPLAY_PRIMARY].xdpi = xdpi;
-                ctx->dpyAttr[HWC_DISPLAY_PRIMARY].ydpi = ydpi;
+                //Calculate DPI according to changed resolution.
+                float xdpi = ((float)xresFB * 25.4f) / (float)width;
+                float ydpi = ((float)yresFB * 25.4f) / (float)height;
+                ctx->dpyAttr[dpy].xdpi = xdpi;
+                ctx->dpyAttr[dpy].ydpi = ydpi;
             }
         }
     }
+    ctx->dpyAttr[dpy].fbWidthScaleRatio = (float) ctx->dpyAttr[dpy].xres /
+            (float) ctx->dpyAttr[dpy].xresFB;
+    ctx->dpyAttr[dpy].fbHeightScaleRatio = (float) ctx->dpyAttr[dpy].yres /
+            (float) ctx->dpyAttr[dpy].yresFB;
 }
 
 // Initialize hdmi display attributes based on
@@ -234,8 +239,7 @@
     ctx->dpyAttr[HWC_DISPLAY_PRIMARY].vsync_period =
             (uint32_t)(1000000000l / fps);
 
-    //To change resolution of primary display
-    changeResolution(ctx, info.xres, info.yres, info.width, info.height);
+    handleFbScaling(ctx, info.xres, info.yres, info.width, info.height);
 
     //Unblank primary on first boot
     if(ioctl(fb_fd, FBIOBLANK,FB_BLANK_UNBLANK) < 0) {
diff --git a/libhwcomposer/hwc_utils.h b/libhwcomposer/hwc_utils.h
index ea8d652..26d036f 100644
--- a/libhwcomposer/hwc_utils.h
+++ b/libhwcomposer/hwc_utils.h
@@ -112,12 +112,6 @@
     int mAsWidthRatio;
     int mAsHeightRatio;
 
-    //If property fbsize set via adb shell debug.hwc.fbsize = XRESxYRES
-    //following fields are used.
-    bool customFBSize;
-    uint32_t xres_new;
-    uint32_t yres_new;
-
     // This is the 3D mode to which the TV is set
     // The mode may be set via the appearance of a layer with 3D format
     // or by forcing the mode via binder.
@@ -130,6 +124,18 @@
     // HDMI_S3D_NONE
     int s3dMode;
     bool s3dModeForced;
+    //If property fbsize set via adb shell debug.hwc.fbsize = XRESxYRES
+    //following fields are used.
+    //Also used when the actual panel's dimensions change and FB remains
+    //constant
+    bool fbScaling;
+    uint32_t xresFB; //FB's width, by default from VSCREEN overridden by prop
+    uint32_t yresFB; //FB's height, by default from VSCREEN overridden by prop
+    float fbWidthScaleRatio; // Panel Width / FB Width
+    float fbHeightScaleRatio; // Panel Height / FB Height
+    //If configuration changed dynamically without subsequent GEOMETRY changes
+    //we may still need to adjust destination params
+    bool configSwitched;
 };
 
 struct ListStats {
diff --git a/libqdutils/display_config.cpp b/libqdutils/display_config.cpp
index 03a7046..40b1ef7 100644
--- a/libqdutils/display_config.cpp
+++ b/libqdutils/display_config.cpp
@@ -27,14 +27,24 @@
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
 #include <display_config.h>
 #include <QServiceUtils.h>
+#include <qd_utils.h>
 
 using namespace android;
 using namespace qService;
 
 namespace qdutils {
 
+//=============================================================================
+// The functions below run in the client process and wherever necessary
+// do a binder call to HWC to get/set data.
+
 int isExternalConnected(void) {
     int ret;
     status_t err = (status_t) FAILED_TRANSACTION;
@@ -70,7 +80,7 @@
         dpyattr.ydpi = outParcel.readFloat();
         dpyattr.panel_type = (char) outParcel.readInt32();
     } else {
-        ALOGE("%s: Failed to get display attributes err=%d", __FUNCTION__, err);
+        ALOGE("%s() failed with err %d", __FUNCTION__, err);
     }
     return err;
 }
@@ -170,6 +180,238 @@
     return err;
 }
 
+int getConfigCount(int /*dpy*/) {
+    int numConfigs = -1;
+    sp<IQService> binder = getBinder();
+    if(binder != NULL) {
+        Parcel inParcel, outParcel;
+        inParcel.writeInt32(DISPLAY_PRIMARY);
+        status_t err = binder->dispatch(IQService::GET_CONFIG_COUNT,
+                &inParcel, &outParcel);
+        if(!err) {
+            numConfigs = outParcel.readInt32();
+            ALOGI("%s() Received num configs %d", __FUNCTION__, numConfigs);
+        } else {
+            ALOGE("%s() failed with err %d", __FUNCTION__, err);
+        }
+    }
+    return numConfigs;
+}
+
+int getActiveConfig(int /*dpy*/) {
+    int configIndex = -1;
+    sp<IQService> binder = getBinder();
+    if(binder != NULL) {
+        Parcel inParcel, outParcel;
+        inParcel.writeInt32(DISPLAY_PRIMARY);
+        status_t err = binder->dispatch(IQService::GET_ACTIVE_CONFIG,
+                &inParcel, &outParcel);
+        if(!err) {
+            configIndex = outParcel.readInt32();
+            ALOGI("%s() Received active config index %d", __FUNCTION__,
+                    configIndex);
+        } else {
+            ALOGE("%s() failed with err %d", __FUNCTION__, err);
+        }
+    }
+    return configIndex;
+}
+
+int setActiveConfig(int configIndex, int /*dpy*/) {
+    status_t err = (status_t) FAILED_TRANSACTION;
+    sp<IQService> binder = getBinder();
+    if(binder != NULL) {
+        Parcel inParcel, outParcel;
+        inParcel.writeInt32(configIndex);
+        inParcel.writeInt32(DISPLAY_PRIMARY);
+        err = binder->dispatch(IQService::SET_ACTIVE_CONFIG,
+                &inParcel, &outParcel);
+        if(!err) {
+            ALOGI("%s() Successfully set active config index %d", __FUNCTION__,
+                    configIndex);
+        } else {
+            ALOGE("%s() failed with err %d", __FUNCTION__, err);
+        }
+    }
+    return err;
+}
+
+DisplayAttributes getDisplayAttributes(int configIndex, int /*dpy*/) {
+    DisplayAttributes dpyattr;
+    sp<IQService> binder = getBinder();
+    if(binder != NULL) {
+        Parcel inParcel, outParcel;
+        inParcel.writeInt32(configIndex);
+        inParcel.writeInt32(DISPLAY_PRIMARY);
+        status_t err = binder->dispatch(
+                IQService::GET_DISPLAY_ATTRIBUTES_FOR_CONFIG, &inParcel,
+                &outParcel);
+        if(!err) {
+            dpyattr.vsync_period = outParcel.readInt32();
+            dpyattr.xres = outParcel.readInt32();
+            dpyattr.yres = outParcel.readInt32();
+            dpyattr.xdpi = outParcel.readFloat();
+            dpyattr.ydpi = outParcel.readFloat();
+            dpyattr.panel_type = (char) outParcel.readInt32();
+            ALOGI("%s() Received attrs for index %d: xres %d, yres %d",
+                    __FUNCTION__, configIndex, dpyattr.xres, dpyattr.yres);
+        } else {
+            ALOGE("%s() failed with err %d", __FUNCTION__, err);
+        }
+    }
+    return dpyattr;
+}
+
+//=============================================================================
+// The functions/methods below run in the context of HWC and
+// are called in response to binder calls from clients
+
+Configs* Configs::getInstance() {
+    if(sConfigs == NULL) {
+        sConfigs = new Configs();
+        if(sConfigs->init() == false) {
+            ALOGE("%s(): Configs initialization failed", __FUNCTION__);
+            delete sConfigs;
+            sConfigs = NULL;
+        }
+    }
+    return sConfigs;
+}
+
+Configs::Configs() : mActiveConfig(0), mConfigsSupported(0) {}
+
+bool Configs::init() {
+    DisplayAttributes dpyAttr;
+    if(not getCurrentMode(dpyAttr)) {
+        ALOGE("%s(): Mode switch is disabled", __FUNCTION__);
+        return false;
+    }
+
+    FILE *fHnd;
+    size_t len = PAGE_SIZE;
+    ssize_t read = 0;
+    uint32_t configCount = 0;
+    char sysfsPath[MAX_SYSFS_FILE_PATH];
+
+    memset(sysfsPath, '\0', sizeof(sysfsPath));
+    snprintf(sysfsPath , sizeof(sysfsPath),
+            "/sys/class/graphics/fb0/modes");
+
+    fHnd = fopen(sysfsPath, "r");
+    if (fHnd == NULL) {
+        ALOGE("%s(): Opening file %s failed with error %s", __FUNCTION__,
+                sysfsPath, strerror(errno));
+        return false;
+    }
+
+    memset(mModeStr, 0, sizeof(mModeStr));
+    while((configCount < CONFIGS_MAX) and
+            ((read = getline(&mModeStr[configCount], &len, fHnd)) > 0)) {
+        //String is of form "U:1600x2560p-0". Documentation/fb/modedb.txt in the
+        //kernel has more info on the format.
+        char *xptr = strcasestr(mModeStr[configCount], ":");
+        char *yptr = strcasestr(mModeStr[configCount], "x");
+        if(xptr && yptr) {
+            mConfigs[configCount].xres = atoi(xptr + 1);
+            mConfigs[configCount].yres = atoi(yptr + 1);
+            ALOGI("%s(): Parsed Config %s", __FUNCTION__,
+                    mModeStr[configCount]);
+            ALOGI("%s(): Config %u: %u x %u", __FUNCTION__, configCount,
+                    mConfigs[configCount].xres, mConfigs[configCount].yres);
+            if(mConfigs[configCount].xres == dpyAttr.xres and
+                    mConfigs[configCount].yres == dpyAttr.yres) {
+                mActiveConfig = configCount;
+            }
+        } else {
+            ALOGE("%s(): Tokenizing str %s failed", __FUNCTION__,
+                    mModeStr[configCount]);
+            //Free memory allocated internally by getline()
+            for(uint32_t i = 0; i <= configCount; i++) {
+                free(mModeStr[i]);
+            }
+            fclose(fHnd);
+            return false;
+        }
+        configCount++;
+    }
+
+    fclose(fHnd);
+
+    if(configCount == 0) {
+        ALOGE("%s No configs found", __FUNCTION__);
+        return false;
+    }
+    mConfigsSupported = configCount;
+    return true;
+}
+
+bool Configs::getCurrentMode(DisplayAttributes& dpyAttr) {
+    bool ret = false;
+    FILE *fHnd = fopen("/sys/class/graphics/fb0/mode", "r");
+    if(fHnd) {
+        char *buffer = NULL; //getline will allocate
+        size_t len = PAGE_SIZE;
+        if(getline(&buffer, &len, fHnd) > 0) {
+            //String is of form "U:1600x2560p-0". Documentation/fb/modedb.txt in
+            //kernel has more info on the format.
+            char *xptr = strcasestr(buffer, ":");
+            char *yptr = strcasestr(buffer, "x");
+            if(xptr && yptr) {
+                dpyAttr.xres = atoi(xptr + 1);
+                dpyAttr.yres = atoi(yptr + 1);
+                ALOGI("%s(): Parsed Current Config Str %s", __FUNCTION__,
+                        buffer);
+                ALOGI("%s(): Current Config: %u x %u", __FUNCTION__,
+                        dpyAttr.xres, dpyAttr.yres);
+                ret = true;
+            }
+        }
+
+        if(buffer)
+            free(buffer);
+
+        fclose(fHnd);
+    }
+    return ret;
+}
+
+bool Configs::setActiveConfig(const uint32_t& index) {
+    if(index >= mConfigsSupported) {
+        ALOGE("%s(): Invalid Index %u", __FUNCTION__, index);
+        return false;
+    }
+
+    bool ret = true;
+    int fd = -1;
+    size_t len = PAGE_SIZE;
+    char sysfsPath[MAX_SYSFS_FILE_PATH];
+    memset(sysfsPath, '\0', sizeof(sysfsPath));
+    snprintf(sysfsPath , sizeof(sysfsPath),
+            "/sys/class/graphics/fb0/mode");
+
+    fd = open(sysfsPath, O_WRONLY);
+    if (fd < 0) {
+        ALOGE("%s(): Opening file %s failed", __FUNCTION__, sysfsPath);
+        return false;
+    }
+
+    ssize_t written = pwrite(fd, mModeStr[index], strlen(mModeStr[index]), 0);
+    if(written <= 0) {
+        ALOGE("%s(): Writing config %s to %s failed with error: %s",
+                __FUNCTION__, mModeStr[index], sysfsPath, strerror(errno));
+        close(fd);
+        return false;
+    }
+
+    ALOGI("%s(): Successfully set config %u", __FUNCTION__, index);
+    mActiveConfig = index;
+    MDPVersion::getInstance().updateSplitInfo();
+    close(fd);
+    return true;
+}
+
+Configs* Configs::sConfigs = NULL;
+
 }; //namespace
 
 // ----------------------------------------------------------------------------
diff --git a/libqdutils/display_config.h b/libqdutils/display_config.h
index 8bafe91..6a66e9f 100644
--- a/libqdutils/display_config.h
+++ b/libqdutils/display_config.h
@@ -31,11 +31,8 @@
 #include <mdp_version.h>
 #include <hardware/hwcomposer.h>
 
-// This header is for clients to use to set/get global display configuration
-// The functions in this header run in the client process and wherever necessary
-// do a binder call to HWC to get/set data.
+// This header is for clients to use to set/get global display configuration.
 // Only primary and external displays are supported here.
-// WiFi/virtual displays are not supported.
 
 namespace qdutils {
 
@@ -69,14 +66,20 @@
 
 // Display Attributes that are available to clients of this library
 // Not to be confused with a similar struct in hwc_utils (in the hwc namespace)
-struct DisplayAttributes_t {
+typedef struct DisplayAttributes {
     uint32_t vsync_period; //nanoseconds
     uint32_t xres;
     uint32_t yres;
     float xdpi;
     float ydpi;
     char panel_type;
-};
+    DisplayAttributes() : vsync_period(0), xres(0), yres(0), xdpi(0.0f),
+            ydpi(0.0f), panel_type(0) {}
+} DisplayAttributes_t;
+
+//=============================================================================
+// The functions below run in the client process and wherever necessary
+// do a binder call to HWC to get/set data.
 
 // Check if external display is connected. Useful to check before making
 // calls for external displays
@@ -104,4 +107,66 @@
 
 // Enable/Disable/Set refresh rate dynamically
 int configureDynRefreshRate(uint32_t op, uint32_t refreshRate);
+
+// Returns the number of configs supported for the display on success.
+// Returns -1 on error.
+// Only primary display supported for now, value of dpy ignored.
+int getConfigCount(int dpy);
+
+// Returns the index of config that is current set for the display on success.
+// Returns -1 on error.
+// Only primary display supported for now, value of dpy ignored.
+int getActiveConfig(int dpy);
+
+// Sets the config for the display on success and returns 0.
+// Returns -1 on error.
+// Only primary display supported for now, value of dpy ignored
+int setActiveConfig(int configIndex, int dpy);
+
+// Returns the attributes for the specified config for the display on success.
+// Returns xres and yres as 0 on error.
+// Only primary display supported for now, value of dpy ignored
+DisplayAttributes getDisplayAttributes(int configIndex, int dpy);
+
+//=============================================================================
+// The functions and methods below run in the context of HWC and
+// are called in response to binder calls from clients
+
+class Configs {
+public:
+    DisplayAttributes getAttributes(const uint32_t& index) const;
+    uint32_t getActiveConfig() const;
+    bool setActiveConfig(const uint32_t& index);
+    uint32_t getConfigCount() const;
+    static Configs *getInstance();
+private:
+    enum { CONFIGS_MAX = 32 };
+    Configs();
+    bool init();
+    bool getCurrentMode(DisplayAttributes& dpyAttr);
+    DisplayAttributes mConfigs[CONFIGS_MAX];
+    char *mModeStr[CONFIGS_MAX];
+    uint32_t mActiveConfig;
+    uint32_t mConfigsSupported;
+    static Configs *sConfigs;
+};
+
+inline DisplayAttributes Configs::getAttributes(const uint32_t& index) const {
+    if(index >= mConfigsSupported) {
+        ALOGE("%s() Invalid index %d, max %d", __FUNCTION__, index,
+                mConfigsSupported);
+        return DisplayAttributes(); //All 0s
+    }
+    return mConfigs[index];
+}
+
+// Retuns the current config index, -1 if called without a setActiveConfig
+inline uint32_t Configs::getActiveConfig() const {
+    return mActiveConfig;
+}
+
+inline uint32_t Configs::getConfigCount() const {
+    return mConfigsSupported;
+}
+
 }; //namespace
diff --git a/libqdutils/mdp_version.h b/libqdutils/mdp_version.h
index aeb2218..ad92c83 100644
--- a/libqdutils/mdp_version.h
+++ b/libqdutils/mdp_version.h
@@ -152,11 +152,11 @@
     bool is8x16();
     bool is8x39();
     bool is8992();
+    bool updateSplitInfo();
 
 private:
     bool updateSysFsInfo();
     void updatePanelInfo();
-    bool updateSplitInfo();
     int tokenizeParams(char *inputParams, const char *delim,
                         char* tokenStr[], int *idx);
     int mFd;
diff --git a/libqservice/IQService.h b/libqservice/IQService.h
index 78cbd2a..8c63f1f 100644
--- a/libqservice/IQService.h
+++ b/libqservice/IQService.h
@@ -63,6 +63,10 @@
         SET_FRAME_DUMP_CONFIG = 21,  // Provides ability to set the frame dump config
         SET_S3D_MODE = 22, // Set the 3D mode as specified in msm_hdmi_modes.h
         CONNECT_HDMI_CLIENT = 23,  // Connect HDMI CEC HAL Client
+        SET_ACTIVE_CONFIG = 25, //Set a specified display config
+        GET_ACTIVE_CONFIG = 26, //Get the current config index
+        GET_CONFIG_COUNT = 27, //Get the number of supported display configs
+        GET_DISPLAY_ATTRIBUTES_FOR_CONFIG = 28, //Get attr for specified config
         COMMAND_LIST_END = 400,
     };