sde: resource config: rectangle alignment for video and rotator

1. Align rectangle to be even for video format input.
2. Align width and height for rotator downscaling case.
3. Validate scaling again after config is done because scaling ratio
might be changed.
4. Make sure layer count doesn't exceed mixer stage number.
5. Add sde.disable_rotator_downscaling property for disabling
rotator downscaling.
6. Add sde.disable_decimation property for disabling decimation
7. Disable 2 pipes on non-display_split case temporarily as z-order
needs be different

Change-Id: Iec20c292552b23ff1a4135a6be1695fa8fb737de
diff --git a/displayengine/include/utils/constants.h b/displayengine/include/utils/constants.h
index 6f98b07..329dd86 100644
--- a/displayengine/include/utils/constants.h
+++ b/displayengine/include/utils/constants.h
@@ -53,6 +53,9 @@
 #define MAX(a, b) (((a) > (b)) ? (a) : (b))
 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
 
+#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)))
+
 template <class T>
 inline void Swap(T &a, T &b) {
   T c(a);
diff --git a/displayengine/include/utils/debug.h b/displayengine/include/utils/debug.h
index 65079a9..5f65834 100644
--- a/displayengine/include/utils/debug.h
+++ b/displayengine/include/utils/debug.h
@@ -63,6 +63,8 @@
   static uint32_t GetSimulationFlag();
   static uint32_t GetHDMIResolution();
   static uint32_t GetIdleTimeoutMs();
+  static bool IsRotatorDownScaleDisabled();
+  static bool IsDecimationDisabled();
 
  private:
   Debug();
diff --git a/displayengine/include/utils/rect.h b/displayengine/include/utils/rect.h
index f009cfd..0730529 100644
--- a/displayengine/include/utils/rect.h
+++ b/displayengine/include/utils/rect.h
@@ -40,7 +40,7 @@
   bool IsValidRect(const LayerRect &rect);
   LayerRect GetIntersection(const LayerRect &rect1, const LayerRect &rect2);
   void LogRect(DebugTag debug_tag, const char *prefix, const LayerRect &roi);
-  void NormalizeRect(const uint32_t &factor, LayerRect *rect);
+  void NormalizeRect(const uint32_t &align_x, const uint32_t &align_y, LayerRect *rect);
 
 }  // namespace sde
 
diff --git a/displayengine/libs/core/Android.mk b/displayengine/libs/core/Android.mk
index 56167e1..c4c3d87 100644
--- a/displayengine/libs/core/Android.mk
+++ b/displayengine/libs/core/Android.mk
@@ -9,7 +9,7 @@
 LOCAL_CFLAGS                  := -Wno-missing-field-initializers -Wno-unused-parameter \
                                  -Wconversion -Wall -Werror \
                                  -DLOG_TAG=\"SDE\"
-LOCAL_SHARED_LIBRARIES        := libdl libsdeutils
+LOCAL_SHARED_LIBRARIES        := libdl libsdeutils libcutils
 LOCAL_ADDITIONAL_DEPENDENCIES := $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr
 LOCAL_SRC_FILES               := core_interface.cpp \
                                  core_impl.cpp \
diff --git a/displayengine/libs/core/res_config.cpp b/displayengine/libs/core/res_config.cpp
index d3c2d26..72edeb6 100644
--- a/displayengine/libs/core/res_config.cpp
+++ b/displayengine/libs/core/res_config.cpp
@@ -52,15 +52,29 @@
 
   // downscale when doing rotation
   if (IsRotationNeeded(transform.rotation)) {
+    if (rotate->downscale_ratio_x > 1.0f) {
+      src_height = ROUND_UP_ALIGN_DOWN(src_height, rotate->downscale_ratio_x);
+      src_rect->bottom = src_rect->top + src_height;
+    }
+    if (rotate->downscale_ratio_y > 1.0f) {
+      src_width = ROUND_UP_ALIGN_DOWN(src_width, rotate->downscale_ratio_y);
+      src_rect->right = src_rect->left + src_width;
+    }
     dst_rect.right = src_height / rotate->downscale_ratio_x;
     dst_rect.bottom = src_width / rotate->downscale_ratio_y;
   } else {
+    if (rotate->downscale_ratio_x > 1.0f) {
+      src_width = ROUND_UP_ALIGN_DOWN(src_width, rotate->downscale_ratio_x);
+      src_rect->right = src_rect->left + src_width;
+    }
+    if (rotate->downscale_ratio_y > 1.0f) {
+      src_height = ROUND_UP_ALIGN_DOWN(src_height, rotate->downscale_ratio_y);
+      src_rect->bottom = src_rect->top + src_height;
+    }
     dst_rect.right = src_width / rotate->downscale_ratio_x;
     dst_rect.bottom = src_height / rotate->downscale_ratio_y;
   }
 
-  dst_rect.right = floorf(dst_rect.right);
-  dst_rect.bottom = floorf(dst_rect.bottom);
   rotate->src_roi = *src_rect;
   rotate->valid = true;
   rotate->dst_roi = dst_rect;
