Merge "QService: Fix binder out-parcel position as well"
diff --git a/Android.mk b/Android.mk
index a128327..5070911 100644
--- a/Android.mk
+++ b/Android.mk
@@ -5,6 +5,7 @@
 endif
 
 display-hals := libgralloc libcopybit liblight libmemtrack libqservice libqdutils
+display-hals += hdmi_cec
 
 ifeq ($(TARGET_USES_SDE), true)
     sde-libs := displayengine/libs
diff --git a/displayengine/include/core/debug_interface.h b/displayengine/include/core/debug_interface.h
index c90e1ff..ff72321 100644
--- a/displayengine/include/core/debug_interface.h
+++ b/displayengine/include/core/debug_interface.h
@@ -48,6 +48,7 @@
   kTagDriverConfig,     //!< Debug log is tagged for driver config.
   kTagBufferManager,    //!< Debug log is tagged for buffer manager state transitions.
   kTagOfflineCtrl,      //!< Debug log is tagged for offline controller.
+  kTagScalar,           //!< Debug log is tagged for Scalar Helper.
 };
 
 /*! @brief Display debug handler class.
diff --git a/displayengine/include/utils/constants.h b/displayengine/include/utils/constants.h
index 6f98b07..447b22a 100644
--- a/displayengine/include/utils/constants.h
+++ b/displayengine/include/utils/constants.h
@@ -33,6 +33,7 @@
 #define INT(exp) static_cast<int>(exp)
 #define FLOAT(exp) static_cast<float>(exp)
 #define UINT8(exp) static_cast<uint8_t>(exp)
+#define UINT16(exp) static_cast<uint16_t>(exp)
 #define UINT32(exp) static_cast<uint32_t>(exp)
 #define INT32(exp) static_cast<int32_t>(exp)
 
@@ -53,6 +54,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..b7d2478 100644
--- a/displayengine/libs/core/Android.mk
+++ b/displayengine/libs/core/Android.mk
@@ -4,12 +4,15 @@
 LOCAL_MODULE                  := libsde
 LOCAL_MODULE_TAGS             := optional
 LOCAL_C_INCLUDES              := hardware/qcom/display/displayengine/include/ \
-                                 $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include \
-                                 $(TARGET_OUT_HEADERS)/scalar/inc
+                                 $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include
 LOCAL_CFLAGS                  := -Wno-missing-field-initializers -Wno-unused-parameter \
                                  -Wconversion -Wall -Werror \
                                  -DLOG_TAG=\"SDE\"
-LOCAL_SHARED_LIBRARIES        := libdl libsdeutils
+ifeq ($(TARGET_USES_SCALAR), true)
+  LOCAL_C_INCLUDES  += $(TARGET_OUT_HEADERS)/scalar/inc
+  LOCAL_CFLAGS      += -DUSES_SCALAR
+endif
+LOCAL_SHARED_LIBRARIES        := libdl libsdeutils libcutils
 LOCAL_ADDITIONAL_DEPENDENCIES := $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr
 LOCAL_SRC_FILES               := core_interface.cpp \
                                  core_impl.cpp \
@@ -25,6 +28,7 @@
                                  hw_interface.cpp \
                                  hw_framebuffer.cpp \
                                  dump_impl.cpp \
-                                 buffer_manager.cpp
+                                 buffer_manager.cpp \
+                                 scalar_helper.cpp
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/displayengine/libs/core/comp_manager.cpp b/displayengine/libs/core/comp_manager.cpp
index 7e888ee..06028a2 100644
--- a/displayengine/libs/core/comp_manager.cpp
+++ b/displayengine/libs/core/comp_manager.cpp
@@ -144,6 +144,10 @@
   DisplayCompositionContext *display_comp_ctx =
                              reinterpret_cast<DisplayCompositionContext *>(comp_handle);
 
+  if (!display_comp_ctx) {
+    return kErrorParameters;
+  }
+
   res_mgr_.UnregisterDisplay(display_comp_ctx->display_resource_ctx);
   destroy_strategy_intf_(display_comp_ctx->strategy_intf);
 
@@ -154,11 +158,8 @@
            "display type %d", registered_displays_, configured_displays_,
            display_comp_ctx->display_type);
 
-  if (display_comp_ctx) {
-    delete display_comp_ctx;
-    display_comp_ctx = NULL;
-  }
-
+  delete display_comp_ctx;
+  display_comp_ctx = NULL;
   return kErrorNone;
 }
 
diff --git a/displayengine/libs/core/display_base.cpp b/displayengine/libs/core/display_base.cpp
index 058bacc..dac17fd 100644
--- a/displayengine/libs/core/display_base.cpp
+++ b/displayengine/libs/core/display_base.cpp
@@ -298,7 +298,7 @@
 
   DisplayError error = kErrorNone;
 
-  DLOGI("Set state = %d", state);
+  DLOGI("Set state = %d, display %d", state, display_type_);
 
   if (state == state_) {
     DLOGI("Same state transition is requested.");
@@ -307,13 +307,12 @@
 
   switch (state) {
   case kStateOff:
-    // Invoke flush during suspend for HDMI and virtual displays. StateOff is handled
-    // separately for primary in DisplayPrimary::SetDisplayState() function.
+    hw_layers_.info.count = 0;
     error = hw_intf_->Flush(hw_device_);
     if (error == kErrorNone) {
       comp_manager_->Purge(display_comp_ctx_);
-      state_ = state;
-      hw_layers_.info.count = 0;
+
+      error = hw_intf_->PowerOff(hw_device_);
     }
     break;
 
diff --git a/displayengine/libs/core/display_primary.cpp b/displayengine/libs/core/display_primary.cpp
index ac44fe3..8eed8ee 100644
--- a/displayengine/libs/core/display_primary.cpp
+++ b/displayengine/libs/core/display_primary.cpp
@@ -35,29 +35,5 @@
                                CompManager *comp_manager, OfflineCtrl *offline_ctrl)
   : DisplayBase(kPrimary, event_handler, kDevicePrimary, hw_intf, comp_manager, offline_ctrl) { }
 
-DisplayError DisplayPrimary::SetDisplayState(DisplayState state) {
-  DisplayError error = kErrorNone;
-
-  DLOGI("Set state = %d", state);
-
-  if (state == kStateOff) {
-    SCOPE_LOCK(locker_);
-    if (state == state_) {
-      DLOGI("Same state transition is requested.");
-      return kErrorNone;
-    }
-    error = hw_intf_->PowerOff(hw_device_);
-    if (error == kErrorNone) {
-      comp_manager_->Purge(display_comp_ctx_);
-      state_ = state;
-      hw_layers_.info.count = 0;
-    }
-  } else {
-    error = DisplayBase::SetDisplayState(state);
-  }
-
-  return error;
-}
-
 }  // namespace sde
 
diff --git a/displayengine/libs/core/display_primary.h b/displayengine/libs/core/display_primary.h
index 79fbddf..ea4cf2b 100644
--- a/displayengine/libs/core/display_primary.h
+++ b/displayengine/libs/core/display_primary.h
@@ -33,7 +33,6 @@
  public:
   DisplayPrimary(DisplayEventHandler *event_handler, HWInterface *hw_intf,
                  CompManager *comp_manager, OfflineCtrl *offline_ctrl);
-  virtual DisplayError SetDisplayState(DisplayState state);
 };
 
 }  // namespace sde
diff --git a/displayengine/libs/core/hw_framebuffer.cpp b/displayengine/libs/core/hw_framebuffer.cpp
index 4a361da..f47361b 100644
--- a/displayengine/libs/core/hw_framebuffer.cpp
+++ b/displayengine/libs/core/hw_framebuffer.cpp
@@ -43,7 +43,7 @@
 #include <utils/debug.h>
 
 #include "hw_framebuffer.h"
-
+#include "scalar_helper.h"
 
 #define __CLASS__ "HWFrameBuffer"
 
@@ -542,49 +542,33 @@
   return kErrorNone;
 }
 
-DisplayError HWFrameBuffer::OpenRotatorSession(Handle device, HWLayers *hw_layers) {
+DisplayError HWFrameBuffer::OpenRotatorSession(Handle device, HWRotateInfo *rotate_info) {
   HWContext *hw_context = reinterpret_cast<HWContext *>(device);
   HWRotator *hw_rotator = &hw_context->hw_rotator;
+  LayerBuffer *input_buffer = rotate_info->input_buffer;
+  HWBufferInfo *rot_buf_info = &rotate_info->hw_buffer_info;
 
   hw_rotator->Reset();
 
-  HWLayersInfo &hw_layer_info = hw_layers->info;
+  STRUCT_VAR(mdp_rotation_config, mdp_rot_config);
+  mdp_rot_config.version = MDP_ROTATION_REQUEST_VERSION_1_0;
+  mdp_rot_config.input.width = input_buffer->width;
+  mdp_rot_config.input.height = input_buffer->height;
+  SetFormat(input_buffer->format, &mdp_rot_config.input.format);
+  mdp_rot_config.output.width = rot_buf_info->output_buffer.width;
+  mdp_rot_config.output.height = rot_buf_info->output_buffer.height;
+  SetFormat(rot_buf_info->output_buffer.format, &mdp_rot_config.output.format);
+  mdp_rot_config.frame_rate = rotate_info->frame_rate;
 
-  for (uint32_t i = 0; i < hw_layer_info.count; i++) {
-    Layer& layer = hw_layer_info.stack->layers[hw_layer_info.index[i]];
-    LayerBuffer *input_buffer = layer.input_buffer;
-    bool rot90 = (layer.transform.rotation == 90.0f);
-
-    for (uint32_t count = 0; count < 2; count++) {
-      HWRotateInfo *rotate_info = &hw_layers->config[i].rotates[count];
-
-      if (rotate_info->valid) {
-        HWBufferInfo *rot_buf_info = &rotate_info->hw_buffer_info;
-
-        if (rot_buf_info->session_id < 0) {
-          STRUCT_VAR(mdp_rotation_config, mdp_rot_config);
-          mdp_rot_config.version = MDP_ROTATION_REQUEST_VERSION_1_0;
-          mdp_rot_config.input.width = input_buffer->width;
-          mdp_rot_config.input.height = input_buffer->height;
-          SetFormat(input_buffer->format, &mdp_rot_config.input.format);
-          mdp_rot_config.output.width = rot_buf_info->output_buffer.width;
-          mdp_rot_config.output.height = rot_buf_info->output_buffer.height;
-          SetFormat(rot_buf_info->output_buffer.format, &mdp_rot_config.output.format);
-          mdp_rot_config.frame_rate = layer.frame_rate;
-
-          if (ioctl_(hw_context->device_fd, MDSS_ROTATION_OPEN, &mdp_rot_config) < 0) {
-            IOCTL_LOGE(MDSS_ROTATION_OPEN, hw_context->type);
-            return kErrorHardware;
-          }
-
-          rot_buf_info->session_id = mdp_rot_config.session_id;
-
-          DLOGV_IF(kTagDriverConfig, "session_id %d", rot_buf_info->session_id);
-        }
-      }
-    }
+  if (ioctl_(hw_context->device_fd, MDSS_ROTATION_OPEN, &mdp_rot_config) < 0) {
+    IOCTL_LOGE(MDSS_ROTATION_OPEN, hw_context->type);
+    return kErrorHardware;
   }
 
+  rot_buf_info->session_id = mdp_rot_config.session_id;
+
+  DLOGV_IF(kTagDriverConfig, "session_id %d", rot_buf_info->session_id);
+
   return kErrorNone;
 }
 
@@ -700,7 +684,7 @@
         }
 
         mdp_layer.alpha = layer.plane_alpha;
-        mdp_layer.z_order = static_cast<uint16_t>(i);
+        mdp_layer.z_order = UINT16(pipe_info->z_order);
         mdp_layer.transp_mask = 0xffffffff;
         SetBlending(layer.blending, &mdp_layer.blend_op);
         mdp_layer.pipe_ndx = pipe_info->pipe_id;
@@ -722,14 +706,16 @@
           }
         }
 
-        if (pipe_info->scale_data.enable_pxl_ext) {
+        mdp_scale_data* mdp_scale = hw_display->GetScaleDataRef(mdp_layer_count);
+#ifdef USES_SCALAR
+        // Set the configured scale data for MDP driver
+        ScalarHelper::GetInstance()->SetScaleData(i, !count, mdp_scale);
+        if (mdp_scale->enable_pxl_ext) {
           if ((mdp_layer.flags & MDP_LAYER_DEINTERLACE) && (layer.transform.rotation == 90.0f))
-            mdp_buffer.width = pipe_info->scale_data.src_width;
-          hw_display->SetScaleData(pipe_info->scale_data, mdp_layer_count);
+            ScalarHelper::GetInstance()->UpdateSrcWidth(i, !count, &mdp_buffer.width);
         }
-
-        // Send scale data to MDP driver
-        mdp_layer.scale = hw_display->GetScaleRef(mdp_layer_count);
+#endif
+        mdp_layer.scale = mdp_scale;
         mdp_layer_count++;
 
         DLOGV_IF(kTagDriverConfig, "******************* Layer[%d] %s pipe Input ******************",
@@ -911,7 +897,7 @@
   return kErrorNone;
 }
 
-DisplayError HWFrameBuffer::RotatorValidate(HWContext *hw_context, HWLayers *hw_layers) {
+void HWFrameBuffer::SetRotatorCtrlParams(HWContext *hw_context, HWLayers *hw_layers) {
   HWRotator *hw_rotator = &hw_context->hw_rotator;
   DLOGV_IF(kTagDriverConfig, "************************* %s Validate Input ************************",
            GetDeviceString(hw_context->type));
@@ -978,17 +964,9 @@
       }
     }
   }
-
-  mdp_rot_request->flags = MDSS_ROTATION_REQUEST_VALIDATE;
-  if (ioctl_(hw_context->device_fd, MDSS_ROTATION_REQUEST, mdp_rot_request) < 0) {
-    IOCTL_LOGE(MDSS_ROTATION_REQUEST, hw_context->type);
-    return kErrorHardware;
-  }
-
-  return kErrorNone;
 }
 
-DisplayError HWFrameBuffer::RotatorCommit(HWContext *hw_context, HWLayers *hw_layers) {
+void HWFrameBuffer::SetRotatorBufferParams(HWContext *hw_context, HWLayers *hw_layers) {
   HWRotator *hw_rotator = &hw_context->hw_rotator;
   mdp_rotation_request *mdp_rot_request = &hw_rotator->mdp_rot_req;
   HWLayersInfo &hw_layer_info = hw_layers->info;
@@ -1041,14 +1019,36 @@
       }
     }
   }
+}
 
-  mdp_rot_request->flags &= ~MDSS_ROTATION_REQUEST_VALIDATE;
-  if (ioctl_(hw_context->device_fd, MDSS_ROTATION_REQUEST, mdp_rot_request) < 0) {
+DisplayError HWFrameBuffer::RotatorValidate(HWContext *hw_context, HWLayers *hw_layers) {
+  HWRotator *hw_rotator = &hw_context->hw_rotator;
+  SetRotatorCtrlParams(hw_context, hw_layers);
+
+  hw_rotator->mdp_rot_req.flags = MDSS_ROTATION_REQUEST_VALIDATE;
+  if (ioctl_(hw_context->device_fd, MDSS_ROTATION_REQUEST, &hw_rotator->mdp_rot_req) < 0) {
     IOCTL_LOGE(MDSS_ROTATION_REQUEST, hw_context->type);
     return kErrorHardware;
   }
 
-  rot_count = 0;
+  return kErrorNone;
+}
+
+DisplayError HWFrameBuffer::RotatorCommit(HWContext *hw_context, HWLayers *hw_layers) {
+  HWRotator *hw_rotator = &hw_context->hw_rotator;
+  HWLayersInfo &hw_layer_info = hw_layers->info;
+  uint32_t rot_count = 0;
+
+  SetRotatorCtrlParams(hw_context, hw_layers);
+
+  SetRotatorBufferParams(hw_context, hw_layers);
+
+  hw_rotator->mdp_rot_req.flags &= ~MDSS_ROTATION_REQUEST_VALIDATE;
+  if (ioctl_(hw_context->device_fd, MDSS_ROTATION_REQUEST, &hw_rotator->mdp_rot_req) < 0) {
+    IOCTL_LOGE(MDSS_ROTATION_REQUEST, hw_context->type);
+    return kErrorHardware;
+  }
+
   for (uint32_t i = 0; i < hw_layer_info.count; i++) {
     Layer& layer = hw_layer_info.stack->layers[hw_layer_info.index[i]];
 
@@ -1059,7 +1059,7 @@
 
       if (rotate_info->valid) {
         HWBufferInfo *rot_buf_info = &rotate_info->hw_buffer_info;
-        mdp_rotation_item *mdp_rot_item = &mdp_rot_request->list[rot_count];
+        mdp_rotation_item *mdp_rot_item = &hw_rotator->mdp_rot_req.list[rot_count];
 
         SyncMerge(layer.input_buffer->release_fence_fd, dup(mdp_rot_item->output.fence),
                   &layer.input_buffer->release_fence_fd);
@@ -1079,14 +1079,26 @@
   HWContext *hw_context = reinterpret_cast<HWContext *>(device);
   HWDisplay *hw_display = &hw_context->hw_display;
 
-  hw_display->Reset();
-  mdp_layer_commit_v1 &mdp_commit = hw_display->mdp_disp_commit.commit_v1;
-  mdp_commit.input_layer_cnt = 0;
-  mdp_commit.flags &= ~MDP_VALIDATE_LAYER;
+  switch (hw_context->type) {
+  case kDevicePrimary:
+  case kDeviceHDMI:
+  case kDeviceVirtual:
+    {
+      hw_display->Reset();
+      mdp_layer_commit_v1 &mdp_commit = hw_display->mdp_disp_commit.commit_v1;
+      mdp_commit.input_layer_cnt = 0;
+      mdp_commit.output_layer = NULL;
 
-  if (ioctl_(hw_context->device_fd, MSMFB_ATOMIC_COMMIT, &hw_display->mdp_disp_commit) == -1) {
-    IOCTL_LOGE(MSMFB_ATOMIC_COMMIT, hw_context->type);
-    return kErrorHardware;
+      mdp_commit.flags &= ~MDP_VALIDATE_LAYER;
+      if (ioctl_(hw_context->device_fd, MSMFB_ATOMIC_COMMIT, &hw_display->mdp_disp_commit) < 0) {
+        IOCTL_LOGE(MSMFB_ATOMIC_COMMIT, hw_context->type);
+        return kErrorHardware;
+      }
+    }
+    break;
+  default:
+    DLOGE("Flush is not supported for the device %s", GetDeviceString(hw_context->type));
+    return kErrorNotSupported;
   }
 
   return kErrorNone;
diff --git a/displayengine/libs/core/hw_framebuffer.h b/displayengine/libs/core/hw_framebuffer.h
index 5c01e5f..3ceb2b3 100644
--- a/displayengine/libs/core/hw_framebuffer.h
+++ b/displayengine/libs/core/hw_framebuffer.h
@@ -56,7 +56,7 @@
   virtual DisplayError Doze(Handle device);
   virtual DisplayError SetVSyncState(Handle device, bool enable);
   virtual DisplayError Standby(Handle device);
-  virtual DisplayError OpenRotatorSession(Handle device, HWLayers *hw_layers);
+  virtual DisplayError OpenRotatorSession(Handle device, HWRotateInfo *rotate_info);
   virtual DisplayError CloseRotatorSession(Handle device, int32_t session_id);
   virtual DisplayError Validate(Handle device, HWLayers *hw_layers);
   virtual DisplayError Commit(Handle device, HWLayers *hw_layers);
@@ -86,38 +86,10 @@
       mdp_disp_commit.commit_v1.input_layers = mdp_in_layers;
       mdp_disp_commit.commit_v1.output_layer = &mdp_out_layer;
       mdp_disp_commit.commit_v1.release_fence = -1;
+      mdp_disp_commit.commit_v1.retire_fence = -1;
     }
 
-    mdp_scale_data* GetScaleRef(uint32_t index) { return &scale_data[index]; }
-
-    void SetScaleData(scalar::Scale scale, uint32_t index) {
-      mdp_scale_data *mdp_scale = &scale_data[index];
-      mdp_scale->enable_pxl_ext = scale.enable_pxl_ext;
-
-      for (int i = 0; i < MAX_PLANES; i++) {
-        mdp_scale->init_phase_x[i] = scale.init_phase_x[i];
-        mdp_scale->phase_step_x[i] = scale.phase_step_x[i];
-        mdp_scale->init_phase_y[i] = scale.init_phase_y[i];
-        mdp_scale->phase_step_y[i] = scale.phase_step_y[i];
-
-        mdp_scale->num_ext_pxls_left[i] = scale.left.extension[i];
-        mdp_scale->num_ext_pxls_top[i] = scale.top.extension[i];
-        mdp_scale->num_ext_pxls_right[i] = scale.right.extension[i];
-        mdp_scale->num_ext_pxls_btm[i] = scale.bottom.extension[i];
-
-        mdp_scale->left_ftch[i] = scale.left.overfetch[i];
-        mdp_scale->top_ftch[i] = scale.top.overfetch[i];
-        mdp_scale->right_ftch[i] = scale.right.overfetch[i];
-        mdp_scale->btm_ftch[i] = scale.bottom.overfetch[i];
-
-        mdp_scale->left_rpt[i] = scale.left.repeat[i];
-        mdp_scale->top_rpt[i] = scale.top.repeat[i];
-        mdp_scale->right_rpt[i] = scale.right.repeat[i];
-        mdp_scale->btm_rpt[i] = scale.bottom.repeat[i];
-
-        mdp_scale->roi_w[i] = scale.roi_width[i];
-      }
-    }
+    mdp_scale_data* GetScaleDataRef(uint32_t index) { return &scale_data[index]; }
   };
 
   struct HWRotator {
@@ -192,6 +164,9 @@
   DisplayError DisplayValidate(HWContext *device_ctx, HWLayers *hw_layers);
   DisplayError DisplayCommit(HWContext *device_ctx, HWLayers *hw_layers);
 
+  void SetRotatorCtrlParams(HWContext *device_ctx, HWLayers *hw_layers);
+  void SetRotatorBufferParams(HWContext *device_ctx, HWLayers *hw_layers);
+
   DisplayError RotatorValidate(HWContext *device_ctx, HWLayers *hw_layers);
   DisplayError RotatorCommit(HWContext *device_ctx, HWLayers *hw_layers);
 
diff --git a/displayengine/libs/core/hw_interface.h b/displayengine/libs/core/hw_interface.h
index 084768e..b92846f 100644
--- a/displayengine/libs/core/hw_interface.h
+++ b/displayengine/libs/core/hw_interface.h
@@ -30,7 +30,6 @@
 #include <utils/constants.h>
 #include <core/buffer_allocator.h>
 #include <core/buffer_sync_handler.h>
-#include <scalar.h>
 
 namespace sde {
 
@@ -89,8 +88,8 @@
   HWResourceInfo()
     : hw_version(0), hw_revision(0), num_dma_pipe(0), num_vig_pipe(0), num_rgb_pipe(0),
       num_cursor_pipe(0), num_blending_stages(0), num_rotator(0), num_control(0),
-      num_mixer_to_disp(0), smp_total(0), smp_size(0), num_smp_per_pipe(0), max_scale_up(0),
-      max_scale_down(0), max_bandwidth_low(0), max_bandwidth_high(0), max_mixer_width(2048),
+      num_mixer_to_disp(0), smp_total(0), smp_size(0), num_smp_per_pipe(0), max_scale_up(1),
+      max_scale_down(1), max_bandwidth_low(0), max_bandwidth_high(0), max_mixer_width(2048),
       max_pipe_bw(0), max_sde_clk(0), clk_fudge_factor(1.0f), has_bwc(false),
       has_decimation(false), has_macrotile(false), has_rotator_downscale(false),
       has_non_scalar_rgb(false), is_src_split(false), always_src_split(false) { }
@@ -107,18 +106,20 @@
 };
 
 struct HWRotateInfo {
+  LayerBuffer *input_buffer;
   uint32_t pipe_id;
   LayerRect src_roi;
   LayerRect dst_roi;
   LayerBufferFormat dst_format;
   HWBlockType writeback_id;
-  float downscale_ratio_x;
-  float downscale_ratio_y;
+  float downscale_ratio;
   HWBufferInfo hw_buffer_info;
   bool valid;
+  uint32_t frame_rate;
 
-  HWRotateInfo() : pipe_id(0), dst_format(kFormatInvalid), writeback_id(kHWWriteback0),
-                   downscale_ratio_x(1.0f), downscale_ratio_y(1.0f), valid(false) { }
+  HWRotateInfo() : input_buffer(NULL), pipe_id(0), dst_format(kFormatInvalid),
+                   writeback_id(kHWWriteback0), downscale_ratio(1.0f), valid(false),
+                   frame_rate(0) { }
 
   void Reset() { *this = HWRotateInfo(); }
 };
@@ -129,10 +130,11 @@
   LayerRect dst_roi;
   uint8_t horizontal_decimation;
   uint8_t vertical_decimation;
-  scalar::Scale scale_data;
   bool valid;
+  uint32_t z_order;
 
-  HWPipeInfo() : pipe_id(0), horizontal_decimation(0), vertical_decimation(0), valid(false) { }
+  HWPipeInfo() : pipe_id(0), horizontal_decimation(0), vertical_decimation(0), valid(false),
+                 z_order(0) { }
 
   void Reset() { *this = HWPipeInfo(); }
 };
@@ -195,7 +197,7 @@
   virtual DisplayError Doze(Handle device) = 0;
   virtual DisplayError SetVSyncState(Handle device, bool enable) = 0;
   virtual DisplayError Standby(Handle device) = 0;
-  virtual DisplayError OpenRotatorSession(Handle device, HWLayers *hw_layers) = 0;
+  virtual DisplayError OpenRotatorSession(Handle device, HWRotateInfo *rotate_info) = 0;
   virtual DisplayError CloseRotatorSession(Handle device, int32_t session_id) = 0;
   virtual DisplayError Validate(Handle device, HWLayers *hw_layers) = 0;
   virtual DisplayError Commit(Handle device, HWLayers *hw_layers) = 0;
diff --git a/displayengine/libs/core/offline_ctrl.cpp b/displayengine/libs/core/offline_ctrl.cpp
index 5ffaf22..8794074 100644
--- a/displayengine/libs/core/offline_ctrl.cpp
+++ b/displayengine/libs/core/offline_ctrl.cpp
@@ -85,34 +85,30 @@
 
   DisplayOfflineContext *disp_offline_ctx = reinterpret_cast<DisplayOfflineContext *>(display_ctx);
 
+  disp_offline_ctx->pending_rot_commit = false;
+
   if (!hw_rotator_device_ && IsRotationRequired(hw_layers)) {
     DLOGV_IF(kTagOfflineCtrl, "No Rotator device found");
     return kErrorHardware;
   }
 
-  disp_offline_ctx->pending_rot_commit = false;
-
-  uint32_t i = 0;
-  while (hw_layers->closed_session_ids[i] >= 0) {
-    error = hw_intf_->CloseRotatorSession(hw_rotator_device_, hw_layers->closed_session_ids[i]);
-    if (LIKELY(error != kErrorNone)) {
-      DLOGE("Rotator close session failed");
-      return error;
-    }
-    hw_layers->closed_session_ids[i++] = -1;
+  error = CloseRotatorSession(hw_layers);
+  if (LIKELY(error != kErrorNone)) {
+    DLOGE("Close rotator session failed for display %d", disp_offline_ctx->display_type);
+    return error;
   }
 
 
   if (IsRotationRequired(hw_layers)) {
-    error = hw_intf_->OpenRotatorSession(hw_rotator_device_, hw_layers);
+    error = OpenRotatorSession(hw_layers);
     if (LIKELY(error != kErrorNone)) {
-      DLOGE("Rotator open session failed");
+      DLOGE("Open rotator session failed for display %d", disp_offline_ctx->display_type);
       return error;
     }
 
     error = hw_intf_->Validate(hw_rotator_device_, hw_layers);
     if (LIKELY(error != kErrorNone)) {
-      DLOGE("Rotator validation failed");
+      DLOGE("Rotator validation failed for display %d", disp_offline_ctx->display_type);
       return error;
     }
     disp_offline_ctx->pending_rot_commit = true;
@@ -129,7 +125,7 @@
   if (disp_offline_ctx->pending_rot_commit) {
     error = hw_intf_->Commit(hw_rotator_device_, hw_layers);
     if (error != kErrorNone) {
-      DLOGE("Rotator commit failed");
+      DLOGE("Rotator commit failed for display %d", disp_offline_ctx->display_type);
       return error;
     }
     disp_offline_ctx->pending_rot_commit = false;
@@ -138,6 +134,50 @@
   return kErrorNone;
 }
 
+DisplayError OfflineCtrl::OpenRotatorSession(HWLayers *hw_layers) {
+  HWLayersInfo &hw_layer_info = hw_layers->info;
+  DisplayError error = kErrorNone;
+
+  for (uint32_t i = 0; i < hw_layer_info.count; i++) {
+    Layer& layer = hw_layer_info.stack->layers[hw_layer_info.index[i]];
+    bool rot90 = (layer.transform.rotation == 90.0f);
+
+    for (uint32_t count = 0; count < 2; count++) {
+      HWRotateInfo *rotate_info = &hw_layers->config[i].rotates[count];
+      HWBufferInfo *rot_buf_info = &rotate_info->hw_buffer_info;
+
+      if (!rotate_info->valid || rot_buf_info->session_id >= 0) {
+        continue;
+      }
+
+      rotate_info->input_buffer = layer.input_buffer;
+      rotate_info->frame_rate = layer.frame_rate;
+
+      error = hw_intf_->OpenRotatorSession(hw_rotator_device_, rotate_info);
+      if (LIKELY(error != kErrorNone)) {
+        return error;
+      }
+    }
+  }
+
+  return kErrorNone;
+}
+
+DisplayError OfflineCtrl::CloseRotatorSession(HWLayers *hw_layers) {
+  DisplayError error = kErrorNone;
+  uint32_t i = 0;
+
+  while (hw_layers->closed_session_ids[i] >= 0) {
+    error = hw_intf_->CloseRotatorSession(hw_rotator_device_, hw_layers->closed_session_ids[i]);
+    if (LIKELY(error != kErrorNone)) {
+      return error;
+    }
+    hw_layers->closed_session_ids[i++] = -1;
+  }
+
+  return kErrorNone;
+}
+
 bool OfflineCtrl::IsRotationRequired(HWLayers *hw_layers) {
   HWLayersInfo &layer_info = hw_layers->info;
 
diff --git a/displayengine/libs/core/offline_ctrl.h b/displayengine/libs/core/offline_ctrl.h
index fafdf7c..fa8b264 100644
--- a/displayengine/libs/core/offline_ctrl.h
+++ b/displayengine/libs/core/offline_ctrl.h
@@ -50,6 +50,8 @@
     DisplayOfflineContext() : display_type(kPrimary), pending_rot_commit(false) { }
   };
 
+  DisplayError OpenRotatorSession(HWLayers *hw_layers);
+  DisplayError CloseRotatorSession(HWLayers *hw_layers);
   bool IsRotationRequired(HWLayers *hw_layers);
 
   HWInterface *hw_intf_;
diff --git a/displayengine/libs/core/res_config.cpp b/displayengine/libs/core/res_config.cpp
index d3c2d26..66f5cba 100644
--- a/displayengine/libs/core/res_config.cpp
+++ b/displayengine/libs/core/res_config.cpp
@@ -31,14 +31,11 @@
 
 #define __CLASS__ "ResManager"
 
-using scalar::PipeInfo;
-using scalar::LayerInfo;
-
 namespace sde {
 
-void ResManager::RotationConfig(const LayerTransform &transform, const float &scale_x,
-                                const float &scale_y, LayerRect *src_rect,
-                                struct HWLayerConfig *layer_config, uint32_t *rotate_count) {
+void ResManager::RotationConfig(const LayerTransform &transform, const float &downscale,
+                                LayerRect *src_rect, struct HWLayerConfig *layer_config,
+                                uint32_t *rotate_count) {
   HWRotateInfo *rotate = &layer_config->rotates[0];
   float src_width = src_rect->right - src_rect->left;
   float src_height = src_rect->bottom - src_rect->top;
@@ -47,20 +44,29 @@
   dst_rect.top = 0.0f;
   dst_rect.left = 0.0f;
 
-  rotate->downscale_ratio_x = scale_x;
-  rotate->downscale_ratio_y = scale_y;
+  rotate->downscale_ratio = downscale;
 
   // downscale when doing rotation
   if (IsRotationNeeded(transform.rotation)) {
-    dst_rect.right = src_height / rotate->downscale_ratio_x;
-    dst_rect.bottom = src_width / rotate->downscale_ratio_y;
+    if (downscale > 1.0f) {
+      src_height = ROUND_UP_ALIGN_DOWN(src_height, downscale);
+      src_rect->bottom = src_rect->top + src_height;
+      src_width = ROUND_UP_ALIGN_DOWN(src_width, downscale);
+      src_rect->right = src_rect->left + src_width;
+    }
+    dst_rect.right = src_height / downscale;
+    dst_rect.bottom = src_width / downscale;
   } else {
-    dst_rect.right = src_width / rotate->downscale_ratio_x;
-    dst_rect.bottom = src_height / rotate->downscale_ratio_y;
+    if (downscale > 1.0f) {
+      src_width = ROUND_UP_ALIGN_DOWN(src_width, downscale);
+      src_rect->right = src_rect->left + src_width;
+      src_height = ROUND_UP_ALIGN_DOWN(src_height, downscale);
+      src_rect->bottom = src_rect->top + src_height;
+    }
+    dst_rect.right = src_width / downscale;
+    dst_rect.bottom = src_height / downscale;
   }
 
-  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 +78,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 +87,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 +103,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 +118,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 +165,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;
@@ -177,10 +191,11 @@
   HWDisplayAttributes &display_attributes = display_resource_ctx->display_attributes;
   HWLayersInfo &layer_info = hw_layers->info;
   DisplayError error = kErrorNone;
+  uint32_t z_order = 0;
 
   for (uint32_t i = 0; i < layer_info.count; i++) {
     Layer& layer = layer_info.stack->layers[layer_info.index[i]];
-    float rot_scale_x = 1.0f, rot_scale_y = 1.0f;
+    float rot_scale = 1.0f;
     if (!IsValidDimension(layer.src_rect, layer.dst_rect)) {
       DLOGV_IF(kTagResources, "Input is invalid");
       LogRect(kTagResources, "input layer src_rect", layer.src_rect);
@@ -193,14 +208,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)) {
+      return kErrorNotSupported;
+    }
+
     // config rotator first
     for (uint32_t j = 0; j < kMaxRotatePerLayer; j++) {
       layer_config->rotates[j].Reset();
@@ -208,143 +239,151 @@
     layer_config->num_rotate = 0;
 
     LayerTransform transform = layer.transform;
-    if (IsRotationNeeded(transform.rotation) ||
-        UINT32(rot_scale_x) != 1 || UINT32(rot_scale_y) != 1) {
-      RotationConfig(layer.transform, rot_scale_x, rot_scale_y, &src_rect, layer_config,
-                     rotate_count);
+    if (IsRotationNeeded(transform.rotation) || UINT32(rot_scale) != 1) {
+      RotationConfig(layer.transform, rot_scale, &src_rect, layer_config, rotate_count);
       // rotator will take care of flipping, reset tranform
       transform = LayerTransform();
     }
 
     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++) {
-      DLOGV_IF(kTagResources, "rotate num = %d, scale_x = %.2f, scale_y = %.2f",
-               k, rot_scale_x, rot_scale_y);
+      DLOGV_IF(kTagResources, "rotate num = %d, scale_x = %.2f", k, rot_scale);
       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);
     }
+    // set z_order, left_pipe should always be valid
+    left_pipe.z_order = z_order;
+    if (layer_config->right_pipe.valid) {
+      // use different z_order if 2 pipes are on one mixer and without src split support
+      if (!hw_res_info_.is_src_split &&
+          ((left_pipe.dst_roi.right <= display_attributes.split_left &&
+            right_pipe.dst_roi.right <= display_attributes.split_left) ||
+           (left_pipe.dst_roi.left >= display_attributes.split_left &&
+            right_pipe.dst_roi.left >= display_attributes.split_left))) {
+        z_order++;
+      }
+      layer_config->right_pipe.z_order = z_order;
+    }
+    z_order++;
+    if (z_order >= hw_res_info_.num_blending_stages) {
+      DLOGV_IF(kTagResources, "z_order is over the limit: z_order = %d", z_order);
+      return kErrorResources;
+    }
   }
 
   return error;
 }
 
 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);
+                                         const LayerRect &dst, float *rot_scale) {
+  bool rotated90 = IsRotationNeeded(layer.transform.rotation) && (rot_scale != 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;
+  uint32_t rot_scale_local = 1;
 
   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;
+    float max_scale_down = FLOAT(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 && !IsMacroTileFormat(layer.input_buffer)) {
+      float scale_min = MIN(scale_x, scale_y);
+      float scale_max = MAX(scale_x, scale_y);
+      // use rotator to downscale when over the pipe scaling ability
+      if (UINT32(scale_min) >= 2 && scale_max > max_scale_down) {
+        // downscaling ratio needs be the same for both direction, use the smaller one.
+        rot_scale_local = 1 << UINT32(ceilf(log2f(scale_min / max_scale_down)));
+        if (rot_scale_local > kMaxRotateDownScaleRatio)
+          rot_scale_local = kMaxRotateDownScaleRatio;
+        scale_x /= FLOAT(rot_scale_local);
+        scale_y /= FLOAT(rot_scale_local);
+      }
+      *rot_scale = FLOAT(rot_scale_local);
+    }
 
-    if (((!hw_res_info_.has_decimation) || (IsMacroTileFormat(layer.input_buffer))) &&
-        (scale_x > max_scale_down || scale_y > max_scale_down)) {
+    if (hw_res_info_.has_decimation && !property_setting_.disable_decimation &&
+               !IsMacroTileFormat(layer.input_buffer)) {
+      max_scale_down *= FLOAT(kMaxDecimationDownScaleRatio);
+    }
+
+    if (scale_x > max_scale_down || scale_y > 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, rot_scale = %d",
+               IsMacroTileFormat(layer.input_buffer), scale_x, scale_y, crop_width, dst_width,
+               hw_res_info_.has_decimation, property_setting_.disable_decimation, rot_scale_local);
       return kErrorNotSupported;
     }
   }
 
-  const uint32_t max_scale_up = hw_res_info_.max_scale_up;
+  float max_scale_up = FLOAT(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;
-  }
-  *rot_scale_x = rot_scale;
-
-  rot_scale = 1.0f;
-  while (scale_y > hw_res_info_.max_scale_down) {
-    scale_y /= 2;
-    rot_scale *= 2;
-  }
-  *rot_scale_y = rot_scale;
-  DLOGV_IF(kTagResources, "rotator scaling hor = %.0f, ver = %.0f", *rot_scale_x, *rot_scale_y);
+  DLOGV_IF(kTagResources, "scale_x = %.4f, scale_y = %.4f, rot_scale = %d",
+           scale_x, scale_y, rot_scale_local);
 
   return kErrorNone;
 }
@@ -370,7 +409,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 +458,7 @@
   }
 
   if (!need_cut)
-    return;
+    return true;
 
   CalculateCut(transform, &left_cut_ratio, &top_cut_ratio, &right_cut_ratio, &bottom_cut_ratio);
 
@@ -427,6 +466,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 +513,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 +532,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 +563,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;
@@ -533,123 +583,49 @@
   }
 }
 
-// Scalar helper functions
-static void SetPipeInfo(HWPipeInfo* hw_pipe, PipeInfo* pipe) {
-  pipe->id = hw_pipe->pipe_id;
-  pipe->scale_data = &hw_pipe->scale_data;
-  pipe->horz_deci = hw_pipe->horizontal_decimation;
-  pipe->vert_deci = hw_pipe->vertical_decimation;
+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.
 
-  pipe->src_rect.x = UINT32(hw_pipe->src_roi.left);
-  pipe->src_rect.y = UINT32(hw_pipe->src_roi.top);
-  pipe->src_rect.w = UINT32(hw_pipe->src_roi.right) - pipe->src_rect.x;
-  pipe->src_rect.h = UINT32(hw_pipe->src_roi.bottom) - pipe->src_rect.y;
+  NormalizeRect(align_x, align_y, &left_pipe->src_roi);
+  NormalizeRect(1, 1, &left_pipe->dst_roi);
 
-  pipe->dst_rect.x = UINT32(hw_pipe->dst_roi.left);
-  pipe->dst_rect.y = UINT32(hw_pipe->dst_roi.top);
-  pipe->dst_rect.w = UINT32(hw_pipe->dst_roi.right) - pipe->dst_rect.x;
-  pipe->dst_rect.h = UINT32(hw_pipe->dst_roi.bottom) - pipe->dst_rect.y;
-}
-
-static void UpdateSrcRoi(PipeInfo* pipe, HWPipeInfo* hw_pipe) {
-  hw_pipe->src_roi.left   = FLOAT(pipe->src_rect.x);
-  hw_pipe->src_roi.top    = FLOAT(pipe->src_rect.y);
-  hw_pipe->src_roi.right  = FLOAT(pipe->src_rect.x + pipe->src_rect.w);
-  hw_pipe->src_roi.bottom = FLOAT(pipe->src_rect.y + pipe->src_rect.h);
-}
-
-static uint32_t GetScalarFormat(LayerBufferFormat source) {
-  uint32_t format = scalar::UNKNOWN_FORMAT;
-
-  switch (source) {
-  case kFormatARGB8888:                 format = scalar::ARGB_8888;         break;
-  case kFormatRGBA8888:                 format = scalar::RGBA_8888;         break;
-  case kFormatBGRA8888:                 format = scalar::BGRA_8888;         break;
-  case kFormatXRGB8888:                 format = scalar::XRGB_8888;         break;
-  case kFormatRGBX8888:                 format = scalar::RGBX_8888;         break;
-  case kFormatBGRX8888:                 format = scalar::BGRX_8888;         break;
-  case kFormatRGB888:                   format = scalar::RGB_888;           break;
-  case kFormatRGB565:                   format = scalar::RGB_565;           break;
-  case kFormatYCbCr420Planar:           format = scalar::Y_CB_CR_H2V2;      break;
-  case kFormatYCrCb420Planar:           format = scalar::Y_CR_CB_H2V2;      break;
-  case kFormatYCbCr420SemiPlanar:       format = scalar::Y_CBCR_H2V2;       break;
-  case kFormatYCrCb420SemiPlanar:       format = scalar::Y_CRCB_H2V2;       break;
-  case kFormatYCbCr422Packed:           format = scalar::YCBYCR_H2V1;       break;
-  case kFormatYCbCr420SemiPlanarVenus:  format = scalar::Y_CBCR_H2V2_VENUS; break;
-  case kFormatRGBA8888Ubwc:             format = scalar::RGBA_8888_UBWC;    break;
-  case kFormatRGB565Ubwc:               format = scalar::RGB_565_UBWC;      break;
-  case kFormatYCbCr420SPVenusUbwc:      format = scalar::Y_CBCR_H2V2_UBWC;  break;
-  default:
-    DLOGE("Unsupported source format: %x", source);
-    break;
+  if (right_pipe->valid) {
+    NormalizeRect(align_x, align_y, &right_pipe->src_roi);
+    NormalizeRect(1, 1, &right_pipe->dst_roi);
   }
 
-  return format;
-}
-
-bool ResManager::ConfigureScaling(HWLayers *hw_layers) {
-  HWLayersInfo &hw_layer_info = hw_layers->info;
-
-  for (uint32_t i = 0; i < hw_layer_info.count; i++) {
-    Layer &layer = hw_layer_info.stack->layers[hw_layer_info.index[i]];
-    LayerBuffer *input_buffer = layer.input_buffer;
-    HWPipeInfo* left_pipe = &hw_layers->config[i].left_pipe;
-    HWPipeInfo* right_pipe = &hw_layers->config[i].right_pipe;
-
-    // Prepare data structure for lib scalar
-    uint32_t flags = 0;
-    struct LayerInfo layer_info;
-
-    if (layer.transform.rotation == 90.0f) {
-      // Flips will be taken care by rotator, if layer requires 90 rotation
-      flags |= scalar::SCALAR_SOURCE_ROTATED_90;
+  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 {
-      flags |= layer.transform.flip_vertical ? scalar::SCALAR_FLIP_UD : 0;
-      flags |= layer.transform.flip_horizontal ? scalar::SCALAR_FLIP_LR : 0;
+      right_pipe->dst_roi.left = left_pipe->dst_roi.right;
     }
-
-    for (uint32_t count = 0; count < 2; count++) {
-      HWPipeInfo* hw_pipe = (count == 0) ? left_pipe : right_pipe;
-      HWRotateInfo* rotate_info = &hw_layers->config[i].rotates[count];
-      PipeInfo* scalar_pipe = (count == 0) ? &layer_info.left_pipe : &layer_info.right_pipe;
-
-      if (rotate_info->valid)
-        input_buffer = &rotate_info->hw_buffer_info.output_buffer;
-
-      scalar_pipe->flags = flags;
-      hw_pipe->scale_data.src_width = input_buffer->width;
-      SetPipeInfo(hw_pipe, scalar_pipe);
-    }
-    layer_info.src_format = GetScalarFormat(input_buffer->format);
-
-    DLOGV_IF(kTagResources, "Scalar Input[%d] flags=%x format=%x", i, flags, layer_info.src_format);
-    DLOGV_IF(kTagResources, "Left: id=%d hD=%d vD=%d srcRect=[%d %d %d %d] dstRect=[%d %d %d %d]",
-        layer_info.left_pipe.id, layer_info.left_pipe.horz_deci, layer_info.left_pipe.vert_deci,
-        layer_info.left_pipe.src_rect.x, layer_info.left_pipe.src_rect.y,
-        layer_info.left_pipe.src_rect.w, layer_info.left_pipe.src_rect.h,
-        layer_info.left_pipe.dst_rect.x, layer_info.left_pipe.dst_rect.y,
-        layer_info.left_pipe.dst_rect.w, layer_info.left_pipe.dst_rect.h);
-    DLOGV_IF(kTagResources, "Right: id=%d hD=%d vD=%d srcRect=[%d %d %d %d] dstRect=[%d %d %d %d]",
-        layer_info.right_pipe.id, layer_info.right_pipe.horz_deci, layer_info.right_pipe.vert_deci,
-        layer_info.right_pipe.src_rect.x, layer_info.right_pipe.src_rect.y,
-        layer_info.right_pipe.src_rect.w, layer_info.right_pipe.src_rect.h,
-        layer_info.right_pipe.dst_rect.x, layer_info.right_pipe.dst_rect.y,
-        layer_info.right_pipe.dst_rect.w, layer_info.right_pipe.dst_rect.h);
-
-    // Configure scale data structure
-    if (ScalarConfigureScale(&layer_info) < 0) {
-      DLOGE("Scalar library failed to configure scale data!");
-      return false;
-    }
-
-    // Update Src Roi in HWPipeInfo
-    if (left_pipe->scale_data.enable_pxl_ext)
-      UpdateSrcRoi(&layer_info.left_pipe, left_pipe);
-    if (right_pipe->scale_data.enable_pxl_ext)
-      UpdateSrcRoi(&layer_info.right_pipe, right_pipe);
+  }
+  error = ValidateScaling(layer, left_pipe->src_roi, left_pipe->dst_roi, NULL);
+  if (error != kErrorNone) {
+    goto PipeConfigExit;
   }
 
-  return true;
+  if (right_pipe->valid) {
+    error = ValidateScaling(layer, right_pipe->src_roi, right_pipe->dst_roi, 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..9868ddf 100644
--- a/displayengine/libs/core/res_manager.cpp
+++ b/displayengine/libs/core/res_manager.cpp
@@ -28,9 +28,9 @@
 #include <dlfcn.h>
 
 #include "res_manager.h"
+#include "scalar_helper.h"
 
 #define __CLASS__ "ResManager"
-#define SCALAR_LIBRARY_NAME "libscalar.so"
 
 namespace sde {
 
@@ -98,7 +98,14 @@
 
   if (hw_res_info_.num_rotator > kMaxNumRotator) {
     DLOGE("Number of rotator is over the limit! %d", hw_res_info_.num_rotator);
-    return kErrorParameters;
+    hw_res_info_.num_rotator = kMaxNumRotator;
+  }
+
+  if (hw_res_info_.max_scale_down < 1 || hw_res_info_.max_scale_up < 1) {
+    DLOGE("Max scaling setting is invalid! max_scale_down = %d, max_scale_up = %d",
+          hw_res_info_.max_scale_down, hw_res_info_.max_scale_up);
+    hw_res_info_.max_scale_down = 1;
+    hw_res_info_.max_scale_up = 1;
   }
 
   if (hw_res_info_.num_rotator > 0) {
@@ -114,23 +121,16 @@
   rgb_pipes_[0].state = kPipeStateOwnedByKernel;
   rgb_pipes_[1].state = kPipeStateOwnedByKernel;
 
-  ScalarConfigureScale = NULL;
-  lib_scalar_handle_ = dlopen(SCALAR_LIBRARY_NAME, RTLD_NOW);
-  if (lib_scalar_handle_) {
-    void **scalar_func = reinterpret_cast<void **>(&ScalarConfigureScale);
-    *scalar_func = ::dlsym(lib_scalar_handle_, "configureScale");
-  } else {
-    DLOGW("Unable to load %s !", SCALAR_LIBRARY_NAME);
-  }
-
+#ifdef USES_SCALAR
+  ScalarHelper::GetInstance()->Init();
+#endif
   return kErrorNone;
 }
 
 DisplayError ResManager::Deinit() {
-  if (lib_scalar_handle_) {
-    dlclose(lib_scalar_handle_);
-    lib_scalar_handle_ = NULL;
-  }
+#ifdef USES_SCALAR
+  ScalarHelper::GetInstance()->Deinit();
+#endif
   return kErrorNone;
 }
 
@@ -248,6 +248,9 @@
     }
   }
 
+  property_setting_.disable_rotator_downscaling = Debug::IsRotatorDownScaleDisabled();
+  property_setting_.disable_decimation = Debug::IsDecimationDisabled();
+
   return kErrorNone;
 }
 
@@ -266,20 +269,24 @@
 
   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) {
+    DLOGV_IF(kTagResources, "layer count is over the limit: layer count = %d", layer_info.count);
     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 +326,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 +347,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 +357,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 +381,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);
@@ -374,12 +391,12 @@
     goto CleanupOnError;
   }
 
-  if (lib_scalar_handle_ && ScalarConfigureScale) {
-    if (!ConfigureScaling(hw_layers)) {
-      DLOGV_IF(kTagResources, "Scale data configuration has failed!");
-      goto CleanupOnError;
-    }
+#ifdef USES_SCALAR
+  if (!ScalarHelper::GetInstance()->ConfigureScale(hw_layers)) {
+    DLOGV_IF(kTagResources, "Scale data configuration has failed!");
+    goto CleanupOnError;
   }
+#endif
 
   if (!CheckBandwidth(display_resource_ctx, hw_layers)) {
     DLOGV_IF(kTagResources, "Bandwidth check failed!");
@@ -626,15 +643,17 @@
   for (uint32_t i = 0; i < layer_info.count; i++) {
     Layer& layer = layer_info.stack->layers[layer_info.index[i]];
     HWRotateInfo *rotate = &hw_layers->config[i].rotates[0];
+    bool rot90 = (layer.transform.rotation == 90.0f);
 
     if (rotate->valid) {
       LayerBufferFormat rot_ouput_format;
-      SetRotatorOutputFormat(layer.input_buffer->format, false, true, &rot_ouput_format);
+      SetRotatorOutputFormat(layer.input_buffer->format, false, rot90, &rot_ouput_format);
 
       HWBufferInfo *hw_buffer_info = &rotate->hw_buffer_info;
       hw_buffer_info->buffer_config.width = UINT32(rotate->dst_roi.right - rotate->dst_roi.left);
       hw_buffer_info->buffer_config.height = UINT32(rotate->dst_roi.bottom - rotate->dst_roi.top);
       hw_buffer_info->buffer_config.format = rot_ouput_format;
+      // Allocate two rotator output buffers by default for double buffering.
       hw_buffer_info->buffer_config.buffer_count = 2;
       hw_buffer_info->buffer_config.secure = layer.input_buffer->flags.secure;
 
@@ -647,12 +666,13 @@
     rotate = &hw_layers->config[i].rotates[1];
     if (rotate->valid) {
       LayerBufferFormat rot_ouput_format;
-      SetRotatorOutputFormat(layer.input_buffer->format, false, true, &rot_ouput_format);
+      SetRotatorOutputFormat(layer.input_buffer->format, false, rot90, &rot_ouput_format);
 
       HWBufferInfo *hw_buffer_info = &rotate->hw_buffer_info;
       hw_buffer_info->buffer_config.width = UINT32(rotate->dst_roi.right - rotate->dst_roi.left);
       hw_buffer_info->buffer_config.height = UINT32(rotate->dst_roi.bottom - rotate->dst_roi.top);
       hw_buffer_info->buffer_config.format = rot_ouput_format;
+      // Allocate two rotator output buffers by default for double buffering.
       hw_buffer_info->buffer_config.buffer_count = 2;
       hw_buffer_info->buffer_config.secure = layer.input_buffer->flags.secure;
 
@@ -802,6 +822,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 +994,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)
@@ -1087,8 +1129,5 @@
   return;
 }
 
-void* ResManager::lib_scalar_handle_ = NULL;
-int (*ResManager::ScalarConfigureScale)(struct scalar::LayerInfo* layer) = NULL;
-
 }  // namespace sde
 
diff --git a/displayengine/libs/core/res_manager.h b/displayengine/libs/core/res_manager.h
index c80dc2c..9ff5384 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);
+                               const LayerRect &dst, float *rot_scale);
   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,14 +189,14 @@
   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)
          { return (UINT32(rotation) == 90 || UINT32(rotation) == 270); }
-  void RotationConfig(const LayerTransform &transform, const float &scale_x,
-                      const float &scale_y, LayerRect *src_rect,
-                      struct HWLayerConfig *layer_config, uint32_t *rotate_count);
+  void RotationConfig(const LayerTransform &transform, const float &downscale,
+                      LayerRect *src_rect, struct HWLayerConfig *layer_config,
+                      uint32_t *rotate_count);
   DisplayError AcquireRotator(DisplayResourceContext *display_resource_ctx,
                               const uint32_t roate_cnt);
   void AssignRotator(HWRotateInfo *rotate, uint32_t *rotate_cnt);
@@ -197,7 +204,10 @@
   DisplayError AllocRotatorBuffer(Handle display_ctx, HWLayers *hw_layers);
   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_;
@@ -216,8 +226,7 @@
   BufferAllocator *buffer_allocator_;
   BufferSyncHandler *buffer_sync_handler_;  // Pointer to buffer sync handler that was defined by
                                             // 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/core/scalar_helper.cpp b/displayengine/libs/core/scalar_helper.cpp
new file mode 100755
index 0000000..57c51c0
--- /dev/null
+++ b/displayengine/libs/core/scalar_helper.cpp
@@ -0,0 +1,228 @@
+/*
+* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without modification, are permitted
+* provided that the following conditions are met:
+*    * Redistributions of source code must retain the above copyright notice, this list of
+*      conditions and the following disclaimer.
+*    * Redistributions in binary form must reproduce the above copyright notice, this list of
+*      conditions and the following disclaimer in the documentation and/or other materials provided
+*      with the distribution.
+*    * Neither the name of The Linux Foundation nor the names of its contributors may be used to
+*      endorse or promote products derived from this software without specific prior written
+*      permission.
+*
+* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+* NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifdef USES_SCALAR
+
+#include <dlfcn.h>
+#include <utils/debug.h>
+#include "scalar_helper.h"
+
+#define __CLASS__ "ScalarHelper"
+
+namespace sde {
+
+ScalarHelper* ScalarHelper::scalar_helper_ = NULL;
+
+ScalarHelper* ScalarHelper::GetInstance() {
+  if (scalar_helper_ == NULL) {
+    scalar_helper_ = new ScalarHelper();
+  }
+  return scalar_helper_;
+}
+
+// Scalar helper functions
+static void SetPipeInfo(HWPipeInfo* hw_pipe, scalar::PipeInfo* pipe) {
+  pipe->id = hw_pipe->pipe_id;
+  pipe->horz_deci = hw_pipe->horizontal_decimation;
+  pipe->vert_deci = hw_pipe->vertical_decimation;
+
+  pipe->src_rect.x = UINT32(hw_pipe->src_roi.left);
+  pipe->src_rect.y = UINT32(hw_pipe->src_roi.top);
+  pipe->src_rect.w = UINT32(hw_pipe->src_roi.right) - pipe->src_rect.x;
+  pipe->src_rect.h = UINT32(hw_pipe->src_roi.bottom) - pipe->src_rect.y;
+
+  pipe->dst_rect.x = UINT32(hw_pipe->dst_roi.left);
+  pipe->dst_rect.y = UINT32(hw_pipe->dst_roi.top);
+  pipe->dst_rect.w = UINT32(hw_pipe->dst_roi.right) - pipe->dst_rect.x;
+  pipe->dst_rect.h = UINT32(hw_pipe->dst_roi.bottom) - pipe->dst_rect.y;
+}
+
+static void UpdateSrcRoi(scalar::PipeInfo* pipe, HWPipeInfo* hw_pipe) {
+  hw_pipe->src_roi.left   = FLOAT(pipe->src_rect.x);
+  hw_pipe->src_roi.top    = FLOAT(pipe->src_rect.y);
+  hw_pipe->src_roi.right  = FLOAT(pipe->src_rect.x + pipe->src_rect.w);
+  hw_pipe->src_roi.bottom = FLOAT(pipe->src_rect.y + pipe->src_rect.h);
+}
+
+static uint32_t GetScalarFormat(LayerBufferFormat source) {
+  uint32_t format = scalar::UNKNOWN_FORMAT;
+
+  switch (source) {
+  case kFormatARGB8888:                 format = scalar::ARGB_8888;         break;
+  case kFormatRGBA8888:                 format = scalar::RGBA_8888;         break;
+  case kFormatBGRA8888:                 format = scalar::BGRA_8888;         break;
+  case kFormatXRGB8888:                 format = scalar::XRGB_8888;         break;
+  case kFormatRGBX8888:                 format = scalar::RGBX_8888;         break;
+  case kFormatBGRX8888:                 format = scalar::BGRX_8888;         break;
+  case kFormatRGB888:                   format = scalar::RGB_888;           break;
+  case kFormatRGB565:                   format = scalar::RGB_565;           break;
+  case kFormatYCbCr420Planar:           format = scalar::Y_CB_CR_H2V2;      break;
+  case kFormatYCrCb420Planar:           format = scalar::Y_CR_CB_H2V2;      break;
+  case kFormatYCbCr420SemiPlanar:       format = scalar::Y_CBCR_H2V2;       break;
+  case kFormatYCrCb420SemiPlanar:       format = scalar::Y_CRCB_H2V2;       break;
+  case kFormatYCbCr422Packed:           format = scalar::YCBYCR_H2V1;       break;
+  case kFormatYCbCr420SemiPlanarVenus:  format = scalar::Y_CBCR_H2V2_VENUS; break;
+  case kFormatRGBA8888Ubwc:             format = scalar::RGBA_8888_UBWC;    break;
+  case kFormatRGB565Ubwc:               format = scalar::RGB_565_UBWC;      break;
+  case kFormatYCbCr420SPVenusUbwc:      format = scalar::Y_CBCR_H2V2_UBWC;  break;
+  default:
+    DLOGE("Unsupported source format: %x", source);
+    break;
+  }
+  return format;
+}
+
+void ScalarHelper::Init() {
+  lib_scalar_handle_   = NULL;
+  ScalarConfigureScale = NULL;
+
+  lib_scalar_handle_ = dlopen(SCALAR_LIBRARY_NAME, RTLD_NOW);
+  if (lib_scalar_handle_) {
+    void **scalar_func = reinterpret_cast<void **>(&ScalarConfigureScale);
+    *scalar_func = ::dlsym(lib_scalar_handle_, "configureScale");
+  } else {
+    DLOGW("Unable to load %s !", SCALAR_LIBRARY_NAME);
+  }
+}
+
+void ScalarHelper::Deinit() {
+  if (lib_scalar_handle_) {
+    dlclose(lib_scalar_handle_);
+    lib_scalar_handle_ = NULL;
+  }
+}
+
+bool ScalarHelper::ConfigureScale(HWLayers *hw_layers) {
+
+  if (!lib_scalar_handle_ || !ScalarConfigureScale) {
+    // No scalar library
+    return true;
+  }
+
+  // Reset scale data
+  memset(&scale_data_, 0, sizeof(scale_data_));
+  HWLayersInfo &hw_layer_info = hw_layers->info;
+
+  for (uint32_t i = 0; i < hw_layer_info.count; i++) {
+    Layer &layer = hw_layer_info.stack->layers[hw_layer_info.index[i]];
+    LayerBuffer *input_buffer = layer.input_buffer;
+    HWPipeInfo* left_pipe = &hw_layers->config[i].left_pipe;
+    HWPipeInfo* right_pipe = &hw_layers->config[i].right_pipe;
+
+    // Prepare data structure for lib scalar
+    uint32_t flags = 0;
+    struct scalar::LayerInfo layer_info;
+
+    if (layer.transform.rotation == 90.0f) {
+      // Flips will be taken care by rotator, if layer requires 90 rotation
+      flags |= scalar::SCALAR_SOURCE_ROTATED_90;
+    } else {
+      flags |= layer.transform.flip_vertical ? scalar::SCALAR_FLIP_UD : 0;
+      flags |= layer.transform.flip_horizontal ? scalar::SCALAR_FLIP_LR : 0;
+    }
+
+    for (uint32_t count = 0; count < 2; count++) {
+      HWPipeInfo* hw_pipe = (count == 0) ? left_pipe : right_pipe;
+      HWRotateInfo* rotate_info = &hw_layers->config[i].rotates[count];
+      scalar::PipeInfo* pipe = (count == 0) ? &layer_info.left_pipe : &layer_info.right_pipe;
+
+      if (rotate_info->valid)
+        input_buffer = &rotate_info->hw_buffer_info.output_buffer;
+
+      pipe->flags = flags;
+      pipe->scale_data = GetScaleRef(i, !count);
+      pipe->scale_data->src_width = input_buffer->width;
+      SetPipeInfo(hw_pipe, pipe);
+    }
+    layer_info.src_format = GetScalarFormat(input_buffer->format);
+
+    DLOGV_IF(kTagScalar, "Scalar Input[%d] flags=%x format=%x", i, flags, layer_info.src_format);
+    DLOGV_IF(kTagScalar, "Left: id=%d hD=%d vD=%d srcRect=[%d %d %d %d] dstRect=[%d %d %d %d]",
+      layer_info.left_pipe.id, layer_info.left_pipe.horz_deci, layer_info.left_pipe.vert_deci,
+      layer_info.left_pipe.src_rect.x, layer_info.left_pipe.src_rect.y,
+      layer_info.left_pipe.src_rect.w, layer_info.left_pipe.src_rect.h,
+      layer_info.left_pipe.dst_rect.x, layer_info.left_pipe.dst_rect.y,
+      layer_info.left_pipe.dst_rect.w, layer_info.left_pipe.dst_rect.h);
+    DLOGV_IF(kTagScalar, "Right: id=%d hD=%d vD=%d srcRect=[%d %d %d %d] dstRect=[%d %d %d %d]",
+      layer_info.right_pipe.id, layer_info.right_pipe.horz_deci, layer_info.right_pipe.vert_deci,
+      layer_info.right_pipe.src_rect.x, layer_info.right_pipe.src_rect.y,
+      layer_info.right_pipe.src_rect.w, layer_info.right_pipe.src_rect.h,
+      layer_info.right_pipe.dst_rect.x, layer_info.right_pipe.dst_rect.y,
+      layer_info.right_pipe.dst_rect.w, layer_info.right_pipe.dst_rect.h);
+
+    // Configure scale data structure
+    if (ScalarConfigureScale(&layer_info) < 0) {
+      DLOGE("Scalar library failed to configure scale data!");
+      return false;
+    }
+
+    // Update Src Roi in HWPipeInfo
+    if (layer_info.left_pipe.scale_data->enable_pxl_ext)
+      UpdateSrcRoi(&layer_info.left_pipe, left_pipe);
+    if (layer_info.right_pipe.scale_data->enable_pxl_ext)
+      UpdateSrcRoi(&layer_info.right_pipe, right_pipe);
+  }
+  return true;
+}
+
+void ScalarHelper::UpdateSrcWidth(uint32_t index, bool left, uint32_t* width) {
+  *width = GetScaleRef(index, left)->src_width;
+}
+
+void ScalarHelper::SetScaleData(uint32_t index, bool left, mdp_scale_data* mdp_scale) {
+
+  if (!lib_scalar_handle_ || !ScalarConfigureScale)
+    return;
+
+  scalar::Scale* scale = GetScaleRef(index, left);
+  mdp_scale->enable_pxl_ext = scale->enable_pxl_ext;
+
+  for (int i = 0; i < MAX_PLANES; i++) {
+    mdp_scale->init_phase_x[i] = scale->init_phase_x[i];
+    mdp_scale->phase_step_x[i] = scale->phase_step_x[i];
+    mdp_scale->init_phase_y[i] = scale->init_phase_y[i];
+    mdp_scale->phase_step_y[i] = scale->phase_step_y[i];
+
+    mdp_scale->num_ext_pxls_left[i] = scale->left.extension[i];
+    mdp_scale->num_ext_pxls_top[i] = scale->top.extension[i];
+    mdp_scale->num_ext_pxls_right[i] = scale->right.extension[i];
+    mdp_scale->num_ext_pxls_btm[i] = scale->bottom.extension[i];
+
+    mdp_scale->left_ftch[i] = scale->left.overfetch[i];
+    mdp_scale->top_ftch[i] = scale->top.overfetch[i];
+    mdp_scale->right_ftch[i] = scale->right.overfetch[i];
+    mdp_scale->btm_ftch[i] = scale->bottom.overfetch[i];
+
+    mdp_scale->left_rpt[i] = scale->left.repeat[i];
+    mdp_scale->top_rpt[i] = scale->top.repeat[i];
+    mdp_scale->right_rpt[i] = scale->right.repeat[i];
+    mdp_scale->btm_rpt[i] = scale->bottom.repeat[i];
+
+    mdp_scale->roi_w[i] = scale->roi_width[i];
+  }
+}
+
+} // namespace sde
+
+#endif
diff --git a/displayengine/libs/core/scalar_helper.h b/displayengine/libs/core/scalar_helper.h
new file mode 100755
index 0000000..a54cb0c
--- /dev/null
+++ b/displayengine/libs/core/scalar_helper.h
@@ -0,0 +1,67 @@
+/*
+* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without modification, are permitted
+* provided that the following conditions are met:
+*    * Redistributions of source code must retain the above copyright notice, this list of
+*      conditions and the following disclaimer.
+*    * Redistributions in binary form must reproduce the above copyright notice, this list of
+*      conditions and the following disclaimer in the documentation and/or other materials provided
+*      with the distribution.
+*    * Neither the name of The Linux Foundation nor the names of its contributors may be used to
+*      endorse or promote products derived from this software without specific prior written
+*      permission.
+*
+* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+* NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef __SCALAR_HELPER_H__
+#define __SCALAR_HELPER_H__
+
+#ifdef USES_SCALAR
+
+#include <sys/types.h>
+#include <linux/msm_mdp_ext.h>
+#include <hw_interface.h>
+#include <scalar.h>
+
+#define SCALAR_LIBRARY_NAME "libscalar.so"
+
+namespace sde {
+
+class ScalarHelper {
+
+ public:
+  void Init();
+  void Deinit();
+  bool ConfigureScale(HWLayers *hw_layers);
+  void UpdateSrcWidth(uint32_t index, bool left, uint32_t* src_width);
+  void SetScaleData(uint32_t index, bool left, mdp_scale_data* mdp_scale);
+  static ScalarHelper* GetInstance();
+
+ private:
+  explicit ScalarHelper() { }
+  struct ScaleData {
+    scalar::Scale left_scale;
+    scalar::Scale right_scale;
+  };
+  struct ScaleData scale_data_[kMaxSDELayers];
+  void* lib_scalar_handle_;
+  int (*ScalarConfigureScale)(struct scalar::LayerInfo* layer);
+  scalar::Scale* GetScaleRef(uint32_t index, bool left) {
+    return (left ? &scale_data_[index].left_scale : &scale_data_[index].right_scale);
+  }
+  static ScalarHelper* scalar_helper_;  // Singleton Instance
+};
+
+}  // namespace sde
+
+#endif
+#endif  // __SCALAR_HELPER_H__
diff --git a/displayengine/libs/hwc/hwc_session.cpp b/displayengine/libs/hwc/hwc_session.cpp
index 3bc4fd1..40eae99 100644
--- a/displayengine/libs/hwc/hwc_session.cpp
+++ b/displayengine/libs/hwc/hwc_session.cpp
@@ -94,7 +94,7 @@
                 android::defaultServiceManager()->getService(android::String16(qservice_name)));
 
   if (qservice.get()) {
-    qservice->connect(this);
+    qservice->connect(android::sp<qClient::IQClient>(this));
   } else {
     DLOGE("Failed to acquire %s", qservice_name);
     return -EINVAL;
@@ -329,17 +329,18 @@
   switch (disp) {
   case HWC_DISPLAY_PRIMARY:
     status = hwc_session->display_primary_->SetPowerMode(mode);
+  // Set the power mode for virtual display while setting power mode for primary, as surfaceflinger
+  // does not invoke SetPowerMode() for virtual display.
+  case HWC_DISPLAY_VIRTUAL:
+    if (hwc_session->display_virtual_) {
+      status = hwc_session->display_virtual_->SetPowerMode(mode);
+    }
     break;
   case HWC_DISPLAY_EXTERNAL:
     if (hwc_session->display_external_) {
       status = hwc_session->display_external_->SetPowerMode(mode);
     }
     break;
-  case HWC_DISPLAY_VIRTUAL:
-    if (hwc_session->display_virtual_) {
-      status = hwc_session->display_virtual_->SetPowerMode(mode);
-    }
-    break;
   default:
     status = -EINVAL;
   }
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
diff --git a/hdmi_cec/Android.mk b/hdmi_cec/Android.mk
new file mode 100644
index 0000000..ed06da1
--- /dev/null
+++ b/hdmi_cec/Android.mk
@@ -0,0 +1,15 @@
+LOCAL_PATH := $(call my-dir)
+include $(LOCAL_PATH)/../common.mk
+include $(CLEAR_VARS)
+
+LOCAL_MODULE                  := hdmi_cec.$(TARGET_BOARD_PLATFORM)
+LOCAL_MODULE_RELATIVE_PATH    := hw
+LOCAL_MODULE_TAGS             := optional
+LOCAL_C_INCLUDES              := $(common_includes)
+LOCAL_SHARED_LIBRARIES        := $(common_libs) libqservice libbinder libqdutils
+
+LOCAL_CFLAGS                  := $(common_flags) -DLOG_TAG=\"qdhdmi_cec\"
+LOCAL_ADDITIONAL_DEPENDENCIES := $(common_deps)
+LOCAL_SRC_FILES               := qhdmi_cec.cpp \
+                                 QHDMIClient.cpp
+include $(BUILD_SHARED_LIBRARY)
diff --git a/hdmi_cec/QHDMIClient.cpp b/hdmi_cec/QHDMIClient.cpp
new file mode 100644
index 0000000..2b2b1e6
--- /dev/null
+++ b/hdmi_cec/QHDMIClient.cpp
@@ -0,0 +1,66 @@
+/*
+* Copyright (c) 2014 The Linux Foundation. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*    * Redistributions of source code must retain the above copyright
+*      notice, this list of conditions and the following disclaimer.
+*    * Redistributions in binary form must reproduce the above
+*      copyright notice, this list of conditions and the following
+*      disclaimer in the documentation and/or other materials provided
+*      with the distribution.
+*    * Neither the name of The Linux Foundation. nor the names of its
+*      contributors may be used to endorse or promote products derived
+*      from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define DEBUG 0
+#include <QServiceUtils.h>
+#include "QHDMIClient.h"
+
+using namespace android;
+using namespace qhdmicec;
+using namespace qService;
+
+namespace qClient {
+
+void QHDMIClient::binderDied(const wp<IBinder>& who __unused)
+{
+    ALOGW("%s: Display QService died", __FUNCTION__);
+}
+
+void QHDMIClient::onHdmiHotplug(int connected)
+{
+    ALOGD("%s: HDMI connected event connected: %d", __FUNCTION__, connected);
+    cec_hdmi_hotplug(mCtx, connected);
+}
+
+void QHDMIClient::onCECMessageRecieved(char *msg, ssize_t len)
+{
+    ALOGD_IF(DEBUG, "%s: CEC message received len: %zd", __FUNCTION__, len);
+    cec_receive_message(mCtx, msg, len);
+}
+
+void QHDMIClient::registerClient(sp<QHDMIClient>& client)
+{
+    sp<IServiceManager> sm = defaultServiceManager();
+    sp<IBinder> binder = sm->getService(String16("display.qservice"));
+    binder->linkToDeath(client);
+    mQService = interface_cast<IQService>(binder);
+    mQService->connect(interface_cast<IQHDMIClient>(client));
+}
+
+};
diff --git a/hdmi_cec/QHDMIClient.h b/hdmi_cec/QHDMIClient.h
new file mode 100644
index 0000000..9e54f2f
--- /dev/null
+++ b/hdmi_cec/QHDMIClient.h
@@ -0,0 +1,57 @@
+/*
+* Copyright (c) 2014 The Linux Foundation. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*    * Redistributions of source code must retain the above copyright
+*      notice, this list of conditions and the following disclaimer.
+*    * Redistributions in binary form must reproduce the above
+*      copyright notice, this list of conditions and the following
+*      disclaimer in the documentation and/or other materials provided
+*      with the distribution.
+*    * Neither the name of The Linux Foundation. nor the names of its
+*      contributors may be used to endorse or promote products derived
+*      from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "IQHDMIClient.h"
+#include "qhdmi_cec.h"
+#include <IQService.h>
+
+namespace qClient {
+
+class QHDMIClient: public android::IBinder::DeathRecipient,
+    public BnQHDMIClient
+{
+public:
+    QHDMIClient() {}
+
+    virtual void binderDied(const android::wp<android::IBinder>& who);
+
+    virtual void onHdmiHotplug(int connected);
+
+    virtual void onCECMessageRecieved(char *msg, ssize_t len);
+
+    void setCECContext(qhdmicec::cec_context_t* ctx) { mCtx = ctx; }
+
+    void registerClient(android::sp<QHDMIClient>& client);
+
+private:
+    qhdmicec::cec_context_t* mCtx;
+    android::sp<qService::IQService> mQService;
+
+};
+};
diff --git a/hdmi_cec/qhdmi_cec.cpp b/hdmi_cec/qhdmi_cec.cpp
new file mode 100644
index 0000000..6e46254
--- /dev/null
+++ b/hdmi_cec/qhdmi_cec.cpp
@@ -0,0 +1,519 @@
+/*
+* Copyright (c) 2014 The Linux Foundation. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*    * Redistributions of source code must retain the above copyright
+*      notice, this list of conditions and the following disclaimer.
+*    * Redistributions in binary form must reproduce the above
+*      copyright notice, this list of conditions and the following
+*      disclaimer in the documentation and/or other materials provided
+*      with the distribution.
+*    * Neither the name of The Linux Foundation. nor the names of its
+*      contributors may be used to endorse or promote products derived
+*      from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define DEBUG 0
+#define ATRACE_TAG (ATRACE_TAG_GRAPHICS | ATRACE_TAG_HAL)
+#include <cstdlib>
+#include <cutils/log.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <hardware/hdmi_cec.h>
+#include <utils/Trace.h>
+#include "qhdmi_cec.h"
+#include "QHDMIClient.h"
+
+namespace qhdmicec {
+
+const int NUM_HDMI_PORTS = 1;
+const int MAX_SYSFS_DATA = 128;
+const int MAX_CEC_FRAME_SIZE = 20;
+const int MAX_SEND_MESSAGE_RETRIES = 1;
+
+enum {
+    LOGICAL_ADDRESS_SET   =  1,
+    LOGICAL_ADDRESS_UNSET = -1,
+};
+
+// Offsets of members of struct hdmi_cec_msg
+// drivers/video/msm/mdss/mdss_hdmi_cec.c
+// XXX: Get this from a driver header
+enum {
+    CEC_OFFSET_SENDER_ID,
+    CEC_OFFSET_RECEIVER_ID,
+    CEC_OFFSET_OPCODE,
+    CEC_OFFSET_OPERAND,
+    CEC_OFFSET_FRAME_LENGTH = 18,
+    CEC_OFFSET_RETRANSMIT,
+};
+
+//Forward declarations
+static void cec_close_context(cec_context_t* ctx __unused);
+static int cec_enable(cec_context_t *ctx, int enable);
+static int cec_is_connected(const struct hdmi_cec_device* dev, int port_id);
+
+static ssize_t read_node(const char *path, char *data)
+{
+    ssize_t err = 0;
+    FILE *fp = NULL;
+    err = access(path, R_OK);
+    if (!err) {
+        fp = fopen(path, "r");
+        if (fp) {
+            err = fread(data, sizeof(char), MAX_SYSFS_DATA ,fp);
+            fclose(fp);
+        }
+    }
+    return err;
+}
+
+static ssize_t write_node(const char *path, const char *data, size_t len)
+{
+    ssize_t err = 0;
+    int fd = -1;
+    err = access(path, W_OK);
+    if (!err) {
+        fd = open(path, O_WRONLY);
+        errno = 0;
+        err = write(fd, data, len);
+        if (err < 0) {
+            err = -errno;
+        }
+        close(fd);
+    } else {
+        ALOGE("%s: Failed to access path: %s error: %s",
+                __FUNCTION__, path, strerror(errno));
+        err = -errno;
+    }
+    return err;
+}
+
+// Helper function to write integer values to the full sysfs path
+static ssize_t write_int_to_node(cec_context_t *ctx,
+        const char *path_postfix,
+        const int value)
+{
+    char sysfs_full_path[MAX_PATH_LENGTH];
+    char sysfs_data[MAX_SYSFS_DATA];
+    snprintf(sysfs_data, sizeof(sysfs_data), "%d",value);
+    snprintf(sysfs_full_path,sizeof(sysfs_full_path), "%s/%s",
+            ctx->fb_sysfs_path, path_postfix);
+    ssize_t err = write_node(sysfs_full_path, sysfs_data, strlen(sysfs_data));
+    return err;
+}
+
+static void hex_to_string(const char *msg, ssize_t len, char *str)
+{
+    //Functions assumes sufficient memory in str
+    char *ptr = str;
+    for(int i=0; i < len ; i++) {
+        ptr += snprintf(ptr, 3,  "%02X", msg[i]);
+        // Overwrite null termination of snprintf in all except the last byte
+        if (i < len - 1)
+            *ptr = ':';
+        ptr++;
+    }
+}
+
+static ssize_t cec_get_fb_node_number(cec_context_t *ctx)
+{
+    //XXX: Do this from a common utility library across the display HALs
+    const int MAX_FB_DEVICES = 2;
+    ssize_t len = 0;
+    char fb_type_path[MAX_PATH_LENGTH];
+    char fb_type[MAX_SYSFS_DATA];
+    const char *dtv_panel_str = "dtv panel";
+
+    for(int num = 0; num < MAX_FB_DEVICES; num++) {
+        snprintf(fb_type_path, sizeof(fb_type_path),"%s%d/msm_fb_type",
+                SYSFS_BASE,num);
+        ALOGD_IF(DEBUG, "%s: num: %d fb_type_path: %s", __FUNCTION__, num, fb_type_path);
+        len = read_node(fb_type_path, fb_type);
+        ALOGD_IF(DEBUG, "%s: fb_type:%s", __FUNCTION__, fb_type);
+        if(len > 0 && (strncmp(fb_type, dtv_panel_str, strlen(dtv_panel_str)) == 0)){
+            ALOGD_IF(DEBUG, "%s: Found DTV panel at fb%d", __FUNCTION__, num);
+            ctx->fb_num = num;
+            snprintf(ctx->fb_sysfs_path, sizeof(ctx->fb_sysfs_path),
+                    "%s%d", SYSFS_BASE, num);
+            break;
+        }
+    }
+    if (len < 0)
+        return len;
+    else
+        return 0;
+}
+
+static int cec_add_logical_address(const struct hdmi_cec_device* dev,
+        cec_logical_address_t addr)
+{
+    if (addr <  CEC_ADDR_TV || addr > CEC_ADDR_BROADCAST) {
+        ALOGE("%s: Received invalid address: %d ", __FUNCTION__, addr);
+        return -EINVAL;
+    }
+    cec_context_t* ctx = (cec_context_t*)(dev);
+    ctx->logical_address[addr] = LOGICAL_ADDRESS_SET;
+
+    //XXX: We can get multiple logical addresses here but we can only send one
+    //to the driver. Store locally for now
+    ssize_t err = write_int_to_node(ctx, "cec/logical_addr", addr);
+    ALOGI("%s: Allocated logical address: %d ", __FUNCTION__, addr);
+    return (int) err;
+}
+
+static void cec_clear_logical_address(const struct hdmi_cec_device* dev)
+{
+    cec_context_t* ctx = (cec_context_t*)(dev);
+    memset(ctx->logical_address, LOGICAL_ADDRESS_UNSET,
+            sizeof(ctx->logical_address));
+    //XXX: Find logical_addr that needs to be reset
+    write_int_to_node(ctx, "cec/logical_addr", 15);
+    ALOGD_IF(DEBUG, "%s: Cleared logical addresses", __FUNCTION__);
+}
+
+static int cec_get_physical_address(const struct hdmi_cec_device* dev,
+        uint16_t* addr)
+{
+    cec_context_t* ctx = (cec_context_t*)(dev);
+    char pa_path[MAX_PATH_LENGTH];
+    char pa_data[MAX_SYSFS_DATA];
+    snprintf (pa_path, sizeof(pa_path),"%s/pa",
+            ctx->fb_sysfs_path);
+    int err = (int) read_node(pa_path, pa_data);
+    *addr = (uint16_t) atoi(pa_data);
+    ALOGD_IF(DEBUG, "%s: Physical Address: 0x%x", __FUNCTION__, *addr);
+    if (err < 0)
+        return err;
+    else
+        return 0;
+}
+
+static int cec_send_message(const struct hdmi_cec_device* dev,
+        const cec_message_t* msg)
+{
+    ATRACE_CALL();
+    if(cec_is_connected(dev, 0) <= 0)
+        return HDMI_RESULT_FAIL;
+
+    cec_context_t* ctx = (cec_context_t*)(dev);
+    ALOGD_IF(DEBUG, "%s: initiator: %d destination: %d length: %u",
+            __FUNCTION__, msg->initiator, msg->destination,
+            (uint32_t) msg->length);
+
+    // Dump message received from framework
+    char dump[128];
+    if(msg->length > 0) {
+        hex_to_string((char*)msg->body, msg->length, dump);
+        ALOGD_IF(DEBUG, "%s: message from framework: %s", __FUNCTION__, dump);
+    }
+
+    char write_msg_path[MAX_PATH_LENGTH];
+    char write_msg[MAX_CEC_FRAME_SIZE];
+    memset(write_msg, 0, sizeof(write_msg));
+    // See definition of struct hdmi_cec_msg in driver code
+    // drivers/video/msm/mdss/mdss_hdmi_cec.c
+    // Write header block
+    // XXX: Include this from header in kernel
+    write_msg[CEC_OFFSET_SENDER_ID] = msg->initiator;
+    write_msg[CEC_OFFSET_RECEIVER_ID] = msg->destination;
+    //Kernel splits opcode/operand, but Android sends it in one byte array
+    write_msg[CEC_OFFSET_OPCODE] = msg->body[0];
+    if(msg->length > 1) {
+        memcpy(&write_msg[CEC_OFFSET_OPERAND], &msg->body[1],
+                sizeof(char)*(msg->length - 1));
+    }
+    //msg length + initiator + destination
+    write_msg[CEC_OFFSET_FRAME_LENGTH] = (unsigned char) (msg->length + 1);
+    hex_to_string(write_msg, sizeof(write_msg), dump);
+    ALOGD_IF(DEBUG, "%s: message to driver: %s", __FUNCTION__, dump);
+    snprintf(write_msg_path, sizeof(write_msg_path), "%s/cec/wr_msg",
+            ctx->fb_sysfs_path);
+    int retry_count = 0;
+    ssize_t err = 0;
+    //HAL spec requires us to retry at least once.
+    while (true) {
+        err = write_node(write_msg_path, write_msg, sizeof(write_msg));
+        retry_count++;
+        if (err == -EAGAIN && retry_count <= MAX_SEND_MESSAGE_RETRIES) {
+            ALOGE("%s: CEC line busy, retrying", __FUNCTION__);
+        } else {
+            break;
+        }
+    }
+
+    if (err < 0) {
+       if (err == -ENXIO) {
+           ALOGI("%s: No device exists with the destination address",
+                   __FUNCTION__);
+           return HDMI_RESULT_NACK;
+       } else if (err == -EAGAIN) {
+            ALOGE("%s: CEC line is busy, max retry count exceeded",
+                    __FUNCTION__);
+            return HDMI_RESULT_BUSY;
+        } else {
+            return HDMI_RESULT_FAIL;
+            ALOGE("%s: Failed to send CEC message err: %zd - %s",
+                    __FUNCTION__, err, strerror(int(-err)));
+        }
+    } else {
+        ALOGD_IF(DEBUG, "%s: Sent CEC message - %zd bytes written",
+                __FUNCTION__, err);
+        return HDMI_RESULT_SUCCESS;
+    }
+}
+
+void cec_receive_message(cec_context_t *ctx, char *msg, ssize_t len)
+{
+    if(!ctx->system_control)
+        return;
+
+    char dump[128];
+    if(len > 0) {
+        hex_to_string(msg, len, dump);
+        ALOGD_IF(DEBUG, "%s: Message from driver: %s", __FUNCTION__, dump);
+    }
+
+    hdmi_event_t event;
+    event.type = HDMI_EVENT_CEC_MESSAGE;
+    event.dev = (hdmi_cec_device *) ctx;
+    // Remove initiator/destination from this calculation
+    event.cec.length = msg[CEC_OFFSET_FRAME_LENGTH] - 1;
+    event.cec.initiator = (cec_logical_address_t) msg[CEC_OFFSET_SENDER_ID];
+    event.cec.destination = (cec_logical_address_t) msg[CEC_OFFSET_RECEIVER_ID];
+    //Copy opcode and operand
+    memcpy(event.cec.body, &msg[CEC_OFFSET_OPCODE], event.cec.length);
+    hex_to_string((char *) event.cec.body, event.cec.length, dump);
+    ALOGD_IF(DEBUG, "%s: Message to framework: %s", __FUNCTION__, dump);
+    ctx->callback.callback_func(&event, ctx->callback.callback_arg);
+}
+
+void cec_hdmi_hotplug(cec_context_t *ctx, int connected)
+{
+    //Ignore unplug events when system control is disabled
+    if(!ctx->system_control && connected == 0)
+        return;
+    hdmi_event_t event;
+    event.type = HDMI_EVENT_HOT_PLUG;
+    event.dev = (hdmi_cec_device *) ctx;
+    event.hotplug.connected = connected ? HDMI_CONNECTED : HDMI_NOT_CONNECTED;
+    ctx->callback.callback_func(&event, ctx->callback.callback_arg);
+}
+
+static void cec_register_event_callback(const struct hdmi_cec_device* dev,
+            event_callback_t callback, void* arg)
+{
+    ALOGD_IF(DEBUG, "%s: Registering callback", __FUNCTION__);
+    cec_context_t* ctx = (cec_context_t*)(dev);
+    ctx->callback.callback_func = callback;
+    ctx->callback.callback_arg = arg;
+}
+
+static void cec_get_version(const struct hdmi_cec_device* dev, int* version)
+{
+    cec_context_t* ctx = (cec_context_t*)(dev);
+    *version = ctx->version;
+    ALOGD_IF(DEBUG, "%s: version: %d", __FUNCTION__, *version);
+}
+
+static void cec_get_vendor_id(const struct hdmi_cec_device* dev,
+        uint32_t* vendor_id)
+{
+    cec_context_t* ctx = (cec_context_t*)(dev);
+    *vendor_id = ctx->vendor_id;
+    ALOGD_IF(DEBUG, "%s: vendor id: %u", __FUNCTION__, *vendor_id);
+}
+
+static void cec_get_port_info(const struct hdmi_cec_device* dev,
+            struct hdmi_port_info* list[], int* total)
+{
+    ALOGD_IF(DEBUG, "%s: Get port info", __FUNCTION__);
+    cec_context_t* ctx = (cec_context_t*)(dev);
+    *total = NUM_HDMI_PORTS;
+    *list = ctx->port_info;
+}
+
+static void cec_set_option(const struct hdmi_cec_device* dev, int flag,
+        int value)
+{
+    cec_context_t* ctx = (cec_context_t*)(dev);
+    switch (flag) {
+        case HDMI_OPTION_WAKEUP:
+            ALOGD_IF(DEBUG, "%s: Wakeup: value: %d", __FUNCTION__, value);
+            //XXX
+            break;
+        case HDMI_OPTION_ENABLE_CEC:
+            ALOGD_IF(DEBUG, "%s: Enable CEC: value: %d", __FUNCTION__, value);
+            cec_enable(ctx, value? 1 : 0);
+            break;
+        case HDMI_OPTION_SYSTEM_CEC_CONTROL:
+            ALOGD_IF(DEBUG, "%s: system_control: value: %d",
+                    __FUNCTION__, value);
+            ctx->system_control = !!value;
+            break;
+    }
+}
+
+static void cec_set_audio_return_channel(const struct hdmi_cec_device* dev,
+        int flag)
+{
+    cec_context_t* ctx = (cec_context_t*)(dev);
+    ctx->arc_enabled = flag ? true : false;
+    ALOGD_IF(DEBUG, "%s: ARC flag: %d", __FUNCTION__, flag);
+}
+
+static int cec_is_connected(const struct hdmi_cec_device* dev, int port_id)
+{
+    // Ignore port_id since we have only one port
+    int connected = 0;
+    cec_context_t* ctx = (cec_context_t*)(dev);
+    char connected_path[MAX_PATH_LENGTH];
+    char connected_data[MAX_SYSFS_DATA];
+    snprintf (connected_path, sizeof(connected_path),"%s/connected",
+            ctx->fb_sysfs_path);
+    ssize_t err = read_node(connected_path, connected_data);
+    connected = atoi(connected_data);
+
+    ALOGD_IF(DEBUG, "%s: HDMI at port %d is - %s", __FUNCTION__, port_id,
+            connected ? "connected":"disconnected");
+    if (err < 0)
+        return (int) err;
+    else
+        return connected;
+}
+
+static int cec_device_close(struct hw_device_t *dev)
+{
+    ALOGD_IF(DEBUG, "%s: Close CEC HAL ", __FUNCTION__);
+    if (!dev) {
+        ALOGE("%s: NULL device pointer", __FUNCTION__);
+        return -EINVAL;
+    }
+    cec_context_t* ctx = (cec_context_t*)(dev);
+    cec_close_context(ctx);
+    free(dev);
+    return 0;
+}
+
+static int cec_enable(cec_context_t *ctx, int enable)
+{
+    ssize_t err;
+    // Enable CEC
+    // TODO: Set to 0x3 to enable CEC wakeup once driver has support
+    int value = enable ? 0x1 : 0x0;
+    err = write_int_to_node(ctx, "cec/enable", value);
+    if(err < 0) {
+        ALOGE("%s: Failed to toggle CEC: enable: %d",
+                __FUNCTION__, enable);
+        return (int) err;
+    }
+    ctx->enabled = enable;
+    return 0;
+}
+
+static void cec_init_context(cec_context_t *ctx)
+{
+    ALOGD_IF(DEBUG, "%s: Initializing context", __FUNCTION__);
+    cec_get_fb_node_number(ctx);
+
+    //Initialize ports - We support only one output port
+    ctx->port_info = new hdmi_port_info[NUM_HDMI_PORTS];
+    ctx->port_info[0].type = HDMI_OUTPUT;
+    ctx->port_info[0].port_id = 1;
+    ctx->port_info[0].cec_supported = 1;
+    //XXX: Enable ARC if supported
+    ctx->port_info[0].arc_supported = 0;
+    cec_get_physical_address((hdmi_cec_device *) ctx,
+            &ctx->port_info[0].physical_address );
+
+    ctx->version = 0x4;
+    ctx->vendor_id = 0xA47733;
+    cec_clear_logical_address((hdmi_cec_device_t*)ctx);
+
+    //Set up listener for HDMI events
+    ctx->disp_client = new qClient::QHDMIClient();
+    ctx->disp_client->setCECContext(ctx);
+    ctx->disp_client->registerClient(ctx->disp_client);
+
+    //Enable CEC - framework expects it to be enabled by default
+    cec_enable(ctx, true);
+
+    ALOGD("%s: CEC enabled", __FUNCTION__);
+}
+
+static void cec_close_context(cec_context_t* ctx __unused)
+{
+    ALOGD("%s: Closing context", __FUNCTION__);
+}
+
+static int cec_device_open(const struct hw_module_t* module,
+        const char* name,
+        struct hw_device_t** device)
+{
+    ALOGD_IF(DEBUG, "%s: name: %s", __FUNCTION__, name);
+    int status = -EINVAL;
+    if (!strcmp(name, HDMI_CEC_HARDWARE_INTERFACE )) {
+        struct cec_context_t *dev;
+        dev = (cec_context_t *) calloc (1, sizeof(*dev));
+        if (dev) {
+            cec_init_context(dev);
+
+            //Setup CEC methods
+            dev->device.common.tag       = HARDWARE_DEVICE_TAG;
+            dev->device.common.version   = HDMI_CEC_DEVICE_API_VERSION_1_0;
+            dev->device.common.module    = const_cast<hw_module_t* >(module);
+            dev->device.common.close     = cec_device_close;
+            dev->device.add_logical_address = cec_add_logical_address;
+            dev->device.clear_logical_address = cec_clear_logical_address;
+            dev->device.get_physical_address = cec_get_physical_address;
+            dev->device.send_message = cec_send_message;
+            dev->device.register_event_callback = cec_register_event_callback;
+            dev->device.get_version = cec_get_version;
+            dev->device.get_vendor_id = cec_get_vendor_id;
+            dev->device.get_port_info = cec_get_port_info;
+            dev->device.set_option = cec_set_option;
+            dev->device.set_audio_return_channel = cec_set_audio_return_channel;
+            dev->device.is_connected = cec_is_connected;
+
+            *device = &dev->device.common;
+            status = 0;
+        } else {
+            status = -EINVAL;
+        }
+    }
+    return status;
+}
+}; //namespace qhdmicec
+
+// Standard HAL module, should be outside qhdmicec namespace
+static struct hw_module_methods_t cec_module_methods = {
+        .open = qhdmicec::cec_device_open
+};
+
+hdmi_module_t HAL_MODULE_INFO_SYM = {
+    .common = {
+        .tag = HARDWARE_MODULE_TAG,
+        .version_major = 1,
+        .version_minor = 0,
+        .id = HDMI_CEC_HARDWARE_MODULE_ID,
+        .name = "QTI HDMI CEC module",
+        .author = "The Linux Foundation",
+        .methods = &cec_module_methods,
+    }
+};
+
+
diff --git a/hdmi_cec/qhdmi_cec.h b/hdmi_cec/qhdmi_cec.h
new file mode 100644
index 0000000..aa97620
--- /dev/null
+++ b/hdmi_cec/qhdmi_cec.h
@@ -0,0 +1,74 @@
+/*
+* Copyright (c) 2014 The Linux Foundation. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*    * Redistributions of source code must retain the above copyright
+*      notice, this list of conditions and the following disclaimer.
+*    * Redistributions in binary form must reproduce the above
+*      copyright notice, this list of conditions and the following
+*      disclaimer in the documentation and/or other materials provided
+*      with the distribution.
+*    * Neither the name of The Linux Foundation. nor the names of its
+*      contributors may be used to endorse or promote products derived
+*      from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef QHDMI_CEC_H
+#define QHDMI_CEC_H
+
+#include <hardware/hdmi_cec.h>
+#include <utils/RefBase.h>
+
+namespace qClient {
+    class QHDMIClient;
+};
+
+namespace qhdmicec {
+
+#define SYSFS_BASE  "/sys/class/graphics/fb"
+#define MAX_PATH_LENGTH  128
+
+struct cec_callback_t {
+    // Function in HDMI service to call back on CEC messages
+    event_callback_t callback_func;
+    // This stores the object to pass back to the framework
+    void* callback_arg;
+
+};
+
+struct cec_context_t {
+    hdmi_cec_device_t device;    // Device for HW module
+    cec_callback_t callback;     // Struct storing callback object
+    bool enabled;
+    bool arc_enabled;
+    bool system_control;         // If true, HAL/driver handle CEC messages
+    int fb_num;                  // Framebuffer node for HDMI
+    char fb_sysfs_path[MAX_PATH_LENGTH];
+    hdmi_port_info *port_info;   // HDMI port info
+
+    // Logical address is stored in an array, the index of the array is the
+    // logical address and the value in the index shows whether it is set or not
+    int logical_address[CEC_ADDR_BROADCAST];
+    int version;
+    uint32_t vendor_id;
+    android::sp<qClient::QHDMIClient> disp_client;
+};
+
+void cec_receive_message(cec_context_t *ctx, char *msg, ssize_t len);
+void cec_hdmi_hotplug(cec_context_t *ctx, int connected);
+
+}; //namespace
+#endif /* end of include guard: QHDMI_CEC_H */
diff --git a/libcopybit/copybit.cpp b/libcopybit/copybit.cpp
index e4624e0..9783896 100644
--- a/libcopybit/copybit.cpp
+++ b/libcopybit/copybit.cpp
@@ -512,7 +512,11 @@
 
             // Set Color Space for MDP to configure CSC matrix
             req->color_space = ITU_R_601;
