hal: msm8996: Update to AU_LINUX_ANDROID_LA.HB.1.3.9.06.00.01.213.152

This is a squashed version of following commits:

	a21cee80 sdm: Add support for qseed3 scalar(v2)
	774e6ad4 sdm: color-manager: Frame capture implementation.
	790357e6 libgralloc: Get appropriate width and height from metadata
	17711ea2 sdm: Fix incorrect permission on hw_info.cpp
	c85cceef gralloc : Add singleton class for mdp capability
	375b3210 gralloc: Use PROTECTED flag for L1 allocations

Change-Id: I370feedfb15dcc58778bc4012323ceb16c292fd8
diff --git a/msm8996/libgralloc/alloc_controller.cpp b/msm8996/libgralloc/alloc_controller.cpp
index f9fe314..ec3c0d7 100644
--- a/msm8996/libgralloc/alloc_controller.cpp
+++ b/msm8996/libgralloc/alloc_controller.cpp
@@ -76,6 +76,7 @@
 using namespace android;
 
 ANDROID_SINGLETON_STATIC_INSTANCE(AdrenoMemInfo);
+ANDROID_SINGLETON_STATIC_INSTANCE(MDPCapabilityInfo);
 
 static void getYuvUBwcWidthHeight(int, int, int, int&, int&);
 static unsigned int getUBwcSize(int, int, int, const int, const int);
@@ -97,7 +98,17 @@
     return false;
 }
 
-//-------------- AdrenoMemInfo-----------------------//
+//------------- MDPCapabilityInfo-----------------------//
+MDPCapabilityInfo :: MDPCapabilityInfo() {
+  isMacroTileSupported = false;
+  qdutils::querySDEInfo(HAS_MACRO_TILE, &isMacroTileSupported);
+}
+
+int MDPCapabilityInfo :: isMacroTilingSupportedByMDP(){
+    return isMacroTileSupported;
+ }
+
+//------------- AdrenoMemInfo-----------------------//
 AdrenoMemInfo::AdrenoMemInfo()
 {
     LINK_adreno_compute_aligned_width_and_height = NULL;
@@ -447,23 +458,16 @@
     data.allocType = 0;
 
     if(usage & GRALLOC_USAGE_PROTECTED) {
-        if (usage & GRALLOC_USAGE_PRIVATE_MM_HEAP) {
-            if (usage & GRALLOC_USAGE_PRIVATE_SECURE_DISPLAY) {
-                ionHeapId = ION_HEAP(SD_HEAP_ID);
-                /*
-                 * There is currently no flag in ION for Secure Display
-                 * VM. Please add it to the define once available.
-                */
-                ionFlags |= ION_SD_FLAGS;
-            } else {
-                ionHeapId = ION_HEAP(CP_HEAP_ID);
-                ionFlags |= ION_CP_FLAGS;
-            }
+        if (usage & GRALLOC_USAGE_PRIVATE_SECURE_DISPLAY) {
+            ionHeapId = ION_HEAP(SD_HEAP_ID);
+            /*
+             * There is currently no flag in ION for Secure Display
+             * VM. Please add it to the define once available.
+             */
+            ionFlags |= ION_SD_FLAGS;
         } else {
-            // for targets/OEMs which do not need HW level protection
-            // do not set ion secure flag & MM heap. Fallback to system heap.
-            ionHeapId |= ION_HEAP(ION_SYSTEM_HEAP_ID);
-            data.allocType |= private_handle_t::PRIV_FLAGS_PROTECTED_BUFFER;
+            ionHeapId = ION_HEAP(CP_HEAP_ID);
+            ionFlags |= ION_CP_FLAGS;
         }
     } else if(usage & GRALLOC_USAGE_PRIVATE_MM_HEAP) {
         //MM Heap is exclusively a secure heap.
@@ -517,13 +521,9 @@
 bool isMacroTileEnabled(int format, int usage)
 {
     bool tileEnabled = false;
-    int isMacroTileSupportedByMDP = 0;
-
-    qdutils::querySDEInfo(HAS_MACRO_TILE, &isMacroTileSupportedByMDP);
-
     // Check whether GPU & MDSS supports MacroTiling feature
     if(AdrenoMemInfo::getInstance().isMacroTilingSupportedByGPU() &&
-       isMacroTileSupportedByMDP)
+       MDPCapabilityInfo::getInstance().isMacroTilingSupportedByMDP())
     {
         // check the format
         switch(format)
@@ -728,8 +728,8 @@
     size = getSize(format, width, height, usage, alignedw, alignedh);
 }
 
-void getYuvUbwcSPPlaneInfo(private_handle_t* hnd, int color_format,
-                         struct android_ycbcr* ycbcr)
+void getYuvUbwcSPPlaneInfo(uint64_t base, int width, int height,
+                           int color_format, struct android_ycbcr* ycbcr)
 {
     // UBWC buffer has these 4 planes in the following sequence:
     // Y_Meta_Plane, Y_Plane, UV_Meta_Plane, UV_Plane
@@ -737,8 +737,6 @@
     unsigned int y_stride, y_height, y_size;
     unsigned int c_meta_stride, c_meta_height, c_meta_size;
     unsigned int alignment = 4096;
-    int width = hnd->width;
-    int height = hnd->height;
 
     y_meta_stride = VENUS_Y_META_STRIDE(color_format, width);
     y_meta_height = VENUS_Y_META_SCANLINES(color_format, height);
@@ -752,25 +750,23 @@
     c_meta_height = VENUS_UV_META_SCANLINES(color_format, height);
     c_meta_size = ALIGN((c_meta_stride * c_meta_height), alignment);
 
-    ycbcr->y  = (void*)(hnd->base + y_meta_size);
-    ycbcr->cb = (void*)(hnd->base + y_meta_size + y_size + c_meta_size);
-    ycbcr->cr = (void*)(hnd->base + y_meta_size + y_size +
+    ycbcr->y  = (void*)(base + y_meta_size);
+    ycbcr->cb = (void*)(base + y_meta_size + y_size + c_meta_size);
+    ycbcr->cr = (void*)(base + y_meta_size + y_size +
                         c_meta_size + 1);
     ycbcr->ystride = y_stride;
     ycbcr->cstride = VENUS_UV_STRIDE(color_format, width);
 }
 
-void getYuvSPPlaneInfo(private_handle_t* hnd, int bpp,
-                          struct android_ycbcr* ycbcr)
+void getYuvSPPlaneInfo(uint64_t base, int width, int height, int bpp,
+                       struct android_ycbcr* ycbcr)
 {
-    int width = hnd->width;
-    int height = hnd->height;
     unsigned int ystride, cstride;
 
     ystride = cstride = width * bpp;
-    ycbcr->y  = (void*)hnd->base;
-    ycbcr->cb = (void*)(hnd->base + ystride * height);
-    ycbcr->cr = (void*)(hnd->base + ystride * height + 1);
+    ycbcr->y  = (void*)base;
+    ycbcr->cb = (void*)(base + ystride * height);
+    ycbcr->cr = (void*)(base + ystride * height + 1);
     ycbcr->ystride = ystride;
     ycbcr->cstride = cstride;
     ycbcr->chroma_step = 2 * bpp;
@@ -813,20 +809,22 @@
         case HAL_PIXEL_FORMAT_YCbCr_422_SP:
         case HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS:
         case HAL_PIXEL_FORMAT_NV12_ENCODEABLE: //Same as YCbCr_420_SP_VENUS
-            getYuvSPPlaneInfo(hnd, 1, ycbcr);
+            getYuvSPPlaneInfo(hnd->base, width, height, 1, ycbcr);
         break;
 
         case HAL_PIXEL_FORMAT_YCbCr_420_P010:
-            getYuvSPPlaneInfo(hnd, 2, ycbcr);
+            getYuvSPPlaneInfo(hnd->base, width, height, 2, ycbcr);
         break;
 
         case HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS_UBWC:
-            getYuvUbwcSPPlaneInfo(hnd, COLOR_FMT_NV12_UBWC, ycbcr);
+            getYuvUbwcSPPlaneInfo(hnd->base, width, height,
+                                  COLOR_FMT_NV12_UBWC, ycbcr);
             ycbcr->chroma_step = 2;
         break;
 
         case HAL_PIXEL_FORMAT_YCbCr_420_TP10_UBWC:
-            getYuvUbwcSPPlaneInfo(hnd, COLOR_FMT_NV12_BPP10_UBWC, ycbcr);
+            getYuvUbwcSPPlaneInfo(hnd->base, width, height,
+                                  COLOR_FMT_NV12_BPP10_UBWC, ycbcr);
             ycbcr->chroma_step = 3;
         break;
 
@@ -837,7 +835,7 @@
         case HAL_PIXEL_FORMAT_NV21_ZSL:
         case HAL_PIXEL_FORMAT_RAW16:
         case HAL_PIXEL_FORMAT_RAW10:
-            getYuvSPPlaneInfo(hnd, 1, ycbcr);
+            getYuvSPPlaneInfo(hnd->base, width, height, 1, ycbcr);
             std::swap(ycbcr->cb, ycbcr->cr);
         break;
 
diff --git a/msm8996/libgralloc/gr.h b/msm8996/libgralloc/gr.h
index bb1f6dd..e7d449e 100644
--- a/msm8996/libgralloc/gr.h
+++ b/msm8996/libgralloc/gr.h
@@ -197,4 +197,21 @@
 
         unsigned int (*LINK_adreno_get_gpu_pixel_alignment) ();
 };
+
+
+class MDPCapabilityInfo : public android::Singleton <MDPCapabilityInfo>
+{
+    int isMacroTileSupported;
+    public:
+        MDPCapabilityInfo();
+        /*
+        * Function to return whether MDP support MacroTile feature
+        *
+        * @return  1 : supported
+        *          0 : not supported
+        */
+        int isMacroTilingSupportedByMDP();
+
+};
+
 #endif /* GR_H_ */
diff --git a/msm8996/sdm/include/private/color_params.h b/msm8996/sdm/include/private/color_params.h
index e7f779a..9eb5d79 100644
--- a/msm8996/sdm/include/private/color_params.h
+++ b/msm8996/sdm/include/private/color_params.h
@@ -48,6 +48,8 @@
   kEnterQDCMMode = BITMAP(3),
   kExitQDCMMode = BITMAP(4),
   kSetPanelBrightness = BITMAP(5),
+  kEnableFrameCapture = BITMAP(6),
+  kDisableFrameCapture = BITMAP(7),
   kNoAction = BITMAP(31),
 };
 
@@ -141,6 +143,21 @@
     return ret;
   }
 
+  DisplayError CreatePayloadBytes(uint8_t *output, uint32_t size_in_bytes) {
+    DisplayError ret = kErrorNone;
+
+    payload = new uint8_t[size_in_bytes]();
+    if (!payload) {
+      ret = kErrorMemory;
+      output = NULL;
+    } else {
+      this->size = size_in_bytes;
+      output = payload;
+      own_payload = true;
+    }
+    return ret;
+  }
+
   inline void DestroyPayload() {
     if (payload && own_payload) {
       delete[] payload;
@@ -153,6 +170,34 @@
   }
 };
 
+struct PPRectInfo {
+  uint32_t width;
+  uint32_t height;
+  int32_t x;
+  int32_t y;
+};
+
+typedef enum {
+  PP_PIXEL_FORMAT_NONE = 0,
+  PP_PIXEL_FORMAT_RGB_888,
+  PP_PIXEL_FORMAT_RGB_2101010,
+  PP_PIXEL_FORMAT_MAX,
+  PP_PIXEL_FORMAT_FORCE32BIT = 0x7FFFFFFF,
+} PPPixelFormats;
+
+struct PPFrameCaptureInputParams {
+  PPRectInfo rect;
+  PPPixelFormats out_pix_format;
+  uint32_t flags;
+};
+
+struct PPFrameCaptureData {
+  PPFrameCaptureInputParams input_params;
+  uint8_t *buffer;
+  uint32_t buffer_stride;
+  uint32_t buffer_size;
+};
+
 struct SDEGamutCfg {
   static const int kGamutTableNum = 4;
   static const int kGamutScaleoffTableNum = 3;
@@ -413,7 +458,7 @@
   }
 
   inline Locker &GetLocker(void) { return locker_; }
-
+  inline PPFrameCaptureData *GetFrameCaptureData(void) { return &frame_capture_data; }
   // Once all features are consumed, destroy/release all TFeatureInfo<T> on the list,
   // then clear dirty_ flag and return the lock to the TFeatureInfo<T> producer.
   void Reset();
@@ -429,6 +474,7 @@
   Locker locker_;
   PPFeatureInfo *feature_[kMaxNumPPFeatures];  // reference to TFeatureInfo<T>.
   uint32_t next_idx_ = 0;
+  PPFrameCaptureData frame_capture_data;
 };
 
 }  // namespace sdm