@@ -72,7 +86,8 @@
 
 DisplayError ResManager::SrcSplitConfig(DisplayResourceContext *display_resource_ctx,
                                         const LayerTransform &transform, const LayerRect &src_rect,
-                                        const LayerRect &dst_rect, HWLayerConfig *layer_config) {
+                                        const LayerRect &dst_rect, HWLayerConfig *layer_config,
+                                        uint32_t align_x) {
   HWDisplayAttributes &display_attributes = display_resource_ctx->display_attributes;
   HWPipeInfo *left_pipe = &layer_config->left_pipe;
   HWPipeInfo *right_pipe = &layer_config->right_pipe;
@@ -80,7 +95,7 @@
   if ((src_rect.right - src_rect.left) > kMaxSourcePipeWidth ||
       (dst_rect.right - dst_rect.left) > kMaxInterfaceWidth || hw_res_info_.always_src_split) {
     SplitRect(transform.flip_horizontal, src_rect, dst_rect, &left_pipe->src_roi,
-              &left_pipe->dst_roi, &right_pipe->src_roi, &right_pipe->dst_roi);
+              &left_pipe->dst_roi, &right_pipe->src_roi, &right_pipe->dst_roi, align_x);
     left_pipe->valid = true;
     right_pipe->valid = true;
   } else {
@@ -96,7 +111,7 @@
 DisplayError ResManager::DisplaySplitConfig(DisplayResourceContext *display_resource_ctx,
                                             const LayerTransform &transform,
                                             const LayerRect &src_rect, const LayerRect &dst_rect,
-                                            HWLayerConfig *layer_config) {
+                                            HWLayerConfig *layer_config, uint32_t align_x) {
   LayerRect scissor_dst_left, scissor_dst_right;
   HWDisplayAttributes &display_attributes = display_resource_ctx->display_attributes;
 
@@ -111,36 +126,43 @@
   dst_left = dst_rect;
   crop_right = crop_left;
   dst_right = dst_left;
-  CalculateCropRects(scissor, transform, &crop_left, &dst_left);
+  bool crop_left_valid = CalculateCropRects(scissor, transform, &crop_left, &dst_left);
 
   scissor.left = FLOAT(display_attributes.split_left);
   scissor.top = 0.0f;
   scissor.right = FLOAT(display_attributes.x_pixels);
   scissor.bottom = FLOAT(display_attributes.y_pixels);
-  CalculateCropRects(scissor, transform, &crop_right, &dst_right);
-  if ((crop_left.right - crop_left.left) > kMaxSourcePipeWidth) {
-    if (crop_right.right != crop_right.left) {
+  bool crop_right_valid = false;
+
+  if (IsValidRect(scissor)) {
+    crop_right_valid = CalculateCropRects(scissor, transform, &crop_right, &dst_right);
+  }
+
+  if (crop_left_valid && (crop_left.right - crop_left.left) > kMaxSourcePipeWidth) {
+    if (crop_right_valid) {
       DLOGV_IF(kTagResources, "Need more than 2 pipes: left width = %.0f, right width = %.0f",
                crop_left.right - crop_left.left, crop_right.right - crop_right.left);
       return kErrorNotSupported;
     }
     // 2 pipes both are on the left
     SplitRect(transform.flip_horizontal, crop_left, dst_left, &left_pipe->src_roi,
-              &left_pipe->dst_roi, &right_pipe->src_roi, &right_pipe->dst_roi);
+              &left_pipe->dst_roi, &right_pipe->src_roi, &right_pipe->dst_roi, align_x);
     left_pipe->valid = true;
     right_pipe->valid = true;
-  } else if ((crop_right.right - crop_right.left) > kMaxSourcePipeWidth) {
-    if (crop_left.right != crop_left.left) {
+    crop_right_valid = true;
+  } else if (crop_right_valid && (crop_right.right - crop_right.left) > kMaxSourcePipeWidth) {
+    if (crop_left_valid) {
       DLOGV_IF(kTagResources, "Need more than 2 pipes: left width = %.0f, right width = %.0f",
                crop_left.right - crop_left.left, crop_right.right - crop_right.left);
       return kErrorNotSupported;
     }
     // 2 pipes both are on the right
     SplitRect(transform.flip_horizontal, crop_right, dst_right, &left_pipe->src_roi,
-              &left_pipe->dst_roi, &right_pipe->src_roi, &right_pipe->dst_roi);
+              &left_pipe->dst_roi, &right_pipe->src_roi, &right_pipe->dst_roi, align_x);
     left_pipe->valid = true;
     right_pipe->valid = true;
-  } else if (UINT32(dst_left.right) > UINT32(dst_left.left)) {
+    crop_left_valid = true;
+  } else if (crop_left_valid) {
     // assign left pipe
     left_pipe->src_roi = crop_left;
     left_pipe->dst_roi = dst_left;
@@ -151,7 +173,7 @@
   }
 
   // assign right pipe if needed
-  if (UINT32(dst_right.right) > UINT32(dst_right.left)) {
+  if (crop_right_valid) {
     if (left_pipe->valid) {
       right_pipe->src_roi = crop_right;
       right_pipe->dst_roi = dst_right;
@@ -168,6 +190,12 @@
     right_pipe->Reset();
   }
 
+  // TODO(user) remove this when zorder is assigned properly, check num_blending_stages on pipes
+  if (!display_attributes.is_device_split && right_pipe->valid) {
+    DLOGV_IF(kTagResources, "Non display split, do not support 2 pipes for now");
+    return kErrorNotSupported;
+  }
+
   return kErrorNone;
 }
 
@@ -193,14 +221,30 @@
     dst_rect = layer.dst_rect;
     scissor.right = FLOAT(display_attributes.x_pixels);
     scissor.bottom = FLOAT(display_attributes.y_pixels);
-    CalculateCropRects(scissor, layer.transform, &src_rect, &dst_rect);
-
-    if (ValidateScaling(layer, src_rect, dst_rect, &rot_scale_x, &rot_scale_y))
-      return kErrorNotSupported;
 
     struct HWLayerConfig *layer_config = &hw_layers->config[i];
     HWPipeInfo &left_pipe = layer_config->left_pipe;
     HWPipeInfo &right_pipe = layer_config->right_pipe;
+
+    if (!CalculateCropRects(scissor, layer.transform, &src_rect, &dst_rect)) {
+      layer_config->Reset();
+      left_pipe.Reset();
+      right_pipe.Reset();
+      continue;
+    }
+
+    uint32_t align_x = 1, align_y = 1;
+    if (IsYuvFormat(layer.input_buffer->format)) {
+      // TODO(user) Select x and y alignment according to the format
+      align_x = 2;
+      align_y = 2;
+      NormalizeRect(align_x, align_y, &src_rect);
+    }
+
+    if (ValidateScaling(layer, src_rect, dst_rect, &rot_scale_x, &rot_scale_y)) {
+      return kErrorNotSupported;
+    }
+
     // config rotator first
     for (uint32_t j = 0; j < kMaxRotatePerLayer; j++) {
       layer_config->rotates[j].Reset();
@@ -218,31 +262,23 @@
 
     if (hw_res_info_.is_src_split) {
       error = SrcSplitConfig(display_resource_ctx, transform, src_rect,
-                             dst_rect, layer_config);
+                             dst_rect, layer_config, align_x);
     } else {
       error = DisplaySplitConfig(display_resource_ctx, transform, src_rect,
-                                 dst_rect, layer_config);
+                                 dst_rect, layer_config, align_x);
     }
 
-    if (error != kErrorNone)
+    if (error != kErrorNone) {
       break;
-
-    // 1. Normalize Video layer source rectangle to multiple of 2, as MDP hardware require source
-    //    rectangle of video layer to be even.
-    // 2. Normalize source and destination rect of a layer to multiple of 1.
-    uint32_t factor = (1 << layer.input_buffer->flags.video);
-    if (left_pipe.valid) {
-      NormalizeRect(factor, &left_pipe.src_roi);
-      NormalizeRect(1, &left_pipe.dst_roi);
     }
 
-    if (right_pipe.valid) {
-      NormalizeRect(factor, &right_pipe.src_roi);
-      NormalizeRect(1, &right_pipe.dst_roi);
+    error = AlignPipeConfig(layer, transform, &left_pipe, &right_pipe, align_x, align_y);
+    if (error != kErrorNone) {
+      break;
     }
 
-    DLOGV_IF(kTagResources, "layer = %d, left pipe_id = %x",
-             i, layer_config->left_pipe.pipe_id);
+    DLOGV_IF(kTagResources, "==== layer = %d, left pipe valid = %d ====",
+             i, layer_config->left_pipe.valid);
     LogRect(kTagResources, "input layer src_rect", layer.src_rect);
     LogRect(kTagResources, "input layer dst_rect", layer.dst_rect);
     for (uint32_t k = 0; k < layer_config->num_rotate; k++) {
@@ -251,11 +287,12 @@
       LogRect(kTagResources, "rotate src", layer_config->rotates[k].src_roi);
       LogRect(kTagResources, "rotate dst", layer_config->rotates[k].dst_roi);
     }
+
     LogRect(kTagResources, "cropped src_rect", src_rect);
     LogRect(kTagResources, "cropped dst_rect", dst_rect);
     LogRect(kTagResources, "left pipe src", layer_config->left_pipe.src_roi);
     LogRect(kTagResources, "left pipe dst", layer_config->left_pipe.dst_roi);
-    if (hw_layers->config[i].right_pipe.pipe_id) {
+    if (hw_layers->config[i].right_pipe.valid) {
       LogRect(kTagResources, "right pipe src", layer_config->right_pipe.src_roi);
       LogRect(kTagResources, "right pipe dst", layer_config->right_pipe.dst_roi);
     }
@@ -267,50 +304,52 @@
 DisplayError ResManager::ValidateScaling(const Layer &layer, const LayerRect &crop,
                                          const LayerRect &dst, float *rot_scale_x,
                                          float *rot_scale_y) {
-  bool rotated90 = IsRotationNeeded(layer.transform.rotation);
+  bool rotated90 = IsRotationNeeded(layer.transform.rotation) && (rot_scale_x != NULL);
   float crop_width = rotated90 ? crop.bottom - crop.top : crop.right - crop.left;
   float crop_height = rotated90 ? crop.right - crop.left : crop.bottom - crop.top;
   float dst_width = dst.right - dst.left;
   float dst_height = dst.bottom - dst.top;
 
   if ((dst_width < 1.0f) || (dst_height < 1.0f)) {
-    DLOGV_IF(kTagResources, "Destination region is too small w = %d, h = %d",
-    dst_width, dst_height);
+    DLOGV_IF(kTagResources, "dst ROI is too small w = %.0f, h = %.0f, right = %.0f, bottom = %.0f",
+             dst_width, dst_height, dst.right, dst.bottom);
     return kErrorNotSupported;
   }
 
   if ((crop_width < 1.0f) || (crop_height < 1.0f)) {
-    DLOGV_IF(kTagResources, "source region is too small w = %d, h = %d", crop_width, crop_height);
+    DLOGV_IF(kTagResources, "src ROI is too small w = %.0f, h = %.0f, right = %.0f, bottom = %.0f",
+             crop_width, crop_height, crop.right, crop.bottom);
     return kErrorNotSupported;
   }
 
-  if (((crop_width - dst_width) == 1) || ((crop_height - dst_height) == 1)) {
-    DLOGV_IF(kTagResources, "One pixel downscaling detected crop_w %d, dst_w %d, crop_h %d, " \
-             "dst_h %d", crop_width, dst_width, crop_height, dst_height);
+  if ((UINT32(crop_width - dst_width) == 1) || (UINT32(crop_height - dst_height) == 1)) {
+    DLOGV_IF(kTagResources, "One pixel downscaling detected crop_w = %.0f, dst_w = %.0f, " \
+             "crop_h = %.0f, dst_h = %.0f", crop_width, dst_width, crop_height, dst_height);
     return kErrorNotSupported;
   }
 
   float scale_x = crop_width / dst_width;
   float scale_y = crop_height / dst_height;
+  bool use_rotator = false;
 
   if ((UINT32(scale_x) > 1) || (UINT32(scale_y) > 1)) {
-    const uint32_t max_scale_down = hw_res_info_.max_scale_down;
-    uint32_t max_downscale_with_rotator;
+    uint32_t max_scale_down = hw_res_info_.max_scale_down;
 
-    if (hw_res_info_.has_rotator_downscale)
-      max_downscale_with_rotator = max_scale_down * kMaxRotateDownScaleRatio;
-    else
-      max_downscale_with_rotator = max_scale_down;
+    if (hw_res_info_.has_rotator_downscale && !property_setting_.disable_rotator_downscaling &&
+        rot_scale_x) {
+      max_scale_down *= kMaxRotateDownScaleRatio;
+      use_rotator = true;
+    } else if (hw_res_info_.has_decimation && !property_setting_.disable_decimation &&
+               !IsMacroTileFormat(layer.input_buffer)) {
+      max_scale_down *= kMaxDecimationDownScaleRatio;
+    }
 
-    if (((!hw_res_info_.has_decimation) || (IsMacroTileFormat(layer.input_buffer))) &&
-        (scale_x > max_scale_down || scale_y > max_scale_down)) {
+    if (scale_x > FLOAT(max_scale_down) || scale_y > FLOAT(max_scale_down)) {
       DLOGV_IF(kTagResources,
-               "Scaling down is over the limit is_tile = %d, scale_x = %d, scale_y = %d",
-               IsMacroTileFormat(layer.input_buffer), scale_x, scale_y);
-      return kErrorNotSupported;
-    } else if (scale_x > max_downscale_with_rotator || scale_y > max_downscale_with_rotator) {
-      DLOGV_IF(kTagResources, "Scaling down is over the limit scale_x = %d, scale_y = %d",
-               scale_x, scale_y);
+               "Scaling down is over the limit: is_tile = %d, scale_x = %.0f, scale_y = %.0f, " \
+               "crop_w = %.0f, dst_w = %.0f, has_deci = %d, disable_deci = %d",
+               IsMacroTileFormat(layer.input_buffer), scale_x, scale_y, crop_width, dst_width,
+               hw_res_info_.has_decimation, property_setting_.disable_decimation);
       return kErrorNotSupported;
     }
   }
@@ -318,33 +357,36 @@
   const uint32_t max_scale_up = hw_res_info_.max_scale_up;
   if (UINT32(scale_x) < 1 && scale_x > 0.0f) {
     if ((1.0f / scale_x) > max_scale_up) {
-      DLOGV_IF(kTagResources, "Scaling up is over limit scale_x = %d", 1.0f / scale_x);
+      DLOGV_IF(kTagResources, "Scaling up is over limit scale_x = %f", 1.0f / scale_x);
       return kErrorNotSupported;
     }
   }
 
   if (UINT32(scale_y) < 1 && scale_y > 0.0f) {
     if ((1.0f / scale_y) > max_scale_up) {
-      DLOGV_IF(kTagResources, "Scaling up is over limit scale_y = %d", 1.0f / scale_y);
+      DLOGV_IF(kTagResources, "Scaling up is over limit scale_y = %f", 1.0f / scale_y);
       return kErrorNotSupported;
     }
   }
 
-  // Calculate rotator downscale ratio
-  float rot_scale = 1.0f;
-  while (scale_x > hw_res_info_.max_scale_down) {
-    scale_x /= 2;
-    rot_scale *= 2;
+  if (rot_scale_x == NULL) {
+    return kErrorNone;
   }
-  *rot_scale_x = rot_scale;
 
-  rot_scale = 1.0f;
-  while (scale_y > hw_res_info_.max_scale_down) {
-    scale_y /= 2;
-    rot_scale *= 2;
+  // Calculate rotator downscale ratio
+  if (scale_x > hw_res_info_.max_scale_down && use_rotator) {
+    *rot_scale_x = FLOAT(1 << UINT32(ceilf(log2f(scale_x / FLOAT(hw_res_info_.max_scale_down)))));
+  } else {
+    *rot_scale_x = 1.0f;
   }
-  *rot_scale_y = rot_scale;
-  DLOGV_IF(kTagResources, "rotator scaling hor = %.0f, ver = %.0f", *rot_scale_x, *rot_scale_y);
+
+  if (scale_y > hw_res_info_.max_scale_down && use_rotator) {
+    *rot_scale_y = FLOAT(1 << UINT32(ceilf(log2f(scale_y / FLOAT(hw_res_info_.max_scale_down)))));
+  } else {
+    *rot_scale_y = 1.0f;
+  }
+  DLOGV_IF(kTagResources, "scale_x = %.0f, scale_y = %.0f, rot_scale_x = %.0f, rot_scale_y = %.0f",
+           scale_x, scale_y, *rot_scale_x, *rot_scale_y);
 
   return kErrorNone;
 }
@@ -370,7 +412,7 @@
   }
 }
 
-void ResManager::CalculateCropRects(const LayerRect &scissor, const LayerTransform &transform,
+bool ResManager::CalculateCropRects(const LayerRect &scissor, const LayerTransform &transform,
                                     LayerRect *crop, LayerRect *dst) {
   float &crop_left = crop->left;
   float &crop_top = crop->top;
@@ -419,7 +461,7 @@
   }
 
   if (!need_cut)
-    return;
+    return true;
 
   CalculateCut(transform, &left_cut_ratio, &top_cut_ratio, &right_cut_ratio, &bottom_cut_ratio);
 
@@ -427,6 +469,12 @@
   crop_top += crop_height * top_cut_ratio;
   crop_right -= crop_width * right_cut_ratio;
   crop_bottom -= crop_height * bottom_cut_ratio;
+  NormalizeRect(1, 1, crop);
+  NormalizeRect(1, 1, dst);
+  if (IsValidRect(*crop) && IsValidRect(*dst))
+    return true;
+  else
+    return false;
 }
 
 bool ResManager::IsValidDimension(const LayerRect &src, const LayerRect &dst) {
@@ -468,8 +516,9 @@
     return kErrorNotSupported;
   }
 
-  if ((down_scale_w <= max_down_scale) && (down_scale_h <= max_down_scale))
+  if ((down_scale_w <= max_down_scale) && (down_scale_h <= max_down_scale)) {
     return kErrorNone;
+  }
 
   // Decimation is the remaining downscale factor after doing max SDE downscale.
   // In SDE, decimation is supported in powers of 2.
@@ -486,18 +535,22 @@
 
 void ResManager::SplitRect(bool flip_horizontal, const LayerRect &src_rect,
                            const LayerRect &dst_rect, LayerRect *src_left, LayerRect *dst_left,
-                           LayerRect *src_right, LayerRect *dst_right) {
+                           LayerRect *src_right, LayerRect *dst_right, uint32_t align_x) {
   // Split rectangle horizontally and evenly into two.
   float src_width = src_rect.right - src_rect.left;
   float dst_width = dst_rect.right - dst_rect.left;
+  float src_width_ori = src_width;
+  src_width = ROUND_UP_ALIGN_DOWN(src_width / 2, align_x);
+  dst_width = ROUND_UP_ALIGN_DOWN(dst_width * src_width / src_width_ori, 1);
+
   if (flip_horizontal) {
     src_left->top = src_rect.top;
     src_left->left = src_rect.left;
-    src_left->right = src_rect.left + (src_width / 2);
+    src_left->right = src_rect.left + src_width;
     src_left->bottom = src_rect.bottom;
 
     dst_left->top = dst_rect.top;
-    dst_left->left = dst_rect.left + (dst_width / 2);
+    dst_left->left = dst_rect.left + dst_width;
     dst_left->right = dst_rect.right;
     dst_left->bottom = dst_rect.bottom;
 
@@ -513,12 +566,12 @@
   } else {
     src_left->top = src_rect.top;
     src_left->left = src_rect.left;
-    src_left->right = src_rect.left + (src_width / 2);
+    src_left->right = src_rect.left + src_width;
     src_left->bottom = src_rect.bottom;
 
     dst_left->top = dst_rect.top;
     dst_left->left = dst_rect.left;
-    dst_left->right = dst_rect.left + (dst_width / 2);
+    dst_left->right = dst_rect.left + dst_width;
     dst_left->bottom = dst_rect.bottom;
 
     src_right->top = src_rect.top;
@@ -652,4 +705,49 @@
   return true;
 }
 
+DisplayError ResManager::AlignPipeConfig(const Layer &layer, const LayerTransform &transform,
+                                         HWPipeInfo *left_pipe, HWPipeInfo *right_pipe,
+                                         uint32_t align_x, uint32_t align_y) {
+  DisplayError error = kErrorNone;
+  if (!left_pipe->valid) {
+    DLOGE_IF(kTagResources, "left_pipe should not be invalid");
+    return kErrorNotSupported;
+  }
+  // 1. Normalize video layer source rectangle to multiple of 2, as MDP hardware require source
+  //    rectangle of video layer to be even.
+  // 2. Normalize source and destination rect of a layer to multiple of 1.
+  // TODO(user) Check buffer format and check if rotate is involved.
+
+  NormalizeRect(align_x, align_y, &left_pipe->src_roi);
+  NormalizeRect(1, 1, &left_pipe->dst_roi);
+
+  if (right_pipe->valid) {
+    NormalizeRect(align_x, align_y, &right_pipe->src_roi);
+    NormalizeRect(1, 1, &right_pipe->dst_roi);
+  }
+
+  if (right_pipe->valid) {
+    // Make sure the  left and right ROI are conjunct
+    right_pipe->src_roi.left = left_pipe->src_roi.right;
+    if (transform.flip_horizontal) {
+      right_pipe->dst_roi.right = left_pipe->dst_roi.left;
+    } else {
+      right_pipe->dst_roi.left = left_pipe->dst_roi.right;
+    }
+  }
+  error = ValidateScaling(layer, left_pipe->src_roi, left_pipe->dst_roi, NULL, NULL);
+  if (error != kErrorNone) {
+    goto PipeConfigExit;
+  }
+
+  if (right_pipe->valid) {
+    error = ValidateScaling(layer, right_pipe->src_roi, right_pipe->dst_roi, NULL, NULL);
+  }
+PipeConfigExit:
+  if (error != kErrorNone) {
+    DLOGV_IF(kTagResources, "AlignPipeConfig failed");
+  }
+  return error;
+}
+
 }  // namespace sde
diff --git a/displayengine/libs/core/res_manager.cpp b/displayengine/libs/core/res_manager.cpp
index fb8f2eb..761ab01 100644
--- a/displayengine/libs/core/res_manager.cpp
+++ b/displayengine/libs/core/res_manager.cpp
@@ -248,6 +248,9 @@
     }
   }
 
+  property_setting_.disable_rotator_downscaling = Debug::IsRotatorDownScaleDisabled();
+  property_setting_.disable_decimation = Debug::IsDecimationDisabled();
+
   return kErrorNone;
 }
 
@@ -266,20 +269,23 @@
 
   DisplayError error = kErrorNone;
   const struct HWLayersInfo &layer_info = hw_layers->info;
+  HWBlockType hw_block_id = display_resource_ctx->hw_block_id;
 
-  if (layer_info.count > num_pipe_) {
+  DLOGV_IF(kTagResources, "==== Resource reserving start: hw_block = %d ====", hw_block_id);
+
+  if (layer_info.count > num_pipe_ || layer_info.count >= hw_res_info_.num_blending_stages) {
     return kErrorResources;
   }
 
   uint32_t rotate_count = 0;
   error = Config(display_resource_ctx, hw_layers, &rotate_count);
   if (error != kErrorNone) {
+    DLOGV_IF(kTagResources, "Resource config failed");
     return error;
   }
 
   uint32_t left_index = kPipeIdMax;
   bool need_scale = false;
-  HWBlockType hw_block_id = display_resource_ctx->hw_block_id;
   HWBlockType rotator_block = kHWBlockMax;
 
   // Clear reserved marking
@@ -319,6 +325,10 @@
       need_scale = IsScalingNeeded(pipe_info);
       left_index = GetPipe(hw_block_id, is_yuv, need_scale, false, use_non_dma_pipe);
       if (left_index >= num_pipe_) {
+        DLOGV_IF(kTagResources, "Get left pipe failed: hw_block_id = %d, is_yuv = %d, " \
+                 "need_scale = %d, use_non_dma_pipe= %d",
+                 hw_block_id, is_yuv, need_scale, use_non_dma_pipe);
+        ResourceStateLog();
         goto CleanupOnError;
       }
       src_pipes_[left_index].reserved_hw_block = hw_block_id;
@@ -336,6 +346,8 @@
         layer_config.left_pipe.pipe_id = src_pipes_[left_index].mdss_pipe_id;
         src_pipes_[left_index].at_right = false;
       }
+      DLOGV_IF(kTagResources, "1 pipe acquired, layer index = %d, left_pipe = %x",
+               i, layer_config.left_pipe.pipe_id);
       continue;
     }
 
@@ -344,6 +356,10 @@
     uint32_t right_index;
     right_index = GetPipe(hw_block_id, is_yuv, need_scale, true, use_non_dma_pipe);
     if (right_index >= num_pipe_) {
+      DLOGV_IF(kTagResources, "Get right pipe failed: hw_block_id = %d, is_yuv = %d, " \
+               "need_scale = %d, use_non_dma_pipe= %d",
+               hw_block_id, is_yuv, need_scale, use_non_dma_pipe);
+      ResourceStateLog();
       goto CleanupOnError;
     }
 
@@ -364,8 +380,8 @@
       goto CleanupOnError;
     }
 
-    DLOGV_IF(kTagResources, "Pipe acquired, layer index = %d, left_pipe = %x, right_pipe = %x",
-            i, layer_config.left_pipe.pipe_id,  pipe_info->pipe_id);
+    DLOGV_IF(kTagResources, "2 pipes acquired, layer index = %d, left_pipe = %x, right_pipe = %x",
+             i, layer_config.left_pipe.pipe_id,  pipe_info->pipe_id);
   }
 
   error = AllocRotatorBuffer(display_ctx, hw_layers);
@@ -802,6 +818,7 @@
       src_pipes_[i].ResetState();
   }
   ClearRotator(display_resource_ctx);