-            MetaData_t *metadata = (MetaData_t *)src_hnd->base_metadata;
+            MetaData_t *metadata = NULL;
+
+            if (src_hnd != NULL)
+                metadata = (MetaData_t *)src_hnd->base_metadata;
+
             if (metadata && (metadata->operation & UPDATE_COLOR_SPACE)) {
                 req->color_space = metadata->colorSpace;
             }
@@ -722,6 +726,11 @@
     }
     copybit_context_t *ctx;
     ctx = (copybit_context_t *)malloc(sizeof(copybit_context_t));
+
+    if (ctx == NULL ) {
+       return COPYBIT_FAILURE;
+    }
+
     memset(ctx, 0, sizeof(*ctx));
 
     ctx->device.common.tag = HARDWARE_DEVICE_TAG;
diff --git a/libgralloc/adreno_utils.h b/libgralloc/adreno_utils.h
index 6cb7810..78f49da 100644
--- a/libgralloc/adreno_utils.h
+++ b/libgralloc/adreno_utils.h
@@ -36,6 +36,7 @@
     ADRENO_PIXELFORMAT_NV12          = 103,
     ADRENO_PIXELFORMAT_YUY2          = 107,
     ADRENO_PIXELFORMAT_B4G4R4A4      = 115,
+    ADRENO_PIXELFORMAT_NV12_EXT      = 506,  // NV12 with non-std alignment and offsets
     ADRENO_PIXELFORMAT_R8G8B8        = 508,  // GL_RGB8
     ADRENO_PIXELFORMAT_A1B5G5R5      = 519,  // GL_RGB5_A1
     ADRENO_PIXELFORMAT_R8G8B8X8_SRGB = 520,  // GL_SRGB8