diff --git a/msm8996/sdm/include/private/hw_info_types.h b/msm8996/sdm/include/private/hw_info_types.h
index 5ecb75d..148de7c 100644
--- a/msm8996/sdm/include/private/hw_info_types.h
+++ b/msm8996/sdm/include/private/hw_info_types.h
@@ -35,6 +35,9 @@
 namespace sdm {
 const int kMaxSDELayers = 16;   // Maximum number of layers that can be handled by hardware in a
                                 // given layer stack.
+#define MAX_PLANES 4
+
+#define MAX_DETAIL_ENHANCE_CURVE 3
 
 enum HWDeviceType {
   kDevicePrimary,
@@ -84,9 +87,31 @@
   kHWRotatorInput,
   kHWRotatorOutput,
   kHWWBIntfOutput,
+  kHWDestinationScalar,
   kHWSubBlockMax,
 };
 
+// y/RGB & UV Scaling Filters
+enum HWScalingFilter {
+  kScalingFilterEdgeDirected,
+  kScalingFilterCircular,
+  kScalingFilterSeparable,
+  kScalingFilterBilinear,
+  kScalingFilterMax,
+};
+
+enum HWAlphaInterpolation {
+  kInterpolationPixelRepeat,
+  kInterpolationBilinear,
+  kInterpolationMax,
+};
+
+enum HWBlendingFilter {
+  kBlendFilterCircular,
+  kBlendFilterSeparable,
+  kBlendFilterMax,
+};
+
 typedef std::map<HWSubBlockType, std::vector<LayerBufferFormat>> FormatsMap;
 
 struct HWDynBwLimitInfo {
@@ -148,6 +173,7 @@
   bool perf_calc = false;
   bool has_dyn_bw_support = false;
   bool separate_rotator = false;
+  bool has_qseed3 = false;
   HWDynBwLimitInfo dyn_bw_info;
   std::vector<HWPipeCaps> hw_pipes;
   FormatsMap supported_formats_map;
@@ -263,44 +289,101 @@
   bool is_buffer_cached = false;
 };
 
+struct HWScaleLutInfo {
+  uint32_t dir_lut_size = 0;
+  uint32_t cir_lut_size = 0;
+  uint32_t sep_lut_size = 0;
+  uint64_t dir_lut = 0;
+  uint64_t cir_lut = 0;
+  uint64_t sep_lut = 0;
+};
+
+struct HWDetailEnhanceData {
+  uint32_t enable = 0;
+  int16_t sharpen_level1 = 0;
+  int16_t sharpen_level2 = 0;
+  uint16_t clip = 0;
+  uint16_t limit = 0;
+  uint16_t thr_quiet = 0;
+  uint16_t thr_dieout = 0;
+  uint16_t thr_low = 0;
+  uint16_t thr_high = 0;
+  uint16_t prec_shift = 0;
+  int16_t adjust_a[MAX_DETAIL_ENHANCE_CURVE] = {0};
+  int16_t adjust_b[MAX_DETAIL_ENHANCE_CURVE] = {0};
+  int16_t adjust_c[MAX_DETAIL_ENHANCE_CURVE] = {0};
+};
+
 struct HWPixelExtension {
-  int extension;  // Number of pixels extension in left, right, top and bottom directions for all
-                  // color components. This pixel value for each color component should be sum of
-                  // fetch and repeat pixels.
+  int32_t extension = 0;  // Number of pixels extension in left, right, top and bottom directions
+                          // for all color components. This pixel value for each color component
+                          // should be sum of fetch and repeat pixels.
 
-  int overfetch;  // Number of pixels need to be overfetched in left, right, top and bottom
-                  // directions from source image for scaling.
+  int32_t overfetch = 0;  // Number of pixels need to be overfetched in left, right, top and bottom
+                          // directions from source image for scaling.
 
-  int repeat;     // Number of pixels need to be repeated in left, right, top and bottom directions
-                  // for scaling.
+  int32_t repeat = 0;     // Number of pixels need to be repeated in left, right, top and bottom
+                          // directions for scaling.
 };
 
 struct HWPlane {
-  int init_phase_x = 0;
-  int phase_step_x = 0;
-  int init_phase_y = 0;
-  int phase_step_y = 0;
+  int32_t init_phase_x = 0;
+  int32_t phase_step_x = 0;
+  int32_t init_phase_y = 0;
+  int32_t phase_step_y = 0;
   HWPixelExtension left;
   HWPixelExtension top;
   HWPixelExtension right;
   HWPixelExtension bottom;
   uint32_t roi_width = 0;
-};
-
-struct ScaleData {
-  uint8_t enable_pixel_ext;
+  int32_t preload_x = 0;
+  int32_t preload_y = 0;
   uint32_t src_width = 0;
   uint32_t src_height = 0;
-  HWPlane plane[4];
+};
+
+struct HWScaleData {
+  struct enable {
+    uint8_t scale = 0;
+    uint8_t direction_detection = 0;
+    uint8_t detail_enhance = 0;
+  } enable;
+  uint32_t dst_width = 0;
+  uint32_t dst_height = 0;
+  HWPlane plane[MAX_PLANES];
+  // scale_v2_data fields
+  HWScalingFilter y_rgb_filter_cfg = kScalingFilterEdgeDirected;
+  HWScalingFilter uv_filter_cfg = kScalingFilterEdgeDirected;
+  HWAlphaInterpolation alpha_filter_cfg = kInterpolationPixelRepeat;
+  HWBlendingFilter blend_cfg = kBlendFilterCircular;
+
+  struct lut_flags {
+    uint8_t lut_swap = 0;
+    uint8_t lut_dir_wr = 0;
+    uint8_t lut_y_cir_wr = 0;
+    uint8_t lut_uv_cir_wr = 0;
+    uint8_t lut_y_sep_wr = 0;
+    uint8_t lut_uv_sep_wr = 0;
+  } lut_flag;
+
+  uint32_t dir_lut_idx = 0;
+  /* for Y(RGB) and UV planes*/
+  uint32_t y_rgb_cir_lut_idx = 0;
+  uint32_t uv_cir_lut_idx = 0;
+  uint32_t y_rgb_sep_lut_idx = 0;
+  uint32_t uv_sep_lut_idx = 0;
+
+  HWDetailEnhanceData detail_enhance;
 };
 
 struct HWPipeInfo {
   uint32_t pipe_id = 0;
+  HWSubBlockType sub_block_type = kHWSubBlockMax;
   LayerRect src_roi;
   LayerRect dst_roi;
   uint8_t horizontal_decimation = 0;
   uint8_t vertical_decimation = 0;
-  ScaleData scale_data;
+  HWScaleData scale_data;
   uint32_t z_order = 0;
   bool set_igc = false;
   bool valid = false;
diff --git a/msm8996/sdm/include/private/resource_interface.h b/msm8996/sdm/include/private/resource_interface.h
index 9a5aaea..c350460 100644
--- a/msm8996/sdm/include/private/resource_interface.h
+++ b/msm8996/sdm/include/private/resource_interface.h
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+* Copyright (c) 2015 - 2016, 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:
@@ -52,6 +52,7 @@
   virtual DisplayError ValidateCursorPosition(Handle display_ctx, HWLayers *hw_layers,
                                               int x, int y) = 0;
   virtual DisplayError SetMaxBandwidthMode(HWBwModes mode) = 0;
+  virtual DisplayError GetScaleLutConfig(HWScaleLutInfo *lut_info) = 0;
 
  protected:
   virtual ~ResourceInterface() { }
diff --git a/msm8996/sdm/libs/core/Android.mk b/msm8996/sdm/libs/core/Android.mk
index 738f6ca..218ff98 100644
--- a/msm8996/sdm/libs/core/Android.mk
+++ b/msm8996/sdm/libs/core/Android.mk
@@ -28,6 +28,7 @@
                                  $(LOCAL_HW_INTF_PATH)/hw_primary.cpp \
                                  $(LOCAL_HW_INTF_PATH)/hw_hdmi.cpp \
                                  $(LOCAL_HW_INTF_PATH)/hw_virtual.cpp \
-                                 $(LOCAL_HW_INTF_PATH)/hw_color_manager.cpp
+                                 $(LOCAL_HW_INTF_PATH)/hw_color_manager.cpp \
+                                 $(LOCAL_HW_INTF_PATH)/hw_scale.cpp
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/msm8996/sdm/libs/core/comp_manager.cpp b/msm8996/sdm/libs/core/comp_manager.cpp
index dc1e29e..a7f137a 100644
--- a/msm8996/sdm/libs/core/comp_manager.cpp
+++ b/msm8996/sdm/libs/core/comp_manager.cpp
@@ -475,5 +475,9 @@
   return false;
 }
 
+DisplayError CompManager::GetScaleLutConfig(HWScaleLutInfo *lut_info) {
+  return resource_intf_->GetScaleLutConfig(lut_info);
+}
+
 }  // namespace sdm
 