+  DLOGV_IF(kTagResources, "display id = %d", display_resource_ctx->hw_block_id);
 }
 
 uint32_t ResManager::GetMdssPipeId(PipeType type, uint32_t index) {
@@ -973,6 +990,27 @@
   }
 }
 
+void ResManager::ResourceStateLog() {
+  DLOGV_IF(kTagResources, "==== resource manager pipe state ====");
+  uint32_t i;
+  for (i = 0; i < num_pipe_; i++) {
+    SourcePipe *src_pipe = &src_pipes_[i];
+    DLOGV_IF(kTagResources,
+             "index = %d, id = %x, reserved = %d, state = %d, hw_block = %d, dedicated = %d",
+             src_pipe->index, src_pipe->mdss_pipe_id, src_pipe->reserved_hw_block,
+             src_pipe->state, src_pipe->hw_block_id, src_pipe->dedicated_hw_block);
+  }
+
+  for (i = 0; i < hw_res_info_.num_rotator; i++) {
+    if (rotators_[i].client_bit_mask || rotators_[i].request_bit_mask) {
+      DLOGV_IF(kTagResources,
+               "rotator = %d, pipe index = %x, client_bit_mask = %x, request_bit_mask = %x",
+               i, rotators_[i].pipe_index, rotators_[i].client_bit_mask,
+               rotators_[i].request_bit_mask);
+    }
+  }
+}
+
 DisplayError ResManager::AcquireRotator(DisplayResourceContext *display_resource_ctx,
                                         const uint32_t rotate_count) {
   if (rotate_count == 0)
diff --git a/displayengine/libs/core/res_manager.h b/displayengine/libs/core/res_manager.h
index c80dc2c..eae6730 100644
--- a/displayengine/libs/core/res_manager.h
+++ b/displayengine/libs/core/res_manager.h
@@ -88,6 +88,7 @@
     kMaxSourcePipeWidth = 2048,
     kMaxInterfaceWidth = 2048,
     kMaxRotateDownScaleRatio = 8,
+    kMaxDecimationDownScaleRatio = 8,
     kMaxNumRotator = 2,
   };
 