diff --git a/libgralloc/alloc_controller.cpp b/libgralloc/alloc_controller.cpp
index fd98154..f70ead3 100644
--- a/libgralloc/alloc_controller.cpp
+++ b/libgralloc/alloc_controller.cpp
@@ -308,9 +308,10 @@
         case HAL_PIXEL_FORMAT_sRGB_A_8888:
             return ADRENO_PIXELFORMAT_R8G8B8A8_SRGB;
         case HAL_PIXEL_FORMAT_NV12_ENCODEABLE:
+            return ADRENO_PIXELFORMAT_NV12;
         case HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS:
         case HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS_UBWC:
-            return ADRENO_PIXELFORMAT_NV12;
+            return ADRENO_PIXELFORMAT_NV12_EXT;
         default:
             ALOGE("%s: No map for format: 0x%x", __FUNCTION__, hal_format);
             break;
diff --git a/libgralloc/gralloc_priv.h b/libgralloc/gralloc_priv.h
index e9b9d9d..f973b76 100755
--- a/libgralloc/gralloc_priv.h
+++ b/libgralloc/gralloc_priv.h
@@ -33,44 +33,42 @@
 #define ROUND_UP_PAGESIZE(x) ( (((unsigned long)(x)) + PAGE_SIZE-1)  & \
                                (~(PAGE_SIZE-1)) )
 