diff --git a/msm8996/sdm/libs/core/comp_manager.h b/msm8996/sdm/libs/core/comp_manager.h
index 7744070..d6d45c1 100644
--- a/msm8996/sdm/libs/core/comp_manager.h
+++ b/msm8996/sdm/libs/core/comp_manager.h
@@ -61,6 +61,7 @@
   bool SupportLayerAsCursor(Handle display_ctx, HWLayers *hw_layers);
   bool CanSetIdleTimeout(Handle display_ctx);
   DisplayError SetMaxBandwidthMode(HWBwModes mode);
+  DisplayError GetScaleLutConfig(HWScaleLutInfo *lut_info);
 
   // DumpImpl method
   virtual void AppendDump(char *buffer, uint32_t length);
diff --git a/msm8996/sdm/libs/core/display_base.cpp b/msm8996/sdm/libs/core/display_base.cpp
index c2973e0..5a155b7 100644
--- a/msm8996/sdm/libs/core/display_base.cpp
+++ b/msm8996/sdm/libs/core/display_base.cpp
@@ -54,6 +54,16 @@
   hw_intf_->GetActiveConfig(&active_index);
   hw_intf_->GetDisplayAttributes(active_index, &display_attrib);
 
+  HWScaleLutInfo lut_info = {};
+  error = comp_manager_->GetScaleLutConfig(&lut_info);
+  if (error == kErrorNone) {
+    error = hw_intf_->SetScaleLutConfig(&lut_info);
+  }
+
+  if (error != kErrorNone) {
+    goto CleanupOnError;
+  }
+
   error = comp_manager_->RegisterDisplay(display_type_, display_attrib,
                                          hw_panel_info_, &display_comp_ctx_);
   if (error != kErrorNone) {
diff --git a/msm8996/sdm/libs/core/fb/hw_device.cpp b/msm8996/sdm/libs/core/fb/hw_device.cpp
index e4f8dae..fe157a2 100644
--- a/msm8996/sdm/libs/core/fb/hw_device.cpp
+++ b/msm8996/sdm/libs/core/fb/hw_device.cpp
@@ -59,7 +59,6 @@
 }
 
 DisplayError HWDevice::Init(HWEventHandler *eventhandler) {
-  DisplayError error = kErrorNone;
   char device_name[64] = {0};
 
   event_handler_ = eventhandler;
@@ -84,10 +83,12 @@
     return kErrorResources;
   }
 