@@ -150,7 +151,11 @@
         CLEAR_BIT(request_bit_mask, block); }
   };
 
-  static const int kPipeIdNeedsAssignment = -1;
+  struct PropertySetting {
+    bool disable_rotator_downscaling;
+    bool disable_decimation;
+    PropertySetting() : disable_rotator_downscaling(false), disable_decimation(false) { }
+  };
 
   uint32_t GetMdssPipeId(PipeType pipe_type, uint32_t index);
   uint32_t NextPipe(PipeType pipe_type, HWBlockType hw_block_id, bool at_right);
@@ -163,15 +168,17 @@
                       uint32_t *rotate_count);
   DisplayError DisplaySplitConfig(DisplayResourceContext *display_resource_ctx,
                                   const LayerTransform &transform, const LayerRect &src_rect,
-                                  const LayerRect &dst_rect, HWLayerConfig *layer_config);
+                                  const LayerRect &dst_rect, HWLayerConfig *layer_config,
+                                  uint32_t align_x);
   DisplayError ValidateScaling(const Layer &layer, const LayerRect &crop,
                                const LayerRect &dst, float *rot_scale_x, float *rot_scale_y);
   DisplayError SrcSplitConfig(DisplayResourceContext *display_resource_ctx,
                               const LayerTransform &transform, const LayerRect &src_rect,
-                              const LayerRect &dst_rect, HWLayerConfig *layer_config);
+                              const LayerRect &dst_rect, HWLayerConfig *layer_config,
+                              uint32_t align_x);
   void CalculateCut(const LayerTransform &transform, float *left_cut_ratio, float *top_cut_ratio,
                     float *right_cut_ratio, float *bottom_cut_ratio);