-enum {
-    /* gralloc usage bits indicating the type
-     * of allocation that should be used */
+/* Gralloc usage bits indicating the type of allocation that should be used */
+/* SYSTEM heap comes from kernel vmalloc, can never be uncached,
+ * is not secured */
+#define GRALLOC_USAGE_PRIVATE_SYSTEM_HEAP     GRALLOC_USAGE_PRIVATE_0
 
-    /* SYSTEM heap comes from kernel vmalloc,
-     * can never be uncached, is not secured*/
-    GRALLOC_USAGE_PRIVATE_SYSTEM_HEAP     =       GRALLOC_USAGE_PRIVATE_0,
+/* Non linear, Universal Bandwidth Compression */
+#define GRALLOC_USAGE_PRIVATE_ALLOC_UBWC      GRALLOC_USAGE_PRIVATE_1
 
-    /* Non linear, Universal Bandwidth Compression */
-    GRALLOC_USAGE_PRIVATE_ALLOC_UBWC      =       GRALLOC_USAGE_PRIVATE_1,
+/* IOMMU heap comes from manually allocated pages, can be cached/uncached,
+ * is not secured */
+#define GRALLOC_USAGE_PRIVATE_IOMMU_HEAP      GRALLOC_USAGE_PRIVATE_2
 
-    /* IOMMU heap comes from manually allocated pages,
-     * can be cached/uncached, is not secured */
-    GRALLOC_USAGE_PRIVATE_IOMMU_HEAP      =       GRALLOC_USAGE_PRIVATE_2,
-    /* MM heap is a carveout heap for video, can be secured*/
-    GRALLOC_USAGE_PRIVATE_MM_HEAP         =       GRALLOC_USAGE_PRIVATE_3,
-    /* ADSP heap is a carveout heap, is not secured*/
-    GRALLOC_USAGE_PRIVATE_ADSP_HEAP       =       0x01000000,
+/* MM heap is a carveout heap for video, can be secured */
+#define GRALLOC_USAGE_PRIVATE_MM_HEAP         GRALLOC_USAGE_PRIVATE_3
 
-    /* Set this for allocating uncached memory (using O_DSYNC)
-     * cannot be used with noncontiguous heaps */
-    GRALLOC_USAGE_PRIVATE_UNCACHED        =       0x02000000,
+/* ADSP heap is a carveout heap, is not secured */
+#define GRALLOC_USAGE_PRIVATE_ADSP_HEAP       0x01000000
 
-    /* Buffer content should be displayed on an primary display only */
-    GRALLOC_USAGE_PRIVATE_INTERNAL_ONLY   =       0x04000000,
+/* Set this for allocating uncached memory (using O_DSYNC),
+ * cannot be used with noncontiguous heaps */
+#define GRALLOC_USAGE_PRIVATE_UNCACHED        0x02000000
 
-    /* Buffer content should be displayed on an external display only */
-    GRALLOC_USAGE_PRIVATE_EXTERNAL_ONLY   =       0x08000000,
+/* Buffer content should be displayed on an primary display only */
+#define GRALLOC_USAGE_PRIVATE_INTERNAL_ONLY   0x04000000
 
-    /* This flag is set for WFD usecase */
-    GRALLOC_USAGE_PRIVATE_WFD             =       0x00200000,
+/* Buffer content should be displayed on an external display only */
+#define GRALLOC_USAGE_PRIVATE_EXTERNAL_ONLY   0x08000000
 
-    /* CAMERA heap is a carveout heap for camera, is not secured*/
-    GRALLOC_USAGE_PRIVATE_CAMERA_HEAP     =       0x00400000,
+/* This flag is set for WFD usecase */
+#define GRALLOC_USAGE_PRIVATE_WFD             0x00200000
 
-    /* This flag is used for SECURE display usecase */
-    GRALLOC_USAGE_PRIVATE_SECURE_DISPLAY  =       0x00800000,
-};
+/* CAMERA heap is a carveout heap for camera, is not secured */
+#define GRALLOC_USAGE_PRIVATE_CAMERA_HEAP     0x00400000
+
+/* This flag is used for SECURE display usecase */
+#define GRALLOC_USAGE_PRIVATE_SECURE_DISPLAY  0x00800000
 
 /* define Gralloc perform */
 #define GRALLOC_MODULE_PERFORM_CREATE_HANDLE_FROM_BUFFER 1