-  return error;
+  return HWScale::Create(&hw_scale_, hw_resource_.has_qseed3);
 }
 
 DisplayError HWDevice::Deinit() {
+  HWScale::Destroy(hw_scale_);
+
   if (device_fd_ >= 0) {
     Sys::close_(device_fd_);
     device_fd_ = -1;
@@ -232,13 +233,9 @@
         }
         mdp_layer.bg_color = layer.solid_fill_color;
 
-        if (pipe_info->scale_data.enable_pixel_ext) {
-          SetHWScaleData(pipe_info->scale_data, mdp_layer_count);
-          mdp_layer.flags |= MDP_LAYER_ENABLE_PIXEL_EXT;
-        }
-
-        // Send scale data to MDP driver
-        mdp_layer.scale = GetScaleDataRef(mdp_layer_count);
+        // HWScaleData to MDP driver
+        hw_scale_->SetHWScaleData(pipe_info->scale_data, mdp_layer_count, &mdp_layer);
+        mdp_layer.scale = hw_scale_->GetScaleDataRef(mdp_layer_count);
         mdp_layer_count++;
 
         DLOGV_IF(kTagDriverConfig, "******************* Layer[%d] %s pipe Input ******************",
@@ -253,17 +250,7 @@
                  mdp_layer.src_rect.y, mdp_layer.src_rect.w, mdp_layer.src_rect.h);
         DLOGV_IF(kTagDriverConfig, "dst_rect [%d, %d, %d, %d]", mdp_layer.dst_rect.x,
                  mdp_layer.dst_rect.y, mdp_layer.dst_rect.w, mdp_layer.dst_rect.h);
-        for (int j = 0; j < 4; j++) {
-          mdp_scale_data *scale = reinterpret_cast<mdp_scale_data*>(mdp_layer.scale);
-          DLOGV_IF(kTagDriverConfig, "Scale Data[%d]: Phase=[%x %x %x %x] Pixel_Ext=[%d %d %d %d]",
-                 j, scale->init_phase_x[j], scale->phase_step_x[j], scale->init_phase_y[j],
-                 scale->phase_step_y[j], scale->num_ext_pxls_left[j], scale->num_ext_pxls_top[j],
-                 scale->num_ext_pxls_right[j], scale->num_ext_pxls_btm[j]);
-          DLOGV_IF(kTagDriverConfig, "Fetch=[%d %d %d %d]  Repeat=[%d %d %d %d]  roi_width = %d",
-                 scale->left_ftch[j], scale->top_ftch[j], scale->right_ftch[j], scale->btm_ftch[j],
-                 scale->left_rpt[j], scale->top_rpt[j], scale->right_rpt[j], scale->btm_rpt[j],
-                 scale->roi_w[j]);
-        }
+        hw_scale_->DumpScaleData(mdp_layer.scale);
         DLOGV_IF(kTagDriverConfig, "*************************************************************");
       }
     }
@@ -958,7 +945,7 @@
   memset(&mdp_disp_commit_, 0, sizeof(mdp_disp_commit_));
   memset(&mdp_in_layers_, 0, sizeof(mdp_in_layers_));
   memset(&mdp_out_layer_, 0, sizeof(mdp_out_layer_));
-  memset(&scale_data_, 0, sizeof(scale_data_));
+  hw_scale_->ResetScaleParams();
   memset(&pp_params_, 0, sizeof(pp_params_));
   memset(&igc_lut_data_, 0, sizeof(igc_lut_data_));
 
@@ -973,37 +960,6 @@
   mdp_disp_commit_.commit_v1.retire_fence = -1;
 }
 
-void HWDevice::SetHWScaleData(const ScaleData &scale, uint32_t index) {
-  mdp_scale_data *mdp_scale = GetScaleDataRef(index);
-  mdp_scale->enable_pxl_ext = scale.enable_pixel_ext;
-
-  for (int i = 0; i < 4; i++) {
-    const HWPlane &plane = scale.plane[i];
-    mdp_scale->init_phase_x[i] = plane.init_phase_x;
-    mdp_scale->phase_step_x[i] = plane.phase_step_x;
-    mdp_scale->init_phase_y[i] = plane.init_phase_y;
-    mdp_scale->phase_step_y[i] = plane.phase_step_y;
-
-    mdp_scale->num_ext_pxls_left[i] = plane.left.extension;
-    mdp_scale->left_ftch[i] = plane.left.overfetch;
-    mdp_scale->left_rpt[i] = plane.left.repeat;
-
-    mdp_scale->num_ext_pxls_top[i] = plane.top.extension;
-    mdp_scale->top_ftch[i] = plane.top.overfetch;
-    mdp_scale->top_rpt[i] = plane.top.repeat;
-
-    mdp_scale->num_ext_pxls_right[i] = plane.right.extension;
-    mdp_scale->right_ftch[i] = plane.right.overfetch;
-    mdp_scale->right_rpt[i] = plane.right.repeat;
-
-    mdp_scale->num_ext_pxls_btm[i] = plane.bottom.extension;
-    mdp_scale->btm_ftch[i] = plane.bottom.overfetch;
-    mdp_scale->btm_rpt[i] = plane.bottom.repeat;
-
-    mdp_scale->roi_w[i] = plane.roi_width;
-  }
-}
-
 void HWDevice::SetCSC(LayerCSC source, mdp_color_space *color_space) {
   switch (source) {
   case kCSCLimitedRange601:    *color_space = MDP_CSC_ITU_R_601;      break;
@@ -1133,5 +1089,40 @@
   return kErrorNotSupported;
 }
 
+DisplayError HWDevice::SetScaleLutConfig(HWScaleLutInfo *lut_info) {
+  STRUCT_VAR(mdp_scale_luts_info, mdp_lut_info);
+  STRUCT_VAR(mdp_set_cfg, cfg);
+
+  if (!hw_resource_.has_qseed3) {
+    DLOGV_IF(kTagDriverConfig, "No support for QSEED3 luts");
+    return kErrorNone;
+  }
+
+  if (!lut_info->dir_lut_size && !lut_info->dir_lut && !lut_info->cir_lut_size &&
+      !lut_info->cir_lut && !lut_info->sep_lut_size && !lut_info->sep_lut) {
+      // HWSupports QSEED3, but LutInfo is invalid as scalar is disabled by property or
+      // its loading failed. Driver will use default settings/filter
+      return kErrorNone;
+  }
+
+  mdp_lut_info.dir_lut_size = lut_info->dir_lut_size;
+  mdp_lut_info.dir_lut = lut_info->dir_lut;
+  mdp_lut_info.cir_lut_size = lut_info->cir_lut_size;
+  mdp_lut_info.cir_lut = lut_info->cir_lut;
+  mdp_lut_info.sep_lut_size = lut_info->sep_lut_size;
+  mdp_lut_info.sep_lut = lut_info->sep_lut;
+
+  cfg.flags = MDP_QSEED3_LUT_CFG;
+  cfg.len = sizeof(mdp_scale_luts_info);
+  cfg.payload = reinterpret_cast<uint64_t>(&mdp_lut_info);
+
+  if (Sys::ioctl_(device_fd_, MSMFB_MDP_SET_CFG, &cfg) < 0) {
+    IOCTL_LOGE(MSMFB_MDP_SET_CFG, device_type_);
+    return kErrorHardware;
+  }
+
+  return kErrorNone;
+}
+
 }  // namespace sdm
 