-  void CalculateCropRects(const LayerRect &scissor, const LayerTransform &transform,
+  bool CalculateCropRects(const LayerRect &scissor, const LayerTransform &transform,
                           LayerRect *crop, LayerRect *dst);
   bool IsValidDimension(const LayerRect &src, const LayerRect &dst);
   bool CheckBandwidth(DisplayResourceContext *display_ctx, HWLayers *hw_layers);
@@ -182,7 +189,7 @@
   float GetBpp(LayerBufferFormat format);
   void SplitRect(bool flip_horizontal, const LayerRect &src_rect, const LayerRect &dst_rect,
                  LayerRect *src_left, LayerRect *dst_left, LayerRect *src_right,
-                 LayerRect *dst_right);
+                 LayerRect *dst_right, uint32_t align_x);
   bool IsMacroTileFormat(const LayerBuffer *buffer) { return buffer->flags.macro_tile; }
   bool IsYuvFormat(LayerBufferFormat format) { return (format >= kFormatYCbCr420Planar); }
   bool IsRotationNeeded(float rotation)
@@ -198,6 +205,10 @@
   void SetRotatorOutputFormat(const LayerBufferFormat &input_format, bool bwc, bool rot90,
                               LayerBufferFormat *output_format);
   bool ConfigureScaling(HWLayers *hw_layers);
+  DisplayError AlignPipeConfig(const Layer &layer, const LayerTransform &transform,
+                               HWPipeInfo *left_pipe, HWPipeInfo *right_pipe,
+                               uint32_t align_x, uint32_t align_y);
+  void ResourceStateLog(void);
 
   Locker locker_;
   HWResourceInfo hw_res_info_;
@@ -218,6 +229,7 @@
                                             // the display engine's client
   static void* lib_scalar_handle_;  // Scalar library handle
   static int (*ScalarConfigureScale)(struct scalar::LayerInfo* layer);
+  PropertySetting property_setting_;
 };
 
 }  // namespace sde
diff --git a/displayengine/libs/utils/debug_android.cpp b/displayengine/libs/utils/debug_android.cpp
index 1b08fe9..7503999 100644
--- a/displayengine/libs/utils/debug_android.cpp
+++ b/displayengine/libs/utils/debug_android.cpp
@@ -70,5 +70,23 @@
   return 0;
 }
 
+bool Debug::IsRotatorDownScaleDisabled() {
+  char property[PROPERTY_VALUE_MAX];
+  if (property_get("sde.disable_rotator_downscaling", property, NULL) > 0) {
+    return (atoi(property) ? 0 : false, true);
+  }
+
+  return false;
+}
+
+bool Debug::IsDecimationDisabled() {
+  char property[PROPERTY_VALUE_MAX];
+  if (property_get("sde.disable_decimation", property, NULL) > 0) {
+    return (atoi(property) ? 0 : false, true);
+  }
+
+  return false;
+}
+
 }  // namespace sde
 
diff --git a/displayengine/libs/utils/rect.cpp b/displayengine/libs/utils/rect.cpp
index 2725750..8dfd7dd 100644
--- a/displayengine/libs/utils/rect.cpp
+++ b/displayengine/libs/utils/rect.cpp
@@ -64,16 +64,11 @@
            prefix, roi.left, roi.top, roi.right, roi.bottom);
 }
 
-void NormalizeRect(const uint32_t &factor, LayerRect *rect) {
-  uint32_t left = UINT32(ceilf(rect->left));
-  uint32_t top = UINT32(ceilf(rect->top));
-  uint32_t right = UINT32(floorf(rect->right));
-  uint32_t bottom = UINT32(floorf(rect->bottom));
-
-  rect->left = FLOAT(CeilToMultipleOf(left, factor));
-  rect->top = FLOAT(CeilToMultipleOf(top, factor));
-  rect->right = FLOAT(FloorToMultipleOf(right, factor));
-  rect->bottom = FLOAT(FloorToMultipleOf(bottom, factor));
+void NormalizeRect(const uint32_t &align_x, const uint32_t &align_y, LayerRect *rect) {
+  rect->left = ROUND_UP_ALIGN_UP(rect->left, align_x);
+  rect->right = ROUND_UP_ALIGN_DOWN(rect->right, align_x);
+  rect->top = ROUND_UP_ALIGN_UP(rect->top, align_y);
+  rect->bottom = ROUND_UP_ALIGN_DOWN(rect->bottom, align_y);
 }
 
 }  // namespace sde