@@ -84,6 +82,7 @@
 #define GRALLOC_MODULE_PERFORM_GET_COLOR_SPACE_FROM_HANDLE 6
 #define GRALLOC_MODULE_PERFORM_GET_YUV_PLANE_INFO 7
 #define GRALLOC_MODULE_PERFORM_GET_MAP_SECURE_BUFFER_INFO 8
+#define GRALLOC_MODULE_PERFORM_GET_UBWC_FLAG 9
 
 #define GRALLOC_HEAP_MASK   (GRALLOC_USAGE_PRIVATE_SYSTEM_HEAP    |\
                              GRALLOC_USAGE_PRIVATE_IOMMU_HEAP     |\
diff --git a/libgralloc/mapper.cpp b/libgralloc/mapper.cpp
index 44f4fb2..5382300 100644
--- a/libgralloc/mapper.cpp
+++ b/libgralloc/mapper.cpp
@@ -447,6 +447,17 @@
                 }
             } break;
 
+        case GRALLOC_MODULE_PERFORM_GET_UBWC_FLAG:
+            {
+                private_handle_t* hnd =  va_arg(args, private_handle_t*);
+                int *flag = va_arg(args, int *);
+                if (private_handle_t::validate(hnd)) {
+                    return res;
+                }
+                *flag = hnd->flags & private_handle_t::PRIV_FLAGS_UBWC_ALIGNED;
+                res = 0;
+            } break;
+
         default:
             break;
     }
diff --git a/libhdmi/hdmi.cpp b/libhdmi/hdmi.cpp
index bca7a0b..fb6493a 100644
--- a/libhdmi/hdmi.cpp
+++ b/libhdmi/hdmi.cpp
@@ -95,6 +95,7 @@
     mUnderscanSupported(false), mMDPDownscaleEnabled(false)
 {
     memset(&mVInfo, 0, sizeof(mVInfo));
+    mFbNum = qdutils::getHDMINode();
 
     mDisplayId = HWC_DISPLAY_EXTERNAL;
     // Update the display if HDMI is connected as primary
@@ -102,7 +103,6 @@
         mDisplayId = HWC_DISPLAY_PRIMARY;
     }
 
-    mFbNum = overlay::Overlay::getInstance()->getFbForDpy(mDisplayId);
     // Disable HPD at start if HDMI is external, it will be enabled later
     // when the display powers on
     // This helps for framework reboot or adb shell stop/start
@@ -412,8 +412,9 @@
     // for all the timing info read, get the best config
     for (int configIndex = 0; configIndex < mModeCount; configIndex++) {
         currentModeInfo = mDisplayConfigs[configIndex];
+        edidMode = mEDIDModes[configIndex];
 
-        if (!currentModeInfo.supported) {
+        if (!isValidMode(edidMode)) {
             ALOGD("%s EDID Mode %d is not supported", __FUNCTION__, edidMode);
             continue;
         }
@@ -684,8 +685,7 @@
 }
 
 bool HDMIDisplay::isHDMIPrimaryDisplay() {
-    int hdmiNode = qdutils::getHDMINode();
-    return (hdmiNode == HWC_DISPLAY_PRIMARY);
+    return (mFbNum == HWC_DISPLAY_PRIMARY);
 }
 
 int HDMIDisplay::getConnectedState() {
@@ -739,6 +739,124 @@
     return 0;
 }
 
+static const char* getS3DStringFromMode(int s3dMode) {
+    const char* ret ;
+    switch(s3dMode) {
+    case HDMI_S3D_NONE:
+        ret = "None";
+        break;
+    case HDMI_S3D_SIDE_BY_SIDE:
+        ret = "SSH";
+        break;
+    case HDMI_S3D_TOP_AND_BOTTOM:
+        ret = "TAB";
+        break;
+    //FP (FramePacked) mode is not supported in the HAL
+    default:
+        ALOGD("%s: Unsupported s3d mode: %d", __FUNCTION__, s3dMode);
+        ret = NULL;
+    }
+    return ret;
+}
+
+bool HDMIDisplay::isS3DModeSupported(int s3dMode) {
+    if(s3dMode == HDMI_S3D_NONE)
+        return true;
+
+    char s3dEdidStr[PAGE_SIZE] = {'\0'};
+
+    const char *s3dModeString = getS3DStringFromMode(s3dMode);
+
+    if(s3dModeString == NULL)
+        return false;
+
+    int s3dEdidNode = openDeviceNode("edid_3d_modes", O_RDONLY);
+    if(s3dEdidNode >= 0) {
+        ssize_t len = read(s3dEdidNode, s3dEdidStr, sizeof(s3dEdidStr)-1);
+        if (len > 0) {
+            ALOGI("%s: s3dEdidStr: %s mCurrentMode:%d", __FUNCTION__,
+                    s3dEdidStr, mCurrentMode);
+            //Three level inception!
+            //The string looks like 16=SSH,4=FP:TAB:SSH,5=FP:SSH,32=FP:TAB:SSH
+            char *saveptr_l1, *saveptr_l2, *saveptr_l3;
+            char *l1, *l2, *l3;
+            int mode = 0;
+            l1 = strtok_r(s3dEdidStr,",", &saveptr_l1);
+            while (l1 != NULL) {
+                l2 = strtok_r(l1, "=", &saveptr_l2);
+                if (l2 != NULL)
+                    mode = atoi(l2);
+                while (l2 != NULL) {
+                    if (mode != mCurrentMode) {
+                        break;
+                    }
+                    l3 = strtok_r(l2, ":", &saveptr_l3);
+                    while (l3 != NULL) {
+                        if (strncmp(l3, s3dModeString,
+                                    strlen(s3dModeString)) == 0) {
+                            close(s3dEdidNode);
+                            return true;
+                        }
+                        l3 = strtok_r(NULL, ":", &saveptr_l3);
+                    }
+                    l2 = strtok_r(NULL, "=", &saveptr_l2);
+                }
+                l1 = strtok_r(NULL, ",", &saveptr_l1);
+            }
+
+        }
+    } else {
+        ALOGI("%s: /sys/class/graphics/fb%d/edid_3d_modes could not be opened : %s",
+                __FUNCTION__, mFbNum, strerror(errno));
+    }
+    close(s3dEdidNode);
+    return false;
+}
+
+bool HDMIDisplay::writeS3DMode(int s3dMode) {
+  bool ret = true;
+    if(mFbNum != -1) {
+        int hdmiS3DModeFile = openDeviceNode("s3d_mode", O_RDWR);
+        if(hdmiS3DModeFile >=0 ) {
+            char curModeStr[PROPERTY_VALUE_MAX];
+            int currentS3DMode = -1;
+            size_t len = read(hdmiS3DModeFile, curModeStr, sizeof(curModeStr) - 1);
+            if(len > 0) {
+                currentS3DMode = atoi(curModeStr);
+            } else {
+                ret = false;
+                ALOGE("%s: Failed to read s3d_mode", __FUNCTION__);
+            }
+
+            if (currentS3DMode >=0 && currentS3DMode != s3dMode) {
+                ssize_t err = -1;
+                ALOGD_IF(DEBUG, "%s: mode = %d",
+                        __FUNCTION__, s3dMode);
+                char mode[PROPERTY_VALUE_MAX];
+                snprintf(mode,sizeof(mode),"%d",s3dMode);
+                err = write(hdmiS3DModeFile, mode, sizeof(mode));
+                if (err <= 0) {
+                    ALOGE("%s: file write failed 's3d_mode'", __FUNCTION__);
+                    ret = false;
+                }
+            }
+            close(hdmiS3DModeFile);
+        }
+    }
+    return ret;
+}
+
+bool HDMIDisplay::configure3D(int s3dMode) {
+    if(isS3DModeSupported(s3dMode)) {
+        if(!writeS3DMode(s3dMode))
+            return false;
+    } else {
+        ALOGE("%s: 3D mode: %d is not supported", __FUNCTION__, s3dMode);
+        return false;
+    }
+    return true;
+}
+
 // returns false if the xres or yres of the new config do
 // not match the current config
 bool HDMIDisplay::isValidConfigChange(int newConfig) {
diff --git a/libhdmi/hdmi.h b/libhdmi/hdmi.h
index 32c48ff..d1d5759 100644
--- a/libhdmi/hdmi.h
+++ b/libhdmi/hdmi.h
@@ -64,6 +64,8 @@
     int getAttrForConfig(int config, uint32_t& xres,
             uint32_t& yres, uint32_t& refresh, uint32_t& fps) const;
     int getDisplayConfigs(uint32_t* configs, size_t* numConfigs) const;
+    bool configure3D(int s3dMode);
+    bool isS3DModeSupported(int s3dMode);
 
 private:
     int getModeCount() const;
@@ -87,6 +89,7 @@
     void requestNewPage(int pageNumber);
     void readConfigs();
     bool readResFile(char* configBuffer);
+    bool writeS3DMode(int s3dMode);
 
     int mFd;
     int mFbNum;
diff --git a/libhwcomposer/hwc.cpp b/libhwcomposer/hwc.cpp
index adf48e7..7c69633 100644
--- a/libhwcomposer/hwc.cpp
+++ b/libhwcomposer/hwc.cpp
@@ -635,7 +635,11 @@
             }
         }
 
-        int lSplit = getLeftSplit(ctx, dpy);
+        /* When source split is enabled, right ROI will always be NULL since the
+         * ROI for the whole panel generated in a single coordinate system will
+         * be populuated in left ROI. So leave the right ROI untouched */
+        int lSplit = qdutils::MDPVersion::getInstance().isSrcSplit() ? 0
+                :getLeftSplit(ctx, dpy);
         qhwc::ovutils::Dim lRoi = qhwc::ovutils::Dim(
             ctx->listStats[dpy].lRoi.left,
             ctx->listStats[dpy].lRoi.top,
@@ -889,6 +893,7 @@
         dumpsys_log(aBuf, ovDump);
         ovDump[0] = '\0';
     }
+    dumpsys_log(aBuf, "Copybit::isAbcInUse=%d\n\n",isAbcInUse(ctx) ? 1 : 0);
     strlcpy(buff, aBuf.string(), buff_len);
 }
 
diff --git a/libhwcomposer/hwc_copybit.cpp b/libhwcomposer/hwc_copybit.cpp
index 65482d7..68f168a 100644
--- a/libhwcomposer/hwc_copybit.cpp
+++ b/libhwcomposer/hwc_copybit.cpp
@@ -131,8 +131,10 @@
     return renderArea;
 }
 
-bool CopyBit::isLayerChanging(hwc_display_contents_1_t *list, int k) {
+bool CopyBit::isLayerChanging(hwc_context_t *ctx,
+                                 hwc_display_contents_1_t *list, int k) {
     if((mLayerCache.hnd[k] != list->hwLayers[k].handle) ||
+            (mLayerCache.drop[k] != ctx->copybitDrop[k]) ||
             (mLayerCache.displayFrame[k].left !=
                          list->hwLayers[k].displayFrame.left) ||
             (mLayerCache.displayFrame[k].top !=
@@ -161,7 +163,7 @@
     int updatingLayerCount = 0;
     for (int k = ctx->listStats[dpy].numAppLayers-1; k >= 0 ; k--){
        //swap rect will kick in only for single updating layer
-       if(isLayerChanging(list, k)) {
+       if(isLayerChanging(ctx, list, k)) {
            updatingLayerCount ++;
            if(updatingLayerCount == 1)
              changingLayerIndex = k;
@@ -1233,6 +1235,7 @@
    for (int i=0; i<ctx->listStats[dpy].numAppLayers; i++){
       hnd[i] = list->hwLayers[i].handle;
       displayFrame[i] = list->hwLayers[i].displayFrame;
+      drop[i] = ctx->copybitDrop[i];
    }
 }
 
diff --git a/libhwcomposer/hwc_copybit.h b/libhwcomposer/hwc_copybit.h
index 6ead4a7..4442afc 100644
--- a/libhwcomposer/hwc_copybit.h
+++ b/libhwcomposer/hwc_copybit.h
@@ -61,6 +61,7 @@
       int layerCount;
       buffer_handle_t hnd[MAX_NUM_APP_LAYERS];
       hwc_rect_t displayFrame[MAX_NUM_APP_LAYERS];
+      bool drop[MAX_NUM_APP_LAYERS];
       /* c'tor */
       LayerCache();
       /* clear caching info*/
@@ -135,7 +136,8 @@
                   int dpy);
     int checkDirtyRect(hwc_context_t *ctx, hwc_display_contents_1_t *list,
                   int dpy);
-    bool isLayerChanging(hwc_display_contents_1_t *list, int k);
+    bool isLayerChanging(hwc_context_t *ctx,
+                            hwc_display_contents_1_t *list, int k);
 };
 
 }; //namespace qhwc
diff --git a/libhwcomposer/hwc_fbupdate.cpp b/libhwcomposer/hwc_fbupdate.cpp
index 8660740..ef83008 100644
--- a/libhwcomposer/hwc_fbupdate.cpp
+++ b/libhwcomposer/hwc_fbupdate.cpp
@@ -25,6 +25,7 @@
 #include <overlayRotator.h>
 #include "hwc_fbupdate.h"
 #include "mdp_version.h"
+#include <video/msm_hdmi_modes.h>
 
 using namespace qdutils;
 using namespace overlay;
@@ -499,10 +500,25 @@
     hwc_rect_t dstL = displayFrame;
     hwc_rect_t dstR = displayFrame;
 
+    if(ctx->dpyAttr[mDpy].s3dMode == HDMI_S3D_SIDE_BY_SIDE) {
+        dstL.left = displayFrame.left/2;
+        dstL.right = displayFrame.right/2;
+
+        dstR.left = mAlignedFBWidth/2 + displayFrame.left/2;
+        dstR.right = mAlignedFBWidth/2 + displayFrame.right/2;
+    } else if(ctx->dpyAttr[mDpy].s3dMode == HDMI_S3D_TOP_AND_BOTTOM) {
+        dstL.top = displayFrame.top/2;
+        dstL.bottom = displayFrame.bottom/2;
+
+        dstR.top = mAlignedFBHeight/2 + displayFrame.top/2;
+        dstR.bottom = mAlignedFBHeight/2 + displayFrame.bottom/2;
+    }
+
     //Request left pipe (or 1 by default)
     Overlay::PipeSpecs pipeSpecs;
     pipeSpecs.formatClass = Overlay::FORMAT_RGB;
-    pipeSpecs.needsScaling = qhwc::needsScaling(layer);
+    pipeSpecs.needsScaling = (qhwc::needsScaling(layer) ||
+            needs3DComposition(ctx,mDpy));
     pipeSpecs.dpy = mDpy;
     pipeSpecs.mixer = Overlay::MIXER_DEFAULT;
     pipeSpecs.fb = true;
@@ -519,6 +535,7 @@
         a) FB's width is > Mixer width or
         b) On primary, driver has indicated with caps to split always. This is
            based on an empirically derived value of panel height.
+        c) The composition is 3D
     */
 
     const bool primarySplitAlways = (mDpy == HWC_DISPLAY_PRIMARY) and
@@ -533,7 +550,8 @@
 
     if((cropWidth > qdutils::MDPVersion::getInstance().getMaxPipeWidth()) or
             (primarySplitAlways and
-            (cropWidth > lSplit or layerClock > mixerClock))) {
+            (cropWidth > lSplit or layerClock > mixerClock)) or
+            needs3DComposition(ctx, mDpy)) {
         destR = ov.getPipe(pipeSpecs);
         if(destR == ovutils::OV_INVALID) {
             ALOGE("%s: No pipes available to configure fb for dpy %d's right"
@@ -546,10 +564,12 @@
         }
 
         //Split crop equally when using 2 pipes
-        cropL.right = (sourceCrop.right + sourceCrop.left) / 2;
-        cropR.left = cropL.right;
-        dstL.right = (displayFrame.right + displayFrame.left) / 2;
-        dstR.left = dstL.right;
+        if(!needs3DComposition(ctx, mDpy)) {
+            cropL.right = (sourceCrop.right + sourceCrop.left) / 2;
+            cropR.left = cropL.right;
+            dstL.right = (displayFrame.right + displayFrame.left) / 2;
+            dstR.left = dstL.right;
+        }
     }
 
     mDestLeft = destL;
@@ -563,6 +583,12 @@
         }
     }
 
+    // XXX: Figure out why we need this with source split
+    // Currently, the driver silently fails to configure the right pipe
+    // if we don't increment the zorder
+    if (needs3DComposition(ctx, mDpy))
+        parg.zorder = eZorder(parg.zorder + 1);
+
     //configure right pipe
     if(destR != OV_INVALID) {
         if(configMdp(ctx->mOverlay, parg, orient,
diff --git a/libhwcomposer/hwc_mdpcomp.cpp b/libhwcomposer/hwc_mdpcomp.cpp
index caa1344..2d6497e 100644
--- a/libhwcomposer/hwc_mdpcomp.cpp
+++ b/libhwcomposer/hwc_mdpcomp.cpp
@@ -217,9 +217,6 @@
     mCurrentFrame.reset(numLayers);
     ctx->mOverlay->clear(mDpy);
     ctx->mLayerRotMap[mDpy]->clear();
-    resetROI(ctx, mDpy);
-    memset(&mCurrentFrame.drop, 0, sizeof(mCurrentFrame.drop));
-    mCurrentFrame.dropCount = 0;
 }
 
 void MDPComp::reset() {
@@ -806,6 +803,10 @@
         return false;
     }
 
+    // No MDP composition for 3D
+    if(needs3DComposition(ctx, mDpy))
+        return false;
+
     // check for action safe flag and MDP scaling mode which requires scaling.
     if(ctx->dpyAttr[mDpy].mActionSafePresent
             || ctx->dpyAttr[mDpy].mMDPScalingMode) {
@@ -1340,7 +1341,7 @@
     if(mCurrentFrame.fbCount)
         mCurrentFrame.fbZ = mCurrentFrame.mdpCount;
 
-    if(sEnableYUVsplit){
+    if(sEnableYUVsplit || needs3DComposition(ctx, mDpy)){
         adjustForSourceSplit(ctx, list);
     }
 
@@ -1370,6 +1371,10 @@
         return false;
     }
 
+    // No MDP composition for 3D
+    if(needs3DComposition(ctx,mDpy))
+        return false;
+
     const bool secureOnly = true;
     return mdpOnlyLayersComp(ctx, list, not secureOnly) or
             mdpOnlyLayersComp(ctx, list, secureOnly);
@@ -1831,6 +1836,9 @@
                 }
                 continue;
             }
+            if(needs3DComposition(ctx,mDpy) && get3DFormat(hnd) != HAL_NO_3D) {
+                mdpNextZOrder++;
+            }
             if(configure(ctx, layer, mCurrentFrame.mdpToLayer[mdpIndex]) != 0 ){
                 ALOGD_IF(isDebug(), "%s: Failed to configure overlay for \
                         layer %d",__FUNCTION__, index);
@@ -2083,7 +2091,7 @@
     return ret;
 }
 
-bool MDPComp::allocSplitVGPipesfor4k2k(hwc_context_t *ctx, int index) {
+bool MDPComp::allocSplitVGPipes(hwc_context_t *ctx, int index) {
 
     bool bRet = true;
     int mdpIndex = mCurrentFrame.layerToMDP[index];
@@ -2186,7 +2194,7 @@
         hwc_layer_1_t* layer = &list->hwLayers[index];
         private_handle_t *hnd = (private_handle_t *)layer->handle;
         if(isYUVSplitNeeded(hnd) && sEnableYUVsplit){
-            if(allocSplitVGPipesfor4k2k(ctx, index)){
+            if(allocSplitVGPipes(ctx, index)){
                 continue;
             }
         }
@@ -2363,7 +2371,9 @@
                 mdpNextZOrder++;
                 hwc_layer_1_t* layer = &list->hwLayers[index];
                 private_handle_t *hnd = (private_handle_t *)layer->handle;
-                if(isYUVSplitNeeded(hnd)) {
+                if(isYUVSplitNeeded(hnd) ||
+                        (needs3DComposition(ctx,mDpy) &&
+                         get3DFormat(hnd) != HAL_NO_3D)) {
                     hwc_rect_t dst = layer->displayFrame;
                     if((dst.left > lSplit) || (dst.right < lSplit)) {
                         mCurrentFrame.mdpCount += 1;
@@ -2426,11 +2436,16 @@
         const int lSplit = getLeftSplit(ctx, mDpy);
         if(isYUVSplitNeeded(hnd) && sEnableYUVsplit){
             if((dst.left > lSplit)||(dst.right < lSplit)){
-                if(allocSplitVGPipesfor4k2k(ctx, index)){
+                if(allocSplitVGPipes(ctx, index)){
                     continue;
                 }
             }
         }
+        //XXX: Check for forced 2D composition
+        if(needs3DComposition(ctx, mDpy) && get3DFormat(hnd) != HAL_NO_3D)
+            if(allocSplitVGPipes(ctx,index))
+                continue;
+
         int mdpIndex = mCurrentFrame.layerToMDP[index];
         PipeLayerPair& info = mCurrentFrame.mdpToLayer[mdpIndex];
         info.pipeInfo = new MdpPipeInfoSplit;
@@ -2493,7 +2508,9 @@
     }
 
     // Set the Handle timeout to true for MDP or MIXED composition.
-    if(sIdleInvalidator && !sIdleFallBack && mCurrentFrame.mdpCount) {
+    if(sIdleInvalidator && !sIdleFallBack && mCurrentFrame.mdpCount &&
+            !(needs3DComposition(ctx, HWC_DISPLAY_PRIMARY) ||
+                needs3DComposition(ctx, HWC_DISPLAY_EXTERNAL))) {
         sHandleTimeout = true;
     }
 
@@ -2518,7 +2535,8 @@
 
         int mdpIndex = mCurrentFrame.layerToMDP[i];
 
-        if(isYUVSplitNeeded(hnd) && sEnableYUVsplit)
+        if((isYUVSplitNeeded(hnd) && sEnableYUVsplit) ||
+                (needs3DComposition(ctx, mDpy) && get3DFormat(hnd) != HAL_NO_3D))
         {
             MdpYUVPipeInfo& pipe_info =
                 *(MdpYUVPipeInfo*)mCurrentFrame.mdpToLayer[mdpIndex].pipeInfo;
@@ -2826,6 +2844,7 @@
     int rotFlags = ROT_FLAGS_NONE;
     uint32_t format = ovutils::getMdpFormat(hnd->format, hnd->flags);
     Whf whf(getWidth(hnd), getHeight(hnd), format, hnd->size);
+    eMdpFlags mdpFlags = ovutils::OV_MDP_FLAGS_NONE;
 
     ALOGD_IF(isDebug(),"%s: configuring: layer: %p z_order: %d dest_pipeL: %d"
              "dest_pipeR: %d",__FUNCTION__, layer, z, lDest, rDest);
@@ -2841,6 +2860,12 @@
         trimAgainstROI(ctx, crop, dst);
     }
 
+    if(needs3DComposition(ctx, mDpy) &&
+            get3DFormat(hnd) != HAL_NO_3D){
+        return configure3DVideo(ctx, layer, mDpy, mdpFlags, z, lDest,
+                rDest, &PipeLayerPair.rot);
+    }
+
     // Handle R/B swap
     if (layer->flags & HWC_FORMAT_RB_SWAP) {
         if (hnd->format == HAL_PIXEL_FORMAT_RGBA_8888)
@@ -2857,7 +2882,6 @@
     calcExtDisplayPosition(ctx, hnd, mDpy, crop, dst, transform, orient);
 
     int downscale = getRotDownscale(ctx, layer);
-    eMdpFlags mdpFlags = ovutils::OV_MDP_FLAGS_NONE;
     setMdpFlags(ctx, layer, mdpFlags, downscale, transform);
 
     if(lDest != OV_INVALID && rDest != OV_INVALID) {
@@ -3044,9 +3068,7 @@
         int perfHint = 0x4501; // 45-display layer hint, 01-Enable
         sPerfLockHandle = sPerfLockAcquire(0 /*handle*/, 0/*duration*/,
                                     &perfHint, sizeof(perfHint)/sizeof(int));
-        if(sPerfLockHandle < 0) {
-            ALOGE("Perf Lock Acquire Failed");
-        } else {
+        if(sPerfLockHandle > 0) {
             perflockFlag = 1;
         }
     }
diff --git a/libhwcomposer/hwc_mdpcomp.h b/libhwcomposer/hwc_mdpcomp.h
index 7c46c1a..a83f51f 100644
--- a/libhwcomposer/hwc_mdpcomp.h
+++ b/libhwcomposer/hwc_mdpcomp.h
@@ -270,7 +270,7 @@
     //Enable 4kx2k yuv layer split
     static bool sEnableYUVsplit;
     bool mModeOn; // if prepare happened
-    bool allocSplitVGPipesfor4k2k(hwc_context_t *ctx, int index);
+    bool allocSplitVGPipes(hwc_context_t *ctx, int index);
     //Enable Partial Update for MDP3 targets
     static bool enablePartialUpdateForMDP3;
     static void *sLibPerfHint;
diff --git a/libhwcomposer/hwc_qclient.cpp b/libhwcomposer/hwc_qclient.cpp
index 09013c6..487ec77 100644
--- a/libhwcomposer/hwc_qclient.cpp
+++ b/libhwcomposer/hwc_qclient.cpp
@@ -35,6 +35,8 @@
 #include <hwc_virtual.h>
 #include <overlay.h>
 #include <display_config.h>
+#include <hdmi.h>
+#include <video/msm_hdmi_modes.h>
 
 #define QCLIENT_DEBUG 0
 
@@ -338,6 +340,21 @@
     }
 }
 
+static void setS3DMode(hwc_context_t* ctx, int mode) {
+    if (ctx->mHDMIDisplay) {
+        if(ctx->mHDMIDisplay->isS3DModeSupported(mode)) {
+            ALOGD("%s: Force S3D mode to %d", __FUNCTION__, mode);
+            Locker::Autolock _sl(ctx->mDrawLock);
+            ctx->dpyAttr[HWC_DISPLAY_EXTERNAL].s3dModeForced = true;
+            setup3DMode(ctx, HWC_DISPLAY_EXTERNAL, mode);
+        } else {
+            ALOGD("%s: mode %d is not supported", __FUNCTION__, mode);
+        }
+    } else {
+        ALOGE("%s: No HDMI Display detected", __FUNCTION__);
+    }
+}
+
 status_t QClient::notifyCallback(uint32_t command, const Parcel* inParcel,
         Parcel* outParcel) {
     status_t ret = NO_ERROR;
@@ -398,6 +415,9 @@
         case IQService::TOGGLE_SCREEN_UPDATE:
             toggleScreenUpdate(mHwcContext, inParcel->readInt32());
             break;
+        case IQService::SET_S3D_MODE:
+            setS3DMode(mHwcContext, inParcel->readInt32());
+            break;
         default:
             ret = NO_ERROR;
     }
diff --git a/libhwcomposer/hwc_uevents.cpp b/libhwcomposer/hwc_uevents.cpp
index c6179e4..4d94647 100644
--- a/libhwcomposer/hwc_uevents.cpp
+++ b/libhwcomposer/hwc_uevents.cpp
@@ -125,6 +125,9 @@
                 ctx->proc->hotplug(ctx->proc, dpy, EXTERNAL_OFFLINE);
             }
 
+            // Report Hotplug via CEC HAL
+            ctx->mQService->onHdmiHotplug((int)ctx->dpyAttr[dpy].connected);
+
             //On 8994, 8992 due to hardware limitations, we disable bwc
             //when HDMI intf is active
             if((qdutils::MDPVersion::getInstance().is8994() or
@@ -158,7 +161,7 @@
                 ctx->mDrawLock.lock();
                 handle_online(ctx, dpy);
                 ctx->mDrawLock.unlock();
-
+                ctx->mQService->onHdmiHotplug(ctx->dpyAttr[dpy].connected);
                 ctx->proc->invalidate(ctx->proc);
                 break;
             } else {
@@ -213,6 +216,7 @@
             /* External display is HDMI */
             ALOGI("%s: Sending EXTERNAL ONLINE event", __FUNCTION__);
             ctx->proc->hotplug(ctx->proc, dpy, EXTERNAL_ONLINE);
+            ctx->mQService->onHdmiHotplug(ctx->dpyAttr[dpy].connected);
             break;
         }
     default:
diff --git a/libhwcomposer/hwc_utils.cpp b/libhwcomposer/hwc_utils.cpp
index 9ea8246..d176cd3 100644
--- a/libhwcomposer/hwc_utils.cpp
+++ b/libhwcomposer/hwc_utils.cpp
@@ -45,6 +45,7 @@
 #include "qd_utils.h"
 #include <sys/sysinfo.h>
 #include <dlfcn.h>
+#include <video/msm_hdmi_modes.h>
 
 using namespace qClient;
 using namespace qService;
@@ -351,8 +352,14 @@
         ctx->dpyAttr[i].mActionSafePresent = false;
         ctx->dpyAttr[i].mAsWidthRatio = 0;
         ctx->dpyAttr[i].mAsHeightRatio = 0;
+        ctx->dpyAttr[i].s3dMode = HDMI_S3D_NONE;
+        ctx->dpyAttr[i].s3dModeForced = false;
     }
 
+    //Make sure that the 3D mode is unset at bootup
+    //This makes sure that the state is accurate on framework reboots
+    ctx->mHDMIDisplay->configure3D(HDMI_S3D_NONE);
+
     for (uint32_t i = 0; i < HWC_NUM_DISPLAY_TYPES; i++) {
         ctx->mPrevHwLayerCount[i] = 0;
     }
@@ -369,14 +376,15 @@
     //independent process as well.
     QService::init();
     sp<IQClient> client = new QClient(ctx);
-    android::sp<qService::IQService> qservice_sp = interface_cast<IQService>(
+    sp<IQService> iqs = interface_cast<IQService>(
             defaultServiceManager()->getService(
             String16("display.qservice")));
-    if (qservice_sp.get()) {
-      qservice_sp->connect(client);
+    if (iqs.get()) {
+      iqs->connect(client);
+      ctx->mQService = reinterpret_cast<QService* >(iqs.get());
     } else {
       ALOGE("%s: Failed to acquire service pointer", __FUNCTION__);
-      return ;
+      return;
     }
 
     // Initialize device orientation to its default orientation
@@ -472,7 +480,10 @@
         ctx->mAD = NULL;
     }
 
-
+    if(ctx->mQService) {
+        delete ctx->mQService;
+        ctx->mQService = NULL;
+    }
 }
 
 //Helper to roundoff the refreshrates
@@ -1008,6 +1019,8 @@
     ctx->listStats[dpy].refreshRateRequest = ctx->dpyAttr[dpy].refreshRate;
     uint32_t refreshRate = 0;
     qdutils::MDPVersion& mdpHw = qdutils::MDPVersion::getInstance();
+    int s3dFormat = HAL_NO_3D;
+    int s3dLayerCount = 0;
 
     ctx->listStats[dpy].mAIVVideoMode = false;
     resetROI(ctx, dpy);
@@ -1063,6 +1076,14 @@
                 ctx->listStats[dpy].yuv4k2kIndices[yuv4k2kCount] = (int)i;
                 yuv4k2kCount++;
             }
+
+            // Gets set if one YUV layer is 3D
+            if (displaySupports3D(ctx,dpy)) {
+                s3dFormat = get3DFormat(hnd);
+                if(s3dFormat != HAL_NO_3D)
+                    s3dLayerCount++;
+            }
+
         }
         if(layer->blending == HWC_BLENDING_PREMULT)
             ctx->listStats[dpy].preMultipliedAlpha = true;
@@ -1089,6 +1110,17 @@
         }
 #endif
     }
+
+    //Set the TV's 3D mode based on format if it was not forced
+    //Only one 3D YUV layer is supported on external
+    //If there is more than one 3D YUV layer, the switch to 3D cannot occur.
+    if( !ctx->dpyAttr[dpy].s3dModeForced && (s3dLayerCount <= 1)) {
+        //XXX: Rapidly going in and out of 3D mode in some cases such
+        // as rotation might cause flickers. The OEMs are recommended to disable
+        // rotation on HDMI globally or in the app that plays 3D video
+        setup3DMode(ctx, dpy, convertS3DFormatToMode(s3dFormat));
+    }
+
     if(ctx->listStats[dpy].yuvCount > 0) {
         if (property_get("hw.cabl.yuv", property, NULL) > 0) {
             if (atoi(property) != 1) {
@@ -2197,6 +2229,113 @@
     return 0;
 }
 
+int configure3DVideo(hwc_context_t *ctx, hwc_layer_1_t *layer,
+        const int& dpy, eMdpFlags& mdpFlagsL, eZorder& z,
+        const eDest& lDest, const eDest& rDest,
+        Rotator **rot) {
+    private_handle_t *hnd = (private_handle_t *)layer->handle;
+    if(!hnd) {
+        ALOGE("%s: layer handle is NULL", __FUNCTION__);
+        return -1;
+    }
+    //Both pipes are configured to the same mixer
+    eZorder lz = z;
+    eZorder rz = (eZorder)(z + 1);
+
+    MetaData_t *metadata = (MetaData_t *)hnd->base_metadata;
+
+    hwc_rect_t crop = integerizeSourceCrop(layer->sourceCropf);
+    hwc_rect_t dst = layer->displayFrame;
+    int transform = layer->transform;
+    eTransform orient = static_cast<eTransform>(transform);
+    int rotFlags = ROT_FLAGS_NONE;
+    uint32_t format = ovutils::getMdpFormat(hnd->format, hnd->flags);
+    Whf whf(getWidth(hnd), getHeight(hnd), format, (uint32_t)hnd->size);
+
+    int downscale = getRotDownscale(ctx, layer);
+    setMdpFlags(ctx, layer, mdpFlagsL, downscale, transform);
+
+    //XXX: Check if rotation is supported and valid for 3D
+    if((has90Transform(layer) or downscale) and isRotationDoable(ctx, hnd)) {
+        (*rot) = ctx->mRotMgr->getNext();
+        if((*rot) == NULL) return -1;
+        ctx->mLayerRotMap[dpy]->add(layer, *rot);
+        //Configure rotator for pre-rotation
+        if(configRotator(*rot, whf, crop, mdpFlagsL, orient, downscale) < 0) {
+            ALOGE("%s: configRotator failed!", __FUNCTION__);
+            return -1;
+        }
+        updateSource(orient, whf, crop, *rot);
+        rotFlags |= ROT_PREROTATED;
+    }
+
+    eMdpFlags mdpFlagsR = mdpFlagsL;
+
+    hwc_rect_t cropL = crop, dstL = dst;
+    hwc_rect_t cropR = crop, dstR = dst;
+    int hw_w = ctx->dpyAttr[dpy].xres;
+    int hw_h = ctx->dpyAttr[dpy].yres;
+
+    if(get3DFormat(hnd) == HAL_3D_SIDE_BY_SIDE_L_R ||
+            get3DFormat(hnd) == HAL_3D_SIDE_BY_SIDE_R_L) {
+        // Calculate Left rects
+        // XXX: This assumes crop.right/2 is the center point of the video
+        cropL.right = crop.right/2;
+        dstL.left = dst.left/2;
+        dstL.right = dst.right/2;
+
+        // Calculate Right rects
+        cropR.left = crop.right/2;
+        dstR.left = hw_w/2 + dst.left/2;
+        dstR.right = hw_w/2 + dst.right/2;
+    } else if(get3DFormat(hnd) == HAL_3D_TOP_BOTTOM) {
+        // Calculate Left rects
+        cropL.bottom = crop.bottom/2;
+        dstL.top = dst.top/2;
+        dstL.bottom = dst.bottom/2;
+
+        // Calculate Right rects
+        cropR.top = crop.bottom/2;
+        dstR.top = hw_h/2 + dst.top/2;
+        dstR.bottom = hw_h/2 + dst.bottom/2;
+    } else {
+        ALOGE("%s: Unsupported 3D mode ", __FUNCTION__);
+        return -1;
+    }
+
+    //For the mdp, since either we are pre-rotating or MDP does flips
+    orient = OVERLAY_TRANSFORM_0;
+    transform = 0;
+
+    //configure left pipe
+    if(lDest != OV_INVALID) {
+        PipeArgs pargL(mdpFlagsL, whf, lz,
+                       static_cast<eRotFlags>(rotFlags), layer->planeAlpha,
+                       (ovutils::eBlending) getBlending(layer->blending));
+
+        if(configMdp(ctx->mOverlay, pargL, orient,
+                cropL, dstL, metadata, lDest) < 0) {
+            ALOGE("%s: commit failed for left mixer config", __FUNCTION__);
+            return -1;
+        }
+    }
+
+    //configure right pipe
+    if(rDest != OV_INVALID) {
+        PipeArgs pargR(mdpFlagsR, whf, rz,
+                       static_cast<eRotFlags>(rotFlags),
+                       layer->planeAlpha,
+                       (ovutils::eBlending) getBlending(layer->blending));
+        if(configMdp(ctx->mOverlay, pargR, orient,
+                cropR, dstR, metadata, rDest) < 0) {
+            ALOGE("%s: commit failed for right mixer config", __FUNCTION__);
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
 int configureSourceSplit(hwc_context_t *ctx, hwc_layer_1_t *layer,
         const int& dpy, eMdpFlags& mdpFlagsL, eZorder& z,
         const eDest& lDest, const eDest& rDest,
@@ -2634,7 +2773,13 @@
 void resetROI(hwc_context_t *ctx, const int dpy) {
     const int fbXRes = (int)ctx->dpyAttr[dpy].xres;
     const int fbYRes = (int)ctx->dpyAttr[dpy].yres;
-    if(isDisplaySplit(ctx, dpy)) {
+
+    /* When source split is enabled, both the panels are calibrated
+     * in a single coordinate system. So only one ROI is generated
+     * for the whole panel extending equally from the midpoint and
+     * populated for the left side. */
+    if(!qdutils::MDPVersion::getInstance().isSrcSplit() &&
+            isDisplaySplit(ctx, dpy)) {
         const int lSplit = getLeftSplit(ctx, dpy);
         ctx->listStats[dpy].lRoi = (struct hwc_rect){0, 0, lSplit, fbYRes};
         ctx->listStats[dpy].rRoi = (struct hwc_rect){lSplit, 0, fbXRes, fbYRes};
@@ -2810,4 +2955,41 @@
     ctx->dpyAttr[dpy].isActive = false;
 }
 
+int convertS3DFormatToMode(int s3DFormat) {
+    int ret;
+    switch(s3DFormat) {
+    case HAL_3D_SIDE_BY_SIDE_L_R:
+    case HAL_3D_SIDE_BY_SIDE_R_L:
+        ret = HDMI_S3D_SIDE_BY_SIDE;
+        break;
+    case HAL_3D_TOP_BOTTOM:
+        ret = HDMI_S3D_TOP_AND_BOTTOM;
+        break;
+    default:
+        ret = HDMI_S3D_NONE;
+    }
+    return ret;
+}
+
+bool needs3DComposition(hwc_context_t* ctx, int dpy) {
+    return (displaySupports3D(ctx, dpy) && ctx->dpyAttr[dpy].connected &&
+            ctx->dpyAttr[dpy].s3dMode != HDMI_S3D_NONE);
+}
+
+void setup3DMode(hwc_context_t *ctx, int dpy, int s3dMode) {
+    if (ctx->dpyAttr[dpy].s3dMode != s3dMode) {
+        ALOGD("%s: setup 3D mode: %d", __FUNCTION__, s3dMode);
+        if(ctx->mHDMIDisplay->configure3D(s3dMode)) {
+            ctx->dpyAttr[dpy].s3dMode = s3dMode;
+        }
+    }
+}
+
+bool displaySupports3D(hwc_context_t* ctx, int dpy) {
+    return ((dpy == HWC_DISPLAY_EXTERNAL) ||
+            ((dpy == HWC_DISPLAY_PRIMARY) &&
+             ctx->mHDMIDisplay->isHDMIPrimaryDisplay()));
+}
+
+
 };//namespace qhwc
diff --git a/libhwcomposer/hwc_utils.h b/libhwcomposer/hwc_utils.h
index a97c59b..23157d4 100644
--- a/libhwcomposer/hwc_utils.h
+++ b/libhwcomposer/hwc_utils.h
@@ -35,6 +35,7 @@
 #include <overlayUtils.h>
 #include <overlayRotator.h>
 #include <EGL/egl.h>
+#include <QService.h>
 
 
 #define ALIGN_TO(x, align)     (((x) + ((align)-1)) & ~((align)-1))
@@ -117,6 +118,18 @@
     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.
+    // If the mode is set via binder, the s3dModeForced flag is set, so that the
+    // mode is not changed back when the 3D video layer drops out.
+    // If the forced mode is different from the one in 3D video, the results
+    // are unpredictable. The assumption is made here that the caller forcing
+    // the mode via binder knows the right formats to use.
+    // The s3dModeForced flag is also used to force 2D if the s3dMode is
+    // HDMI_S3D_NONE
+    int s3dMode;
+    bool s3dModeForced;
 };
 
 struct ListStats {
@@ -411,6 +424,15 @@
         const ovutils::eDest& lDest,
         const ovutils::eDest& rDest, overlay::Rotator **rot);
 
+//Check if the current round needs 3D composition
+bool needs3DComposition(hwc_context_t* ctx, int dpy);
+
+//Routine to configure 3D video
+int configure3DVideo(hwc_context_t *ctx, hwc_layer_1_t *layer, const int& dpy,
+        ovutils::eMdpFlags& mdpFlags, ovutils::eZorder& z,
+        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,
@@ -444,6 +466,16 @@
 // Checks if boot animation has completed and applies default mode
 void processBootAnimCompleted(hwc_context_t *ctx);
 
+//The gralloc API and driver have different formats
+//The format needs to be converted before passing to libhdmi
+int convertS3DFormatToMode(int s3DFormat);
+
+//Configure resources for 3D mode
+void setup3DMode(hwc_context_t* ctx, int dpy, int s3dMode);
+
+//Checks if this display supports 3D
+bool displaySupports3D(hwc_context_t* ctx, int dpy);
+
 // Inline utility functions
 static inline bool isSkipLayer(const hwc_layer_1_t* l) {
     return (UNLIKELY(l && (l->flags & HWC_SKIP_LAYER)));
@@ -489,6 +521,14 @@
     return (hnd && (hnd->flags & private_handle_t::PRIV_FLAGS_SECURE_DISPLAY));
 }
 
+static inline uint32_t get3DFormat(const private_handle_t* hnd) {
+    MetaData_t *metadata = reinterpret_cast<MetaData_t*>(hnd->base_metadata);
+    if(isYuvBuffer(hnd) && metadata && metadata->operation & S3D_FORMAT) {
+        return metadata->s3dFormat;
+    }
+    return HAL_NO_3D;
+}
+
 static inline int getWidth(const private_handle_t* hnd) {
     MetaData_t *metadata = reinterpret_cast<MetaData_t*>(hnd->base_metadata);
     if(metadata && metadata->operation & UPDATE_BUFFER_GEOMETRY) {
@@ -644,10 +684,12 @@
     bool mBWCEnabled;
     // Provides a way for OEM's to disable setting dynfps via metadata.
     bool mUseMetaDataRefreshRate;
-   // Stores the hpd enabled status- avoids re-enabling HDP on suspend resume.
+    // Stores the hpd enabled status- avoids re-enabling HDP on suspend resume.
     bool mHPDEnabled;
     //Used to notify that boot has completed
     bool mBootAnimCompleted;
+    // Display binder service
+    qService::QService* mQService;
 };
 
 namespace qhwc {
diff --git a/libhwcomposer/hwc_vsync.cpp b/libhwcomposer/hwc_vsync.cpp
index 89f8044..1a7a836 100644
--- a/libhwcomposer/hwc_vsync.cpp
+++ b/libhwcomposer/hwc_vsync.cpp
@@ -33,6 +33,7 @@
 #include "overlay.h"
 #define __STDC_FORMAT_MACROS 1
 #include <inttypes.h>
+#define DEBUG 0
 
 using namespace qdutils;
 namespace qhwc {
@@ -56,7 +57,8 @@
     return ret;
 }
 
-static void handle_vsync_event(hwc_context_t* ctx, int dpy, char *data)
+static void handle_vsync_event(hwc_context_t* ctx, int dpy, char *data,
+        ssize_t len __unused)
 {
     // extract timestamp
     uint64_t timestamp = 0;
@@ -69,7 +71,8 @@
     ctx->proc->vsync(ctx->proc, dpy, timestamp);
 }
 
-static void handle_blank_event(hwc_context_t* ctx, int dpy, char *data)
+static void handle_blank_event(hwc_context_t* ctx, int dpy, char *data,
+        ssize_t len __unused)
 {
     if (!strncmp(data, PANEL_ON_STR, strlen(PANEL_ON_STR))) {
         unsigned long int poweron = strtoul(data + strlen(PANEL_ON_STR), NULL, 0);
@@ -80,7 +83,8 @@
     }
 }
 
-static void handle_thermal_event(hwc_context_t* ctx, int dpy, char *data)
+static void handle_thermal_event(hwc_context_t* ctx, int dpy, char *data,
+        ssize_t len __unused)
 {
     // extract thermal level
     uint64_t thermalLevel = 0;
@@ -95,15 +99,24 @@
         ctx->mThermalBurstMode = false;
 }
 
+static void handle_cec_event(hwc_context_t* ctx, int dpy, char *data,
+        ssize_t len)
+{
+    ALOGD_IF(DEBUG, "%s: Got CEC event from driver dpy:%d",
+            __FUNCTION__, dpy);
+    ctx->mQService->onCECMessageReceived(data, len);
+}
+
 struct event {
     const char* name;
-    void (*callback)(hwc_context_t* ctx, int dpy, char *data);
+    void (*callback)(hwc_context_t* ctx, int dpy, char *data, ssize_t len);
 };
 
 struct event event_list[] =  {
     { "vsync_event", handle_vsync_event },
     { "show_blank_event", handle_blank_event },
     { "msm_fb_thermal_level", handle_thermal_event },
+    { "cec/rd_msg", handle_cec_event },
 };
 
 #define num_events ARRAY_LENGTH(event_list)
@@ -185,7 +198,7 @@
                                 continue;
                             }
                             vdata[len] = '\0';
-                            event_list[ev].callback(ctx, dpy, vdata);
+                            event_list[ev].callback(ctx, dpy, vdata, len);
                         }
                     }
                 }
diff --git a/liboverlay/overlay.cpp b/liboverlay/overlay.cpp
index a34e599..b7c7fcc 100644
--- a/liboverlay/overlay.cpp
+++ b/liboverlay/overlay.cpp
@@ -151,6 +151,8 @@
         return getPipe_8x39(pipeSpecs);
     } else if(MDPVersion::getInstance().is8994()) {
         return getPipe_8994(pipeSpecs);
+    } else if(MDPVersion::getInstance().is8992()) {
+        return getPipe_8992(pipeSpecs);
     }
 
     eDest dest = OV_INVALID;
@@ -251,6 +253,13 @@
     //supported since we at least need 1 round in between where the DMA is
     //unused
     eDest dest = OV_INVALID;
+
+    // Reset format type to FORMAT_NONE to select the pipe irrespective of the
+    // format specifed by the client. This is required for the device where
+    // SMP starvation is unlikely, we need not keep track of formats
+    // programmed in the pipes to avoid potential pipe crunching.
+    resetPipeBookFormat(pipeSpecs.dpy);
+
     if(pipeSpecs.formatClass == FORMAT_YUV) {
         return nextPipe(OV_MDP_PIPE_VG, pipeSpecs);
     } else {
@@ -262,9 +271,14 @@
             dest = nextPipe(OV_MDP_PIPE_DMA, pipeSpecs);
         }
     }
+
     return dest;
 }
 
+utils::eDest Overlay::getPipe_8992(const PipeSpecs& pipeSpecs) {
+    return getPipe_8994(pipeSpecs);
+}
+
 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 45b5e57..49be930 100644
--- a/liboverlay/overlay.h
+++ b/liboverlay/overlay.h
@@ -178,6 +178,9 @@
     utils::eDest getPipe_8x16(const PipeSpecs& pipeSpecs);
     utils::eDest getPipe_8x39(const PipeSpecs& pipeSpecs);
     utils::eDest getPipe_8994(const PipeSpecs& pipeSpecs);
+    utils::eDest getPipe_8992(const PipeSpecs& pipeSpecs);
+
+    void resetPipeBookFormat(const int &dpy);
 
     /* Returns the handle to libscale.so's programScale function */
     static int (*getFnProgramScale())(struct mdp_overlay_list *);
@@ -443,6 +446,14 @@
     return "Invalid";
 }
 
+inline void Overlay::resetPipeBookFormat(const int &dpy) {
+    for(int i = 0; i < PipeBook::NUM_PIPES; i++) {
+        if (mPipeBook[i].mDisplay == dpy) {
+            mPipeBook[i].mFormatType = FORMAT_NONE;
+        }
+    }
+}
+
 }; // overlay
 
 #endif // OVERLAY_H
diff --git a/liboverlay/overlayMdp.cpp b/liboverlay/overlayMdp.cpp
index 1290f32..0c31dd3 100644
--- a/liboverlay/overlayMdp.cpp
+++ b/liboverlay/overlayMdp.cpp
@@ -353,8 +353,31 @@
     mdp_overlay* ovArray[count];
     memset(&ovArray, 0, sizeof(ovArray));
 
+    uint8_t max_horz_deci = 0, max_vert_deci = 0;
+
+    // Decimation factor for the left and right pipe differs, when there is a
+    // one pixel difference in the dst width of right pipe and the left pipe.
+    // libscalar returns a failure as it expects decimation on both the pipe
+    // to be same. So compare the decimation factor on both the pipes and assign
+    // the maximum of it.
     for(int i = 0; i < count; i++) {
-        ovArray[i] = &mdpCtrlArray[i]->mOVInfo;
+        mdp_overlay *ov_current = &mdpCtrlArray[i]->mOVInfo;
+        for(int j = i + 1; j < count; j++) {
+            mdp_overlay *ov_next = &mdpCtrlArray[j]->mOVInfo;
+            if(ov_current->z_order == ov_next->z_order) {
+                max_horz_deci = utils::max(ov_current->horz_deci,
+                                           ov_next->horz_deci);
+                max_vert_deci = utils::max(ov_current->vert_deci,
+                                           ov_next->vert_deci);
+
+                ov_current->horz_deci = max_horz_deci;
+                ov_next->horz_deci = max_horz_deci;
+                ov_current->vert_deci = max_vert_deci;
+                ov_next->vert_deci = max_vert_deci;
+                break;
+            }
+        }
+        ovArray[i] = ov_current;
     }
 
     struct mdp_overlay_list list;
diff --git a/libqdutils/mdp_version.cpp b/libqdutils/mdp_version.cpp
index 402e129..e045918 100644
--- a/libqdutils/mdp_version.cpp
+++ b/libqdutils/mdp_version.cpp
@@ -501,6 +501,10 @@
             mMdpRev < MDSS_MDP_HW_REV_109);
 }
 
+bool MDPVersion::is8992() {
+    return ((mMdpRev >= MDSS_MDP_HW_REV_110 and
+            mMdpRev < MDSS_MDP_HW_REV_200));
+}
 
 }; //namespace qdutils
 
diff --git a/libqdutils/mdp_version.h b/libqdutils/mdp_version.h
index 3b10010..aeb2218 100644
--- a/libqdutils/mdp_version.h
+++ b/libqdutils/mdp_version.h
@@ -151,6 +151,7 @@
     bool is8994();
     bool is8x16();
     bool is8x39();
+    bool is8992();
 
 private:
     bool updateSysFsInfo();
diff --git a/libqservice/Android.mk b/libqservice/Android.mk
index 78b1d77..287e6ce 100644
--- a/libqservice/Android.mk
+++ b/libqservice/Android.mk
@@ -10,7 +10,8 @@
 LOCAL_ADDITIONAL_DEPENDENCIES := $(common_deps)
 LOCAL_SRC_FILES               := QService.cpp \
                                  IQService.cpp \
-                                 IQClient.cpp
+                                 IQClient.cpp \
+                                 IQHDMIClient.cpp
 LOCAL_COPY_HEADERS_TO         := $(common_header_export_path)
 LOCAL_COPY_HEADERS            := IQService.h \
                                  IQClient.h
diff --git a/libqservice/IQHDMIClient.cpp b/libqservice/IQHDMIClient.cpp
new file mode 100644
index 0000000..9f5044a
--- /dev/null
+++ b/libqservice/IQHDMIClient.cpp
@@ -0,0 +1,103 @@
+/*
+* Copyright (c) 2014 The Linux Foundation. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*    * Redistributions of source code must retain the above copyright
+*      notice, this list of conditions and the following disclaimer.
+*    * Redistributions in binary form must reproduce the above
+*      copyright notice, this list of conditions and the following
+*      disclaimer in the documentation and/or other materials provided
+*      with the distribution.
+*    * Neither the name of The Linux Foundation. nor the names of its
+*      contributors may be used to endorse or promote products derived
+*      from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include <utils/Log.h>
+#include <binder/Parcel.h>
+#include "IQHDMIClient.h"
+
+using namespace android;
+namespace qClient {
+
+enum {
+    HDMI_CONNECTED = IBinder::FIRST_CALL_TRANSACTION,
+    CEC_MESSAGE_RECEIVED
+};
+
+class BpQHDMIClient : public BpInterface<IQHDMIClient>
+{
+public:
+    BpQHDMIClient(const sp<IBinder>& impl)
+            :BpInterface<IQHDMIClient>(impl)
+    {
+    }
+
+    void onHdmiHotplug(int connected)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IQHDMIClient::getInterfaceDescriptor());
+        data.writeInt32(connected);
+        remote()->transact(HDMI_CONNECTED, data, &reply, IBinder::FLAG_ONEWAY);
+    }
+
+    void onCECMessageRecieved(char *msg, ssize_t len)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IQHDMIClient::getInterfaceDescriptor());
+        data.writeInt32((int32_t)len);
+        void *buf = data.writeInplace(len);
+        if (buf != NULL)
+            memcpy(buf, msg, len);
+        remote()->transact(CEC_MESSAGE_RECEIVED, data, &reply,
+                IBinder::FLAG_ONEWAY);
+    }
+};
+
+IMPLEMENT_META_INTERFACE(QHDMIClient,
+        "android.display.IQHDMIClient");
+
+status_t BnQHDMIClient::onTransact(uint32_t code, const Parcel& data,
+        Parcel* reply, uint32_t flags)
+{
+    switch(code) {
+        case HDMI_CONNECTED: {
+            CHECK_INTERFACE(IQHDMIClient, data, reply);
+            int connected = data.readInt32();
+            onHdmiHotplug(connected);
+            return NO_ERROR;
+        }
+        case CEC_MESSAGE_RECEIVED: {
+            CHECK_INTERFACE(IQHDMIClient, data, reply);
+            ssize_t len = data.readInt32();
+            const void* msg;
+            if(len >= 0 && len <= (ssize_t) data.dataAvail()) {
+                msg = data.readInplace(len);
+            } else {
+                msg = NULL;
+                len = 0;
+            }
+            if (msg != NULL)
+                onCECMessageRecieved((char*) msg, len);
+            return NO_ERROR;
+        }
+        default: {
+            return BBinder::onTransact(code, data, reply, flags);
+        }
+    }
+}
+
+}; //namespace qClient
diff --git a/libqservice/IQHDMIClient.h b/libqservice/IQHDMIClient.h
new file mode 100644
index 0000000..c3d012a
--- /dev/null
+++ b/libqservice/IQHDMIClient.h
@@ -0,0 +1,57 @@
+/*
+* Copyright (c) 2014 The Linux Foundation. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*    * Redistributions of source code must retain the above copyright
+*      notice, this list of conditions and the following disclaimer.
+*    * Redistributions in binary form must reproduce the above
+*      copyright notice, this list of conditions and the following
+*      disclaimer in the documentation and/or other materials provided
+*      with the distribution.
+*    * Neither the name of The Linux Foundation. nor the names of its
+*      contributors may be used to endorse or promote products derived
+*      from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef HDMI_EVENTS_LISTENER_H_
+#define HDMI_EVENTS_LISTENER_H_
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+
+namespace qClient {
+
+class IQHDMIClient : public android::IInterface
+{
+public:
+    DECLARE_META_INTERFACE(QHDMIClient);
+    virtual void onHdmiHotplug(int connected) = 0;
+    virtual void onCECMessageRecieved(char *msg, ssize_t len) = 0;
+};
+
+class BnQHDMIClient : public android::BnInterface<IQHDMIClient>
+{
+public:
+    virtual android::status_t onTransact( uint32_t code,
+            const android::Parcel& data,
+            android::Parcel* reply, uint32_t flags = 0);
+};
+
+}; //namespace qhdmi
+
+#endif // HDMI_EVENTS_LISTENER_H_
+
diff --git a/libqservice/IQService.cpp b/libqservice/IQService.cpp
index eee22f0..5e67b15 100644
--- a/libqservice/IQService.cpp
+++ b/libqservice/IQService.cpp
@@ -45,13 +45,22 @@
         : BpInterface<IQService>(impl) {}
 
     virtual void connect(const sp<IQClient>& client) {
-        ALOGD_IF(QSERVICE_DEBUG, "%s: connect client", __FUNCTION__);
+        ALOGD_IF(QSERVICE_DEBUG, "%s: connect HWC client", __FUNCTION__);
         Parcel data, reply;
         data.writeInterfaceToken(IQService::getInterfaceDescriptor());
         data.writeStrongBinder(client->asBinder());
-        remote()->transact(CONNECT, data, &reply);
+        remote()->transact(CONNECT_HWC_CLIENT, data, &reply);
     }
 
+    virtual void connect(const sp<IQHDMIClient>& client) {
+        ALOGD_IF(QSERVICE_DEBUG, "%s: connect HDMI client", __FUNCTION__);
+        Parcel data, reply;
+        data.writeInterfaceToken(IQService::getInterfaceDescriptor());
+        data.writeStrongBinder(client->asBinder());
+        remote()->transact(CONNECT_HDMI_CLIENT, data, &reply);
+    }
+
+
     virtual android::status_t dispatch(uint32_t command, const Parcel* inParcel,
             Parcel* outParcel) {
         ALOGD_IF(QSERVICE_DEBUG, "%s: dispatch in:%p", __FUNCTION__, inParcel);
@@ -90,10 +99,10 @@
             callerUid == AID_ROOT ||
             callerUid == AID_SYSTEM);
 
-    if (code == CONNECT) {
+    if (code == CONNECT_HWC_CLIENT) {
         CHECK_INTERFACE(IQService, data, reply);
         if(callerUid != AID_GRAPHICS) {
-            ALOGE("display.qservice CONNECT access denied: \
+            ALOGE("display.qservice CONNECT_HWC_CLIENT access denied: \
                     pid=%d uid=%d process=%s",
                     callerPid, callerUid, callingProcName);
             return PERMISSION_DENIED;
@@ -102,6 +111,18 @@
                 interface_cast<IQClient>(data.readStrongBinder());
         connect(client);
         return NO_ERROR;
+    } else if(code == CONNECT_HDMI_CLIENT) {
+        CHECK_INTERFACE(IQService, data, reply);
+        if(callerUid != AID_SYSTEM && callerUid != AID_ROOT) {
+            ALOGE("display.qservice CONNECT_HDMI_CLIENT access denied: \
+                    pid=%d uid=%d process=%s",
+                    callerPid, callerUid, callingProcName);
+            return PERMISSION_DENIED;
+        }
+        sp<IQHDMIClient> client =
+                interface_cast<IQHDMIClient>(data.readStrongBinder());
+        connect(client);
+        return NO_ERROR;
     } else if (code > COMMAND_LIST_START && code < COMMAND_LIST_END) {
         if(!permission) {
             ALOGE("display.qservice access denied: command=%d\
diff --git a/libqservice/IQService.h b/libqservice/IQService.h
index cd2d116..78cbd2a 100644
--- a/libqservice/IQService.h
+++ b/libqservice/IQService.h
@@ -28,6 +28,7 @@
 #include <binder/IInterface.h>
 #include <binder/IBinder.h>
 #include <IQClient.h>
+#include <IQHDMIClient.h>
 
 
 namespace qService {
@@ -41,7 +42,7 @@
         COMMAND_LIST_START = android::IBinder::FIRST_CALL_TRANSACTION,
         SECURING = 2,           // Hardware securing start/end notification
         UNSECURING = 3,         // Hardware unsecuring start/end notification
-        CONNECT = 4,            // Connect to qservice
+        CONNECT_HWC_CLIENT = 4, // Connect to qservice
         SCREEN_REFRESH = 5,     // Refresh screen through SF invalidate
         EXTERNAL_ORIENTATION = 6,// Set external orientation
         BUFFER_MIRRORMODE = 7,  // Buffer mirrormode
@@ -60,6 +61,8 @@
         SET_PARTIAL_UPDATE = 19,   // Preference on partial update feature
         TOGGLE_SCREEN_UPDATE = 20, // Provides ability to disable screen updates
         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
         COMMAND_LIST_END = 400,
     };
 
@@ -84,8 +87,13 @@
         DUMP_VIRTUAL_DISPLAY,
     };
 
-    // Register a client that can be notified
+    // Register a HWC client that can be notified
+    // This client is generic and is intended to get
+    // dispatches of all events calling into QService
     virtual void connect(const android::sp<qClient::IQClient>& client) = 0;
+    // Register an HDMI client. This client gets notification of HDMI events
+    // such as plug/unplug and CEC messages
+    virtual void connect(const android::sp<qClient::IQHDMIClient>& client) = 0;
     // Generic function to dispatch binder commands
     // The type of command decides how the data is parceled
     virtual android::status_t dispatch(uint32_t command,
diff --git a/libqservice/QService.cpp b/libqservice/QService.cpp
index 5d2e5de..ddb4b18 100644
--- a/libqservice/QService.cpp
+++ b/libqservice/QService.cpp
@@ -50,10 +50,15 @@
 }
 
 void QService::connect(const sp<qClient::IQClient>& client) {
-    ALOGD_IF(QSERVICE_DEBUG,"client connected");
+    ALOGD_IF(QSERVICE_DEBUG,"HWC client connected");
     mClient = client;
 }
 
+void QService::connect(const sp<qClient::IQHDMIClient>& client) {
+    ALOGD_IF(QSERVICE_DEBUG,"HWC client connected");
+    mHDMIClient = client;
+}
+
 status_t QService::dispatch(uint32_t command, const Parcel* inParcel,
         Parcel* outParcel) {
     status_t err = (status_t) FAILED_TRANSACTION;
@@ -72,6 +77,25 @@
     return err;
 }
 
+void QService::onHdmiHotplug(int connected) {
+    if(mHDMIClient.get()) {
+        ALOGD_IF(QSERVICE_DEBUG, "%s: HDMI hotplug", __FUNCTION__);
+        mHDMIClient->onHdmiHotplug(connected);
+    } else {
+        ALOGE("%s: Failed to get a valid HDMI client", __FUNCTION__);
+    }
+}
+
+void QService::onCECMessageReceived(char *msg, ssize_t len) {
+    if(mHDMIClient.get()) {
+        ALOGD_IF(QSERVICE_DEBUG, "%s: CEC message received", __FUNCTION__);
+        mHDMIClient->onCECMessageRecieved(msg, len);
+    } else {
+        ALOGE("%s: Failed to get a valid HDMI client", __FUNCTION__);
+    }
+}
+
+
 void QService::init()
 {
     if(!sQService) {
diff --git a/libqservice/QService.h b/libqservice/QService.h
index a8e4cdb..719c9b7 100644
--- a/libqservice/QService.h
+++ b/libqservice/QService.h
@@ -46,13 +46,17 @@
 public:
     virtual ~QService();
     virtual void connect(const android::sp<qClient::IQClient>& client);
+    virtual void connect(const android::sp<qClient::IQHDMIClient>& client);
     virtual android::status_t dispatch(uint32_t command,
             const android::Parcel* data,
             android::Parcel* reply);
+    void onHdmiHotplug(int connected);
+    void onCECMessageReceived(char *msg, ssize_t len);
     static void init();
 private:
     QService();
     android::sp<qClient::IQClient> mClient;
+    android::sp<qClient::IQHDMIClient> mHDMIClient;
     static QService *sQService;
 };
 }; // namespace qService