diff --git a/msm8996/sdm/libs/core/fb/hw_device.h b/msm8996/sdm/libs/core/fb/hw_device.h
index a0eeca6..82a9ee6 100644
--- a/msm8996/sdm/libs/core/fb/hw_device.h
+++ b/msm8996/sdm/libs/core/fb/hw_device.h
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2014 - 2015, The Linux Foundation. All rights reserved.
+* Copyright (c) 2014 - 2016, 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:
@@ -31,6 +31,7 @@
 #include <pthread.h>
 
 #include "hw_interface.h"
+#include "hw_scale.h"
 
 #define IOCTL_LOGE(ioctl, type) DLOGE("ioctl %s, device = %d errno = %d, desc = %s", #ioctl, \
                                       type, errno, strerror(errno))
@@ -74,6 +75,7 @@
   virtual DisplayError GetPanelBrightness(int *level);
   virtual DisplayError SetAutoRefresh(bool enable) { return kErrorNone; }
   virtual DisplayError SetS3DMode(HWS3DMode s3d_mode);
+  virtual DisplayError SetScaleLutConfig(HWScaleLutInfo *lut_info);
 
   // For HWDevice derivatives
   virtual DisplayError Init(HWEventHandler *eventhandler);
@@ -108,8 +110,6 @@
   int ParseLine(char *input, char *tokens[], const uint32_t max_token, uint32_t *count);
   int ParseLine(char *input, const char *delim, char *tokens[],
                 const uint32_t max_token, uint32_t *count);
-  mdp_scale_data* GetScaleDataRef(uint32_t index) { return &scale_data_[index]; }
-  void SetHWScaleData(const ScaleData &scale, uint32_t index);
   void ResetDisplayParams();
   void SetCSC(LayerCSC source, mdp_color_space *color_space);
   void SetIGC(const Layer &layer, uint32_t index);
@@ -130,7 +130,7 @@
   HWDeviceType device_type_;
   mdp_layer_commit mdp_disp_commit_;
   mdp_input_layer mdp_in_layers_[kMaxSDELayers * 2];   // split panel (left + right)
-  mdp_scale_data scale_data_[kMaxSDELayers * 2];
+  HWScale *hw_scale_ = NULL;
   mdp_overlay_pp_params pp_params_[kMaxSDELayers * 2];
   mdp_igc_lut_data_v1_7 igc_lut_data_[kMaxSDELayers * 2];
   mdp_output_layer mdp_out_layer_;
diff --git a/msm8996/sdm/libs/core/fb/hw_info.cpp b/msm8996/sdm/libs/core/fb/hw_info.cpp
index 7db0a47..7b707c1 100644
--- a/msm8996/sdm/libs/core/fb/hw_info.cpp
+++ b/msm8996/sdm/libs/core/fb/hw_info.cpp
@@ -255,6 +255,8 @@
             hw_resource->has_dyn_bw_support = true;
           } else if (!strncmp(tokens[i], "separate_rotator", strlen("separate_rotator"))) {
             hw_resource->separate_rotator = true;
+          } else if (!strncmp(tokens[i], "qseed3", strlen("qseed3"))) {
+            hw_resource->has_qseed3 = true;
           }
         }
       } else if (!strncmp(tokens[0], "pipe_count", strlen("pipe_count"))) {
@@ -315,7 +317,7 @@
         hw_resource->max_scale_down, hw_resource->num_blending_stages);
   DLOGI("BWC = %d, UBWC = %d, Decimation = %d, Tile Format = %d", hw_resource->has_bwc,
         hw_resource->has_ubwc, hw_resource->has_decimation, hw_resource->has_macrotile);
-  DLOGI("SourceSplit = %d", hw_resource->is_src_split);
+  DLOGI("SourceSplit = %d QSEED3 = %d", hw_resource->is_src_split, hw_resource->has_qseed3);
   DLOGI("MaxLowBw = %" PRIu64 " , MaxHighBw = % " PRIu64 "", hw_resource->max_bandwidth_low,
         hw_resource->max_bandwidth_high);
   DLOGI("MaxPipeBw = %" PRIu64 " KBps, MaxSDEClock = % " PRIu64 " Hz, ClockFudgeFactor = %f",
diff --git a/msm8996/sdm/libs/core/fb/hw_scale.cpp b/msm8996/sdm/libs/core/fb/hw_scale.cpp
new file mode 100644
index 0000000..f18727f
--- /dev/null
+++ b/msm8996/sdm/libs/core/fb/hw_scale.cpp
@@ -0,0 +1,286 @@
+/*
+* Copyright (c) 2016, 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 <stdio.h>
+#include <utils/debug.h>
+#include "hw_scale.h"
+
+#define __CLASS__ "HWScale"
+
+namespace sdm {
+
+DisplayError HWScale::Create(HWScale **intf, bool has_qseed3) {
+  if (has_qseed3) {
+    *intf = new HWScaleV2();
+  } else {
+    *intf = new HWScaleV1();
+  }
+
+  return kErrorNone;
+}
+
+DisplayError HWScale::Destroy(HWScale *intf) {
+  delete intf;
+
+  return kErrorNone;
+}
+
+void HWScaleV1::SetHWScaleData(const HWScaleData &scale_data, uint32_t index,
+                               mdp_input_layer *mdp_layer) {
+  if (!scale_data.enable.scale) {
+    return;
+  }
+
+  mdp_layer->flags |= MDP_LAYER_ENABLE_PIXEL_EXT;
+  mdp_scale_data *mdp_scale = &scale_data_v1_.at(index);
+  mdp_scale->enable_pxl_ext = scale_data.enable.scale;
+  for (int i = 0; i < MAX_PLANES; i++) {
+    const HWPlane &plane = scale_data.plane[i];
+    mdp_scale->init_phase_x[i] = plane.init_phase_x;
+    mdp_scale->phase_step_x[i] = plane.phase_step_x;
+    mdp_scale->init_phase_y[i] = plane.init_phase_y;
+    mdp_scale->phase_step_y[i] = plane.phase_step_y;
+
+    mdp_scale->num_ext_pxls_left[i] = plane.left.extension;
+    mdp_scale->left_ftch[i] = plane.left.overfetch;
+    mdp_scale->left_rpt[i] = plane.left.repeat;
+
+    mdp_scale->num_ext_pxls_top[i] = plane.top.extension;
+    mdp_scale->top_ftch[i] = plane.top.overfetch;
+    mdp_scale->top_rpt[i] = plane.top.repeat;
+
+    mdp_scale->num_ext_pxls_right[i] = plane.right.extension;
+    mdp_scale->right_ftch[i] = plane.right.overfetch;
+    mdp_scale->right_rpt[i] = plane.right.repeat;
+
+    mdp_scale->num_ext_pxls_btm[i] = plane.bottom.extension;
+    mdp_scale->btm_ftch[i] = plane.bottom.overfetch;
+    mdp_scale->btm_rpt[i] = plane.bottom.repeat;
+
+    mdp_scale->roi_w[i] = plane.roi_width;
+  }
+
+  return;
+}
+
+void* HWScaleV1::GetScaleDataRef(uint32_t index) {
+  return &scale_data_v1_.at(index);
+}
+
+void HWScaleV1::DumpScaleData(void *mdp_scale) {
+  if (!mdp_scale) {
+    return;
+  }
+
+  mdp_scale_data *scale = reinterpret_cast<mdp_scale_data *>(mdp_scale);
+  if (scale->enable_pxl_ext) {
+    DLOGV_IF(kTagDriverConfig, "Scale Enable = %d", scale->enable_pxl_ext);
+    for (int j = 0; j < MAX_PLANES; j++) {
+      DLOGV_IF(kTagDriverConfig, "Scale Data[%d] : Phase=[%x %x %x %x] Pixel_Ext=[%d %d %d %d]",
+               j, scale->init_phase_x[j], scale->phase_step_x[j], scale->init_phase_y[j],
+               scale->phase_step_y[j], scale->num_ext_pxls_left[j], scale->num_ext_pxls_top[j],
+               scale->num_ext_pxls_right[j], scale->num_ext_pxls_btm[j]);
+      DLOGV_IF(kTagDriverConfig, "Fetch=[%d %d %d %d]  Repeat=[%d %d %d %d]  roi_width = %d",
+               scale->left_ftch[j], scale->top_ftch[j], scale->right_ftch[j], scale->btm_ftch[j],
+               scale->left_rpt[j], scale->top_rpt[j], scale->right_rpt[j], scale->btm_rpt[j],
+               scale->roi_w[j]);
+    }
+  }
+
+  return;
+}
+
+void HWScaleV2::SetHWScaleData(const HWScaleData &scale_data, uint32_t index,
+                               mdp_input_layer *mdp_layer) {
+  if (!scale_data.enable.scale && !scale_data.enable.direction_detection &&
+      !scale_data.enable.detail_enhance ) {
+    return;
+  }
+
+  mdp_scale_data_v2 *mdp_scale = &scale_data_v2_.at(index);
+  mdp_layer->flags |= MDP_LAYER_ENABLE_QSEED3_SCALE;
+  mdp_scale->enable = (scale_data.enable.scale ? ENABLE_SCALE : 0) |
+                      (scale_data.enable.direction_detection ? ENABLE_DIRECTION_DETECTION : 0) |
+                      (scale_data.enable.detail_enhance ? ENABLE_DETAIL_ENHANCE : 0);
+
+  for (int i = 0; i < MAX_PLANES; i++) {
+    const HWPlane &plane = scale_data.plane[i];
+    mdp_scale->init_phase_x[i] = plane.init_phase_x;
+    mdp_scale->phase_step_x[i] = plane.phase_step_x;
+    mdp_scale->init_phase_y[i] = plane.init_phase_y;
+    mdp_scale->phase_step_y[i] = plane.phase_step_y;
+
+    mdp_scale->num_ext_pxls_left[i] = UINT32(plane.left.extension);
+    mdp_scale->left_ftch[i] = plane.left.overfetch;
+    mdp_scale->left_rpt[i] = plane.left.repeat;
+
+    mdp_scale->num_ext_pxls_top[i] = UINT32(plane.top.extension);
+    mdp_scale->top_ftch[i] = UINT32(plane.top.overfetch);
+    mdp_scale->top_rpt[i] = UINT32(plane.top.repeat);
+
+    mdp_scale->num_ext_pxls_right[i] = UINT32(plane.right.extension);
+    mdp_scale->right_ftch[i] = plane.right.overfetch;
+    mdp_scale->right_rpt[i] = plane.right.repeat;
+
+    mdp_scale->num_ext_pxls_btm[i] = UINT32(plane.bottom.extension);
+    mdp_scale->btm_ftch[i] = UINT32(plane.bottom.overfetch);
+    mdp_scale->btm_rpt[i] = UINT32(plane.bottom.repeat);
+
+    mdp_scale->roi_w[i] = plane.roi_width;
+
+    mdp_scale->preload_x[i] = UINT32(plane.preload_x);
+    mdp_scale->preload_y[i] = UINT32(plane.preload_y);
+
+    mdp_scale->src_width[i] = plane.src_width;
+    mdp_scale->src_height[i] = plane.src_height;
+  }
+
+  mdp_scale->dst_width = scale_data.dst_width;
+  mdp_scale->dst_height = scale_data.dst_height;
+
+  mdp_scale->y_rgb_filter_cfg = GetMDPScalingFilter(scale_data.y_rgb_filter_cfg);
+  mdp_scale->uv_filter_cfg = GetMDPScalingFilter(scale_data.uv_filter_cfg);
+  mdp_scale->alpha_filter_cfg = GetMDPAlphaInterpolation(scale_data.alpha_filter_cfg);
+  mdp_scale->blend_cfg = scale_data.blend_cfg;
+
+  mdp_scale->lut_flag = (scale_data.lut_flag.lut_swap ? SCALER_LUT_SWAP : 0) |
+                        (scale_data.lut_flag.lut_dir_wr ? SCALER_LUT_DIR_WR : 0) |
+                        (scale_data.lut_flag.lut_y_cir_wr ? SCALER_LUT_Y_CIR_WR : 0) |
+                        (scale_data.lut_flag.lut_uv_cir_wr ? SCALER_LUT_UV_CIR_WR : 0) |
+                        (scale_data.lut_flag.lut_y_sep_wr ? SCALER_LUT_Y_SEP_WR : 0) |
+                        (scale_data.lut_flag.lut_uv_sep_wr ? SCALER_LUT_UV_SEP_WR : 0);
+
+  // lut indicies - index starts from 0, hence subtract by 1 when > 0
+  mdp_scale->dir_lut_idx = (scale_data.dir_lut_idx > 0) ? scale_data.dir_lut_idx - 1 :
+                                                          scale_data.dir_lut_idx;
+  mdp_scale->y_rgb_cir_lut_idx = (scale_data.y_rgb_cir_lut_idx  > 0) ?
+                                  scale_data.y_rgb_cir_lut_idx - 1 : scale_data.y_rgb_cir_lut_idx;
+  mdp_scale->uv_cir_lut_idx = (scale_data.uv_cir_lut_idx > 0) ? scale_data.uv_cir_lut_idx - 1 :
+                               scale_data.uv_cir_lut_idx;
+  mdp_scale->y_rgb_sep_lut_idx = (scale_data.y_rgb_sep_lut_idx  > 0) ?
+                                  scale_data.y_rgb_sep_lut_idx - 1 : scale_data.y_rgb_sep_lut_idx;
+  mdp_scale->uv_sep_lut_idx = (scale_data.uv_sep_lut_idx  > 0) ? scale_data.uv_sep_lut_idx - 1 :
+                               scale_data.uv_sep_lut_idx;
+
+  if (mdp_scale->enable & ENABLE_DETAIL_ENHANCE) {
+    mdp_det_enhance_data *mdp_det_enhance = &mdp_scale->detail_enhance;
+    mdp_det_enhance->enable = scale_data.detail_enhance.enable;
+    mdp_det_enhance->sharpen_level1 = scale_data.detail_enhance.sharpen_level1;
+    mdp_det_enhance->sharpen_level2 = scale_data.detail_enhance.sharpen_level2;
+    mdp_det_enhance->clip = scale_data.detail_enhance.clip;
+    mdp_det_enhance->limit = scale_data.detail_enhance.limit;
+    mdp_det_enhance->thr_quiet = scale_data.detail_enhance.thr_quiet;
+    mdp_det_enhance->thr_dieout = scale_data.detail_enhance.thr_dieout;
+    mdp_det_enhance->thr_low = scale_data.detail_enhance.thr_low;
+    mdp_det_enhance->thr_high = scale_data.detail_enhance.thr_high;
+    mdp_det_enhance->prec_shift = scale_data.detail_enhance.prec_shift;
+
+    for (int i = 0; i < MAX_DET_CURVES; i++) {
+      mdp_det_enhance->adjust_a[i] = scale_data.detail_enhance.adjust_a[i];
+      mdp_det_enhance->adjust_b[i] = scale_data.detail_enhance.adjust_b[i];
+      mdp_det_enhance->adjust_c[i] = scale_data.detail_enhance.adjust_c[i];
+    }
+  }
+
+  return;
+}
+
+void* HWScaleV2::GetScaleDataRef(uint32_t index) {
+  return &scale_data_v2_.at(index);
+}
+
+uint32_t HWScaleV2::GetMDPScalingFilter(HWScalingFilter filter_cfg) {
+  switch (filter_cfg) {
+  case kScalingFilterEdgeDirected:
+    return FILTER_EDGE_DIRECTED_2D;
+  case kScalingFilterCircular:
+    return FILTER_CIRCULAR_2D;
+  case kScalingFilterSeparable:
+    return FILTER_SEPARABLE_1D;
+  case kScalingFilterBilinear:
+    return FILTER_BILINEAR;
+  default:
+    DLOGE("Invalid Scaling Filter");
+    return kScalingFilterMax;
+  }
+}
+
+uint32_t HWScaleV2::GetMDPAlphaInterpolation(HWAlphaInterpolation alpha_filter_cfg) {
+  switch (alpha_filter_cfg) {
+  case kInterpolationPixelRepeat:
+    return FILTER_ALPHA_DROP_REPEAT;
+  case kInterpolationBilinear:
+    return FILTER_ALPHA_BILINEAR;
+  default:
+    DLOGE("Invalid Alpha Interpolation");
+    return kInterpolationMax;
+  }
+}
+
+void HWScaleV2::DumpScaleData(void *mdp_scale) {
+  if (!mdp_scale) {
+    return;
+  }
+
+  mdp_scale_data_v2 *scale = reinterpret_cast<mdp_scale_data_v2 *>(mdp_scale);
+  if (scale->enable) {
+    DLOGV_IF(kTagDriverConfig, "Scale Enable = %d", scale->enable);
+    for (int j = 0; j < MAX_PLANES; j++) {
+      DLOGV_IF(kTagDriverConfig, "Scale Data[%d]: Phase_init[x y]=[%x %x] Phase_step:[x y]=[%x %x]",
+        j, scale->init_phase_x[j], scale->init_phase_y[j], scale->phase_step_x[j],
+        scale->phase_step_y[j]);
+      DLOGV_IF(kTagDriverConfig, "Preload[x y]=[%x %x], Pixel Ext=[%d %d] Ovfetch=[%d %d %d %d]",
+        scale->preload_x[j], scale->preload_y[j], scale->num_ext_pxls_left[j],
+        scale->num_ext_pxls_top[j], scale->left_ftch[j], scale->top_ftch[j], scale->right_ftch[j],
+        scale->btm_ftch[j]);
+      DLOGV_IF(kTagDriverConfig, "Repeat=[%d %d %d %d] Src[w x h]=[%d %d] roi_width = %d",
+        scale->left_rpt[j], scale->top_rpt[j], scale->right_rpt[j], scale->btm_rpt[j],
+        scale->src_width[j], scale->src_height[j], scale->roi_w[j]);
+    }
+
+    DLOGV_IF(kTagDriverConfig, "LUT flags = %d", scale->lut_flag);
+    DLOGV_IF(kTagDriverConfig, "y_rgb_filter=%d, uv_filter=%d, alpha_filter=%d, blend_cfg=%d",
+      scale->y_rgb_filter_cfg, scale->uv_filter_cfg, scale->alpha_filter_cfg, scale->blend_cfg);
+    DLOGV_IF(kTagDriverConfig, "dir_lut=%d, y_rgb_cir=%d, uv_cir=%d, y_rgb_sep=%d, uv_sep=%d",
+      scale->dir_lut_idx, scale->y_rgb_cir_lut_idx, scale->uv_cir_lut_idx,
+      scale->y_rgb_sep_lut_idx, scale->uv_sep_lut_idx);
+    if (scale->enable & ENABLE_DETAIL_ENHANCE) {
+      mdp_det_enhance_data *de = &scale->detail_enhance;
+      DLOGV_IF(kTagDriverConfig, "Detail Enhance: enable: %d sharpen_level1: %d sharpen_level2: %d",
+        de->enable, de->sharpen_level1, de->sharpen_level2);
+      DLOGV_IF(kTagDriverConfig, "clip: %d limit:%d thr_quiet: %d thr_dieout: %d",
+        de->clip, de->limit, de->thr_quiet, de->thr_dieout);
+      DLOGV_IF(kTagDriverConfig, "thr_low: %d thr_high: %d prec_shift: %d", de->thr_low,
+        de->thr_high, de->prec_shift);
+      for (uint32_t i = 0; i < MAX_DET_CURVES; i++) {
+        DLOGV_IF(kTagDriverConfig, "adjust_a[%d]: %d adjust_b[%d]: %d adjust_c[%d]: %d", i,
+          de->adjust_a[i], i, de->adjust_b[i], i, de->adjust_c[i]);
+      }
+    }
+  }
+
+  return;
+}
+
+}  // namespace sdm
diff --git a/msm8996/sdm/libs/core/fb/hw_scale.h b/msm8996/sdm/libs/core/fb/hw_scale.h
new file mode 100644
index 0000000..0891d42
--- /dev/null
+++ b/msm8996/sdm/libs/core/fb/hw_scale.h
@@ -0,0 +1,83 @@
+/*
+* Copyright (c) 2016, 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 __HW_SCALE_H__
+#define __HW_SCALE_H__
+
+#include <linux/msm_mdp_ext.h>
+#include <private/hw_info_types.h>
+
+#include <cstring>
+#include <array>
+
+namespace sdm {
+
+class HWScale {
+ public:
+  static DisplayError Create(HWScale **intf, bool has_qseed3);
+  static DisplayError Destroy(HWScale *intf);
+
+  virtual void SetHWScaleData(const HWScaleData &scale, uint32_t index,
+                              mdp_input_layer *mdp_layer) = 0;
+  virtual void* GetScaleDataRef(uint32_t index) = 0;
+  virtual void DumpScaleData(void *mdp_scale) = 0;
+  virtual void ResetScaleParams() = 0;
+ protected:
+  virtual ~HWScale() { }
+};
+
+class HWScaleV1 : public HWScale {
+ public:
+  virtual void SetHWScaleData(const HWScaleData &scale, uint32_t index,
+                              mdp_input_layer *mdp_layer);
+  virtual void* GetScaleDataRef(uint32_t index);
+  virtual void DumpScaleData(void *mdp_scale);
+  virtual void ResetScaleParams() { scale_data_v1_ = {}; }
+
+ protected:
+  ~HWScaleV1() {}
+  std::array<mdp_scale_data, (kMaxSDELayers * 2)> scale_data_v1_ = {};
+};
+
+class HWScaleV2 : public HWScale {
+ public:
+  virtual void SetHWScaleData(const HWScaleData &scale, uint32_t index,
+                              mdp_input_layer *mdp_layer);
+  virtual void* GetScaleDataRef(uint32_t index);
+  virtual void DumpScaleData(void *mdp_scale);
+  virtual void ResetScaleParams() { scale_data_v2_ = {}; }
+
+ protected:
+  ~HWScaleV2() {}
+  std::array<mdp_scale_data_v2, (kMaxSDELayers * 2)> scale_data_v2_ = {};
+
+ private:
+  uint32_t GetMDPAlphaInterpolation(HWAlphaInterpolation alpha_filter_cfg);
+  uint32_t GetMDPScalingFilter(HWScalingFilter filter_cfg);
+};
+
+}  // namespace sdm
+
+#endif  // __HW_SCALE_H__
+
diff --git a/msm8996/sdm/libs/core/hw_interface.h b/msm8996/sdm/libs/core/hw_interface.h
index 9e9114f..7779350 100644
--- a/msm8996/sdm/libs/core/hw_interface.h
+++ b/msm8996/sdm/libs/core/hw_interface.h
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2014 - 2015, The Linux Foundation. All rights reserved.
+* Copyright (c) 2014 - 2016, 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:
@@ -92,6 +92,7 @@
   virtual DisplayError GetPanelBrightness(int *level) = 0;
   virtual DisplayError SetAutoRefresh(bool enable) = 0;
   virtual DisplayError SetS3DMode(HWS3DMode s3d_mode) = 0;
+  virtual DisplayError SetScaleLutConfig(HWScaleLutInfo *lut_info) = 0;
 
  protected:
   virtual ~HWInterface() { }
diff --git a/msm8996/sdm/libs/core/resource_default.cpp b/msm8996/sdm/libs/core/resource_default.cpp
index b2e928e..9a6eb52 100644
--- a/msm8996/sdm/libs/core/resource_default.cpp
+++ b/msm8996/sdm/libs/core/resource_default.cpp
@@ -893,4 +893,8 @@
   return kErrorNotSupported;
 }
 
+DisplayError ResourceDefault::GetScaleLutConfig(HWScaleLutInfo *lut_info) {
+  return kErrorNone;
+}
+
 }  // namespace sdm
diff --git a/msm8996/sdm/libs/core/resource_default.h b/msm8996/sdm/libs/core/resource_default.h
index cb633c0..23d7dfa 100644
--- a/msm8996/sdm/libs/core/resource_default.h
+++ b/msm8996/sdm/libs/core/resource_default.h
@@ -118,6 +118,7 @@
   DisplayError AlignPipeConfig(const Layer &layer, HWPipeInfo *left_pipe, HWPipeInfo *right_pipe);
   void ResourceStateLog(void);
   DisplayError CalculateDecimation(float downscale, uint8_t *decimation);
+  DisplayError GetScaleLutConfig(HWScaleLutInfo *lut_info);
 
   Locker locker_;
   HWResourceInfo hw_res_info_;
diff --git a/msm8996/sdm/libs/hwc/hwc_color_manager.cpp b/msm8996/sdm/libs/hwc/hwc_color_manager.cpp
index 15270d5..27115b5 100644
--- a/msm8996/sdm/libs/hwc/hwc_color_manager.cpp
+++ b/msm8996/sdm/libs/hwc/hwc_color_manager.cpp
@@ -348,6 +348,82 @@
   return ret;
 }
 
+int HWCColorManager::SetFrameCapture(void *params, bool enable, HWCDisplay *hwc_display) {
+  SCOPE_LOCK(locker_);
+  int ret = 0;
+
+  PPFrameCaptureData *frame_capture_data = reinterpret_cast<PPFrameCaptureData*>(params);
+
+  if (enable) {
+    std::memset(&buffer_info, 0x00, sizeof(buffer_info));
+    hwc_display->GetFrameBufferResolution(&buffer_info.buffer_config.width,
+                                          &buffer_info.buffer_config.height);
+    if (frame_capture_data->input_params.out_pix_format == PP_PIXEL_FORMAT_RGB_888) {
+      buffer_info.buffer_config.format = kFormatRGB888;
+    } else if (frame_capture_data->input_params.out_pix_format == PP_PIXEL_FORMAT_RGB_2101010) {
+      // TODO(user): Complete the implementation
+      DLOGE("RGB 10-bit format NOT supported");
+      return -EFAULT;
+    } else {
+      DLOGE("Pixel-format: %d NOT support.", frame_capture_data->input_params.out_pix_format);
+      return -EFAULT;
+    }
+
+    buffer_info.buffer_config.buffer_count = 1;
+    buffer_info.alloc_buffer_info.fd = -1;
+    buffer_info.alloc_buffer_info.stride = 0;
+    buffer_info.alloc_buffer_info.size = 0;
+
+    buffer_allocator_ = new HWCBufferAllocator();
+    if (buffer_allocator_ == NULL) {
+      DLOGE("Memory allocation for buffer_allocator_ FAILED");
+      return -ENOMEM;
+    }
+
+    ret = buffer_allocator_->AllocateBuffer(&buffer_info);
+    if (ret != 0) {
+      DLOGE("Buffer allocation failed. ret: %d", ret);
+      delete[] buffer_allocator_;
+      buffer_allocator_ = NULL;
+      return -ENOMEM;
+    } else {
+      void *buffer = mmap(NULL, buffer_info.alloc_buffer_info.size,
+                          PROT_READ|PROT_WRITE,
+                          MAP_SHARED, buffer_info.alloc_buffer_info.fd, 0);
+
+      if (buffer == MAP_FAILED) {
+        DLOGE("mmap failed. err = %d", errno);
+        frame_capture_data->buffer = NULL;
+        ret = buffer_allocator_->FreeBuffer(&buffer_info);
+        delete[] buffer_allocator_;
+        buffer_allocator_ = NULL;
+        return -EFAULT;
+      } else {
+        frame_capture_data->buffer = reinterpret_cast<uint8_t *>(buffer);
+        frame_capture_data->buffer_stride = buffer_info.alloc_buffer_info.stride;
+        frame_capture_data->buffer_size = buffer_info.alloc_buffer_info.size;
+      }
+      // TODO(user): Call HWC interface to provide the buffer and rectangle information
+    }
+  } else {
+    if (frame_capture_data->buffer != NULL) {
+      if (munmap(frame_capture_data->buffer, buffer_info.alloc_buffer_info.size) != 0) {
+        DLOGE("munmap failed. err = %d", errno);
+      }
+    }
+    if (buffer_allocator_ != NULL) {
+      std::memset(frame_capture_data, 0x00, sizeof(PPFrameCaptureData));
+      ret = buffer_allocator_->FreeBuffer(&buffer_info);
+      if (ret != 0) {
+        DLOGE("FreeBuffer failed. ret = %d", ret);
+      }
+      delete[] buffer_allocator_;
+      buffer_allocator_ = NULL;
+    }
+  }
+  return ret;
+}
+
 const HWCQDCMModeManager::ActiveFeatureCMD HWCQDCMModeManager::kActiveFeatureCMD[] = {
     HWCQDCMModeManager::ActiveFeatureCMD("cabl:on", "cabl:off", "cabl:status", "running"),
     HWCQDCMModeManager::ActiveFeatureCMD("ad:on", "ad:off", "ad:query:status", "running"),
diff --git a/msm8996/sdm/libs/hwc/hwc_color_manager.h b/msm8996/sdm/libs/hwc/hwc_color_manager.h
index cb80b48..2541e21 100644
--- a/msm8996/sdm/libs/hwc/hwc_color_manager.h
+++ b/msm8996/sdm/libs/hwc/hwc_color_manager.h
@@ -117,6 +117,7 @@
   int SetSolidFill(const void *params, bool enable, HWCDisplay *hwc_display);
   bool SolidFillLayersPrepare(hwc_display_contents_1_t **displays, HWCDisplay *hwc_display);
   bool SolidFillLayersSet(hwc_display_contents_1_t **displays, HWCDisplay *hwc_display);
+  int SetFrameCapture(void *params, bool enable, HWCDisplay *hwc_display);
 
  protected:
   int CreateSolidFillLayers(HWCDisplay *hwc_display);
@@ -134,6 +135,8 @@
   bool solid_fill_enable_ = false;
   PPColorFillParams solid_fill_params_;
   hwc_display_contents_1_t *solid_fill_layers_ = NULL;
+  HWCBufferAllocator *buffer_allocator_ = NULL;
+  BufferInfo buffer_info;
   Locker locker_;
 };
 
diff --git a/msm8996/sdm/libs/hwc/hwc_session.cpp b/msm8996/sdm/libs/hwc/hwc_session.cpp
index b8891bd..129b605 100644
--- a/msm8996/sdm/libs/hwc/hwc_session.cpp
+++ b/msm8996/sdm/libs/hwc/hwc_session.cpp
@@ -1168,6 +1168,15 @@
       if (HWC_DISPLAY_PRIMARY == display_id)
         ret = hwc_display_[HWC_DISPLAY_PRIMARY]->SetPanelBrightness(*brightness_value);
       break;
+    case kEnableFrameCapture:
+      ret = color_mgr_->SetFrameCapture(pending_action.params,
+                                        true, hwc_display_[HWC_DISPLAY_PRIMARY]);
+      hwc_procs_->invalidate(hwc_procs_);
+      break;
+    case kDisableFrameCapture:
+      ret = color_mgr_->SetFrameCapture(pending_action.params,
+                                        false, hwc_display_[HWC_DISPLAY_PRIMARY]);
+      break;
     case kNoAction:
       break;
     default: