Merge "sdm:drm: Get DRM display token info at event handler"
diff --git a/libdrmutils/drm_interface.h b/libdrmutils/drm_interface.h
index 46ab782..866ade6 100644
--- a/libdrmutils/drm_interface.h
+++ b/libdrmutils/drm_interface.h
@@ -125,6 +125,12 @@
    */
   PLANE_SET_SCALER_CONFIG,
   /*
+   * Op: Sets plane rotation destination rect
+   * Arg: uint32_t - Plane ID
+   *      DRMRect - rotator dst Rectangle
+   */
+  PLANE_SET_ROTATION_DST_RECT,
+  /*
    * Op: Activate or deactivate a CRTC
    * Arg: uint32_t - CRTC ID
    *      uint32_t - 1 to enable, 0 to disable
@@ -218,6 +224,7 @@
 enum struct DRMRotation {
   FLIP_H = 0x1,
   FLIP_V = 0x2,
+  ROT_180 = FLIP_H | FLIP_V,
   ROT_90 = 0x4,
 };
 
@@ -314,17 +321,22 @@
   uint32_t max_horizontal_deci;
   uint32_t max_vertical_deci;
   uint64_t max_pipe_bandwidth;
+  uint32_t cache_size;  // cache size in bytes for inline rotation support.
 };
 
 // All DRM Planes as map<Plane_id , plane_type_info> listed from highest to lowest priority
 typedef std::vector<std::pair<uint32_t, DRMPlaneTypeInfo>>  DRMPlanesInfo;
 
 enum struct DRMTopology {
-  UNKNOWN,  // To be compat with driver defs in sde_kms.h
+  UNKNOWN,  // To be compat with driver defs in sde_rm.h
   SINGLE_LM,
+  SINGLE_LM_DSC,
   DUAL_LM,
-  PPSPLIT,
+  DUAL_LM_DSC,
   DUAL_LM_MERGE,
+  DUAL_LM_MERGE_DSC,
+  DUAL_LM_DSCMERGE,
+  PPSPLIT,
 };
 
 enum struct DRMPanelMode {
@@ -358,6 +370,7 @@
   int wmin;
   int hmin;
   bool roi_merge;
+  DRMRotation panel_orientation;
 };
 
 /* Identifier token for a display */
diff --git a/libgralloc/Makefile.am b/libgralloc/Makefile.am
index d57ad32..3cf3960 100644
--- a/libgralloc/Makefile.am
+++ b/libgralloc/Makefile.am
@@ -1,11 +1,12 @@
+HEADER_PATH := ${WORKSPACE}/display/display-hal/include
 h_sources = alloc_controller.h \
             memalloc.h
 
 cpp_sources = ionalloc.cpp \
               alloc_controller.cpp
 
-library_includedir = $(pkgincludedir)
-library_include_HEADERS = $(h_sources)
+memalloc_includedir = $(pkgincludedir)
+memalloc_include_HEADERS = $(h_sources)
 
 lib_LTLIBRARIES = libmemalloc.la
 libmemalloc_la_CC = @CC@
@@ -17,7 +18,9 @@
 libmemalloc_la_LDFLAGS = -shared -avoid-version
 
 header_sources = gralloc_priv.h \
-                 gr.h
+                 gr.h \
+                 adreno_utils.h \
+                 $(HEADER_PATH)/color_metadata.h
 
 c_sources = gpu.cpp \
             gralloc.cpp \
@@ -36,4 +39,4 @@
 libgralloc_la_LIBADD += ../libqdutils/libqdMetaData.la
 libgralloc_la_LIBADD += -lhardware -lcutils -llog -lutils -lbinder
 libgralloc_la_LIBADD += libmemalloc.la
-libgralloc_la_LDFLAGS = -shared -avoid-version
\ No newline at end of file
+libgralloc_la_LDFLAGS = -shared -avoid-version
diff --git a/libgralloc/alloc_controller.cpp b/libgralloc/alloc_controller.cpp
index 1779312..d2a522e 100644
--- a/libgralloc/alloc_controller.cpp
+++ b/libgralloc/alloc_controller.cpp
@@ -247,6 +247,8 @@
                 aligned_w = ALIGN(width, alignment);
                 break;
             case HAL_PIXEL_FORMAT_RAW16:
+            case HAL_PIXEL_FORMAT_Y16:
+            case HAL_PIXEL_FORMAT_Y8:
                 aligned_w = ALIGN(width, 16);
                 break;
             case HAL_PIXEL_FORMAT_RAW12:
@@ -564,6 +566,7 @@
         case HAL_PIXEL_FORMAT_RGBA_5551:
         case HAL_PIXEL_FORMAT_RGBA_4444:
         case HAL_PIXEL_FORMAT_RAW16:
+        case HAL_PIXEL_FORMAT_Y16:
             size = alignedw * alignedh * 2;
             break;
         case HAL_PIXEL_FORMAT_RAW12:
@@ -573,6 +576,7 @@
             size = ALIGN(alignedw * alignedh, 4096);
             break;
         case HAL_PIXEL_FORMAT_RAW8:
+        case HAL_PIXEL_FORMAT_Y8:
             size = alignedw * alignedh;
             break;
             // adreno formats
@@ -815,9 +819,11 @@
         case HAL_PIXEL_FORMAT_YCrCb_420_SP_VENUS:
         case HAL_PIXEL_FORMAT_NV21_ZSL:
         case HAL_PIXEL_FORMAT_RAW16:
+        case HAL_PIXEL_FORMAT_Y16:
         case HAL_PIXEL_FORMAT_RAW12:
         case HAL_PIXEL_FORMAT_RAW10:
         case HAL_PIXEL_FORMAT_RAW8:
+        case HAL_PIXEL_FORMAT_Y8:
             getYuvSPPlaneInfo(hnd->base, width, height, 1, ycbcr);
             std::swap(ycbcr->cb, ycbcr->cr);
         break;
diff --git a/libgralloc1/Android.mk b/libgralloc1/Android.mk
index f4d4ba7..cdb651c 100644
--- a/libgralloc1/Android.mk
+++ b/libgralloc1/Android.mk
@@ -31,7 +31,7 @@
 LOCAL_MODULE_TAGS             := optional
 LOCAL_C_INCLUDES              := $(common_includes) $(kernel_includes)
 LOCAL_HEADER_LIBRARIES        := display_headers
-LOCAL_SHARED_LIBRARIES        := $(common_libs) libqdutils libqdMetaData libdl
+LOCAL_SHARED_LIBRARIES        := $(common_libs) libqdMetaData libdl
 LOCAL_CFLAGS                  := $(common_flags) -DLOG_TAG=\"grallocutils\" -Wno-sign-conversion
 LOCAL_ADDITIONAL_DEPENDENCIES := $(common_deps) $(kernel_deps)
 LOCAL_SRC_FILES               := gr_utils.cpp gr_adreno_info.cpp
diff --git a/libgralloc1/gr_buf_mgr.cpp b/libgralloc1/gr_buf_mgr.cpp
index 4c3f7d4..7e0ba14 100644
--- a/libgralloc1/gr_buf_mgr.cpp
+++ b/libgralloc1/gr_buf_mgr.cpp
@@ -211,11 +211,10 @@
     return GRALLOC1_ERROR_BAD_HANDLE;
   }
 
-  // TODO(user): delete handle once framework bug around this is confirmed
-  // to be resolved. This is tracked in bug 36355756
   private_handle_t * handle = const_cast<private_handle_t *>(hnd);
   handle->fd = -1;
   handle->fd_metadata = -1;
+  delete handle;
   return GRALLOC1_ERROR_NONE;
 }
 
@@ -278,6 +277,10 @@
 }
 
 gralloc1_error_t BufferManager::RetainBuffer(private_handle_t const *hnd) {
+  if (hnd->flags & private_handle_t::PRIV_FLAGS_CLIENT_ALLOCATED) {
+    return GRALLOC1_ERROR_NONE;
+  }
+
   ALOGD_IF(DEBUG, "Retain buffer handle:%p id: %" PRIu64, hnd, hnd->id);
   gralloc1_error_t err = GRALLOC1_ERROR_NONE;
   std::lock_guard<std::mutex> lock(buffer_lock_);
@@ -299,6 +302,10 @@
 }
 
 gralloc1_error_t BufferManager::ReleaseBuffer(private_handle_t const *hnd) {
+  if (hnd->flags & private_handle_t::PRIV_FLAGS_CLIENT_ALLOCATED) {
+    return GRALLOC1_ERROR_NONE;
+  }
+
   ALOGD_IF(DEBUG, "Release buffer handle:%p id: %" PRIu64, hnd, hnd->id);
   std::lock_guard<std::mutex> lock(buffer_lock_);
   auto buf = GetBufferFromHandleLocked(hnd);
@@ -327,16 +334,16 @@
     return GRALLOC1_ERROR_BAD_VALUE;
   }
 
-  if (hnd->base == 0) {
-    // we need to map for real
-    err = MapBuffer(hnd);
-  }
-
   auto buf = GetBufferFromHandleLocked(hnd);
   if (buf == nullptr) {
     return GRALLOC1_ERROR_BAD_HANDLE;
   }
 
+  if (hnd->base == 0) {
+    // we need to map for real
+    err = MapBuffer(hnd);
+  }
+
   // Invalidate if CPU reads in software and there are non-CPU
   // writers. No need to do this for the metadata buffer as it is
   // only read/written in software.
@@ -764,6 +771,17 @@
       AllocateBuffer(descriptor, hnd, size);
     } break;
 
+    case GRALLOC1_MODULE_PERFORM_GET_INTERLACE_FLAG: {
+      private_handle_t *hnd = va_arg(args, private_handle_t *);
+      int *flag = va_arg(args, int *);
+      if (private_handle_t::validate(hnd) != 0) {
+        return GRALLOC1_ERROR_BAD_HANDLE;
+      }
+      if (getMetaData(hnd, GET_PP_PARAM_INTERLACED, flag) != 0) {
+        *flag = 0;
+      }
+    } break;
+
     default:
       break;
   }
@@ -782,9 +800,11 @@
     case HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO:
     case HAL_PIXEL_FORMAT_NV21_ZSL:
     case HAL_PIXEL_FORMAT_RAW16:
+    case HAL_PIXEL_FORMAT_Y16:
     case HAL_PIXEL_FORMAT_RAW12:
     case HAL_PIXEL_FORMAT_RAW10:
     case HAL_PIXEL_FORMAT_YV12:
+    case HAL_PIXEL_FORMAT_Y8:
       return true;
     default:
       return false;
diff --git a/libgralloc1/gr_device_impl.cpp b/libgralloc1/gr_device_impl.cpp
index b955e19..ee90090 100644
--- a/libgralloc1/gr_device_impl.cpp
+++ b/libgralloc1/gr_device_impl.cpp
@@ -106,11 +106,12 @@
 void GrallocImpl::GetCapabilities(struct gralloc1_device *device, uint32_t *out_count,
                                   int32_t  /*gralloc1_capability_t*/ *out_capabilities) {
   if (device != nullptr) {
-    if (out_capabilities != nullptr && *out_count >= 2) {
+    if (out_capabilities != nullptr && *out_count >= 3) {
       out_capabilities[0] = GRALLOC1_CAPABILITY_TEST_ALLOCATE;
       out_capabilities[1] = GRALLOC1_CAPABILITY_LAYERED_BUFFERS;
+      out_capabilities[2] = GRALLOC1_CAPABILITY_RELEASE_IMPLY_DELETE;
     }
-    *out_count = 2;
+    *out_count = 3;
   }
   return;
 }
diff --git a/libgralloc1/gr_utils.cpp b/libgralloc1/gr_utils.cpp
index 28fe4f6..b3056e1 100644
--- a/libgralloc1/gr_utils.cpp
+++ b/libgralloc1/gr_utils.cpp
@@ -201,6 +201,7 @@
   // Below switch should be for only YUV/custom formats
   switch (format) {
     case HAL_PIXEL_FORMAT_RAW16:
+    case HAL_PIXEL_FORMAT_Y16:
       size = alignedw * alignedh * 2;
       break;
     case HAL_PIXEL_FORMAT_RAW10:
@@ -208,6 +209,7 @@
       size = ALIGN(alignedw * alignedh, SIZE_4K);
       break;
     case HAL_PIXEL_FORMAT_RAW8:
+    case HAL_PIXEL_FORMAT_Y8:
       size = alignedw * alignedh * 1;
       break;
 
@@ -309,6 +311,28 @@
   ycbcr->cstride = VENUS_UV_STRIDE(color_format, INT(width));
 }
 
+void GetYuvUbwcInterlacedSPPlaneInfo(uint64_t base, uint32_t width, uint32_t height,
+                                     int color_format, struct android_ycbcr *ycbcr) {
+  unsigned int uv_stride, uv_height, uv_size;
+  unsigned int alignment = 4096;
+  uint64_t field_base;
+
+  // UBWC interlaced has top-bottom field layout with each field as
+  // 4-plane NV12_UBWC with width = image_width & height = image_height / 2.
+  // Client passed ycbcr argument is ptr to struct android_ycbcr[2].
+  // Plane info to be filled for each field separately.
+  height = (height + 1) >> 1;
+  uv_stride = VENUS_UV_STRIDE(color_format, INT(width));
+  uv_height = VENUS_UV_SCANLINES(color_format, INT(height));
+  uv_size = ALIGN((uv_stride * uv_height), alignment);
+
+  field_base = base;
+  GetYuvUbwcSPPlaneInfo(field_base, width, height, COLOR_FMT_NV12_UBWC, &ycbcr[0]);
+
+  field_base = reinterpret_cast<uint64_t>(ycbcr[0].cb) + uv_size;
+  GetYuvUbwcSPPlaneInfo(field_base, width, height, COLOR_FMT_NV12_UBWC, &ycbcr[1]);
+}
+
 void GetYuvSPPlaneInfo(uint64_t base, uint32_t width, uint32_t height, uint32_t bpp,
                        struct android_ycbcr *ycbcr) {
   unsigned int ystride, cstride;
@@ -330,6 +354,7 @@
   gralloc1_producer_usage_t prod_usage = hnd->GetProducerUsage();
   gralloc1_consumer_usage_t cons_usage = hnd->GetConsumerUsage();
   unsigned int ystride, cstride;
+  bool interlaced = false;
 
   memset(ycbcr->reserved, 0, sizeof(ycbcr->reserved));
   MetaData_t *metadata = reinterpret_cast<MetaData_t *>(hnd->base_metadata);
@@ -352,6 +377,11 @@
     GetAlignedWidthAndHeight(info, &width, &height);
   }
 
+  // Check metadata for interlaced content.
+  if (metadata && (metadata->operation & PP_PARAM_INTERLACED)) {
+    interlaced = metadata->interlaced ? true : false;
+  }
+
   // Get the chroma offsets from the handle width/height. We take advantage
   // of the fact the width _is_ the stride
   switch (format) {
@@ -369,7 +399,11 @@
       break;
 
     case HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS_UBWC:
-      GetYuvUbwcSPPlaneInfo(hnd->base, width, height, COLOR_FMT_NV12_UBWC, ycbcr);
+      if (!interlaced) {
+        GetYuvUbwcSPPlaneInfo(hnd->base, width, height, COLOR_FMT_NV12_UBWC, ycbcr);
+      } else {
+        GetYuvUbwcInterlacedSPPlaneInfo(hnd->base, width, height, COLOR_FMT_NV12_UBWC, ycbcr);
+      }
       ycbcr->chroma_step = 2;
       break;
 
@@ -389,8 +423,10 @@
     case HAL_PIXEL_FORMAT_YCrCb_420_SP_VENUS:
     case HAL_PIXEL_FORMAT_NV21_ZSL:
     case HAL_PIXEL_FORMAT_RAW16:
+    case HAL_PIXEL_FORMAT_Y16:
     case HAL_PIXEL_FORMAT_RAW10:
     case HAL_PIXEL_FORMAT_RAW8:
+    case HAL_PIXEL_FORMAT_Y8:
       GetYuvSPPlaneInfo(hnd->base, width, height, 1, ycbcr);
       std::swap(ycbcr->cb, ycbcr->cr);
       break;
@@ -669,6 +705,8 @@
       aligned_w = ALIGN(width, alignment);
       break;
     case HAL_PIXEL_FORMAT_RAW16:
+    case HAL_PIXEL_FORMAT_Y16:
+    case HAL_PIXEL_FORMAT_Y8:
       aligned_w = ALIGN(width, 16);
       break;
     case HAL_PIXEL_FORMAT_RAW12:
diff --git a/libgralloc1/gr_utils.h b/libgralloc1/gr_utils.h
index aa66fd0..2a08539 100644
--- a/libgralloc1/gr_utils.h
+++ b/libgralloc1/gr_utils.h
@@ -83,6 +83,8 @@
                        struct android_ycbcr *ycbcr);
 void GetYuvUbwcSPPlaneInfo(uint64_t base, uint32_t width, uint32_t height, int color_format,
                            struct android_ycbcr *ycbcr);
+void GetYuvUbwcInterlacedSPPlaneInfo(uint64_t base, uint32_t width, uint32_t height,
+                                     int color_format, struct android_ycbcr *ycbcr);
 void GetRgbUBwcBlockSize(uint32_t bpp, int *block_width, int *block_height);
 unsigned int GetRgbUBwcMetaBufferSize(int width, int height, uint32_t bpp);
 unsigned int GetUBwcSize(int width, int height, int format, unsigned int alignedw,
diff --git a/libgralloc1/gralloc_priv.h b/libgralloc1/gralloc_priv.h
index ba156b6..1839d2f 100644
--- a/libgralloc1/gralloc_priv.h
+++ b/libgralloc1/gralloc_priv.h
@@ -97,6 +97,7 @@
 #define GRALLOC_MODULE_PERFORM_SET_SINGLE_BUFFER_MODE 13
 #define GRALLOC1_MODULE_PERFORM_GET_BUFFER_SIZE_AND_DIMENSIONS 14
 #define GRALLOC1_MODULE_PERFORM_ALLOCATE_BUFFER 15
+#define GRALLOC1_MODULE_PERFORM_GET_INTERLACE_FLAG 16
 
 // OEM specific HAL formats
 #define HAL_PIXEL_FORMAT_RGBA_5551 6
diff --git a/libqdutils/Android.mk b/libqdutils/Android.mk
index 7afd00f..60efb3a 100644
--- a/libqdutils/Android.mk
+++ b/libqdutils/Android.mk
@@ -20,12 +20,12 @@
 include $(CLEAR_VARS)
 
 LOCAL_COPY_HEADERS_TO           := $(common_header_export_path)
-LOCAL_COPY_HEADERS              := qdMetaData.h
+LOCAL_COPY_HEADERS              := qdMetaData.h qd_utils.h
 LOCAL_SHARED_LIBRARIES          := liblog libcutils
 LOCAL_C_INCLUDES                := $(common_includes)
 LOCAL_HEADER_LIBRARIES          := display_headers
 LOCAL_ADDITIONAL_DEPENDENCIES   := $(common_deps)
-LOCAL_SRC_FILES                 := qdMetaData.cpp
+LOCAL_SRC_FILES                 := qdMetaData.cpp qd_utils.cpp
 LOCAL_CFLAGS                    := $(common_flags) -Wno-sign-conversion
 LOCAL_CFLAGS                    += -DLOG_TAG=\"DisplayMetaData\"
 
@@ -34,3 +34,18 @@
 LOCAL_VENDOR_MODULE             := true
 include $(BUILD_SHARED_LIBRARY)
 
+
+include $(CLEAR_VARS)
+
+LOCAL_SHARED_LIBRARIES          := liblog libcutils
+LOCAL_C_INCLUDES                := $(common_includes)
+LOCAL_HEADER_LIBRARIES          := display_headers
+LOCAL_ADDITIONAL_DEPENDENCIES   := $(common_deps)
+LOCAL_SRC_FILES                 := qdMetaData.cpp qd_utils.cpp
+LOCAL_CFLAGS                    := $(common_flags) -Wno-sign-conversion
+LOCAL_CFLAGS                    += -DLOG_TAG=\"DisplayMetaData\"
+
+LOCAL_MODULE_TAGS               := optional
+LOCAL_MODULE                    := libqdMetaData.system
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/sdm/include/core/sdm_types.h b/sdm/include/core/sdm_types.h
index fae1153..ce356bf 100644
--- a/sdm/include/core/sdm_types.h
+++ b/sdm/include/core/sdm_types.h
@@ -55,6 +55,7 @@
   kErrorShutDown,         //!< Driver is processing shutdown sequence
   kErrorPerfValidation,   //!< Bandwidth or Clock requirement validation failure.
   kErrorNoAppLayers,      //!< No App layer(s) in the draw cycle.
+  kErrorRotatorValidation,  //!< Rotator configuration validation failure.
 };
 
 /*! @brief This structure is defined for client and library compatibility check purpose only. This
diff --git a/sdm/include/private/color_params.h b/sdm/include/private/color_params.h
index 0a53832..08895ee 100644
--- a/sdm/include/private/color_params.h
+++ b/sdm/include/private/color_params.h
@@ -578,9 +578,13 @@
   // from ColorManager, containing all physical features to be programmed and also compute
   // metadata/populate into T.
   inline DisplayError AddFeature(uint32_t feature_id, PPFeatureInfo *feature) {
-    if (feature_id < kMaxNumPPFeatures)
+    if (feature_id < kMaxNumPPFeatures) {
+      if (feature_[feature_id]) {
+        delete feature_[feature_id];
+        feature_[feature_id] = NULL;
+      }
       feature_[feature_id] = feature;
-
+    }
     return kErrorNone;
   }
 
diff --git a/sdm/include/private/hw_info_types.h b/sdm/include/private/hw_info_types.h
index cdfec2e..64e8454 100644
--- a/sdm/include/private/hw_info_types.h
+++ b/sdm/include/private/hw_info_types.h
@@ -205,6 +205,7 @@
   uint32_t undersized_prefill_lines = 0;
   CompRatioMap comp_ratio_rt_map;
   CompRatioMap comp_ratio_nrt_map;
+  uint32_t cache_size = 0;  // cache size in bytes
 
   void Reset() { *this = HWResourceInfo(); }
 };
@@ -321,6 +322,12 @@
   }
 };
 
+enum HWRotatorMode {
+  kRotatorNone,
+  kRotatorOffline,
+  kRotatorInline
+};
+
 struct HWRotateInfo {
   int pipe_id = -1;  // Not actual pipe id, but the relative DMA id
   int writeback_id = -1;  // Writeback block id, but this is the same as DMA id
@@ -342,6 +349,7 @@
   float input_compression = 1.0f;
   float output_compression = 1.0f;
   bool is_buffer_cached = false;
+  HWRotatorMode mode = kRotatorNone;
 };
 
 struct HWScaleLutInfo {
diff --git a/sdm/include/utils/formats.h b/sdm/include/utils/formats.h
index dd819dc..2d43850 100644
--- a/sdm/include/utils/formats.h
+++ b/sdm/include/utils/formats.h
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+* Copyright (c) 2016 - 2017, 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
@@ -34,10 +34,28 @@
 
 namespace sdm {
 
+struct FormatTileSize {
+  /*< Tile width in pixels. For YUV formats this will give only the
+      tile width for Y plane*/
+  uint32_t tile_width = 0;
+  /*< Tile height in pixels. For YUV formats this will give only the
+      tile height for Y plane*/
+  uint32_t tile_height = 0;
+
+  /*< Tile width in pixels. Only valid for YUV formats where this will
+      give tile width for UV plane*/
+  uint32_t uv_tile_width = 0;
+  /*< Tile height in pixels. Only valid for YUV formats where this will
+       give tile height for UV plane*/
+  uint32_t uv_tile_height = 0;
+};
+
 bool IsUBWCFormat(LayerBufferFormat format);
 bool Is10BitFormat(LayerBufferFormat format);
 const char *GetFormatString(const LayerBufferFormat &format);
 BufferLayout GetBufferLayout(LayerBufferFormat format);
+DisplayError GetBufferFormatTileSize(LayerBufferFormat format, FormatTileSize *tile_size);
+float GetBufferFormatBpp(LayerBufferFormat format);
 
 }  // namespace sdm
 
diff --git a/sdm/include/utils/rect.h b/sdm/include/utils/rect.h
index ea6edfb..303fc9f 100644
--- a/sdm/include/utils/rect.h
+++ b/sdm/include/utils/rect.h
@@ -60,6 +60,11 @@
   void TransformHV(const LayerRect &src_domain, const LayerRect &in_rect,
                    const LayerTransform &transform, LayerRect *out_rect);
   RectOrientation GetOrientation(const LayerRect &in_rect);
+  DisplayError GetCropAndDestination(const LayerRect &crop, const LayerRect &dst,
+                                     bool rotate90, float *crop_width, float *crop_height,
+                                     float *dst_width, float *dst_height);
+  DisplayError GetScaleFactor(const LayerRect &crop, const LayerRect &dst, bool rotate90,
+                              float *scale_x, float *scale_y);
 }  // namespace sdm
 
 #endif  // __RECT_H__
diff --git a/sdm/include/utils/sync_task.h b/sdm/include/utils/sync_task.h
new file mode 100644
index 0000000..725460a
--- /dev/null
+++ b/sdm/include/utils/sync_task.h
@@ -0,0 +1,143 @@
+/*
+* Copyright (c) 2017, 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 __SYNC_TASK_H__
+#define __SYNC_TASK_H__
+
+#include <thread>
+#include <mutex>
+#include <condition_variable>   // NOLINT
+
+namespace sdm {
+
+template <class TaskCode>
+class SyncTask {
+ public:
+  // This class need to be overridden by caller to pass on a task context.
+  class TaskContext {
+   public:
+    virtual ~TaskContext() { }
+  };
+
+  // Methods to callback into caller for command codes executions in worker thread.
+  class TaskHandler {
+   public:
+    virtual ~TaskHandler() { }
+    virtual void OnTask(const TaskCode &task_code, TaskContext *task_context) = 0;
+  };
+
+  explicit SyncTask(TaskHandler &task_handler) : task_handler_(task_handler) {
+    // Block caller thread until worker thread has started and ready to listen to task commands.
+    // Worker thread will signal as soon as callback is received in the new thread.
+    std::unique_lock<std::mutex> caller_lock(caller_mutex_);
+    std::thread worker_thread(SyncTaskThread, this);
+    worker_thread_.swap(worker_thread);
+    caller_cv_.wait(caller_lock);
+  }
+
+  ~SyncTask() {
+    // Task code does not matter here.
+    PerformTask(task_code_, nullptr, true);
+    worker_thread_.join();
+  }
+
+  void PerformTask(const TaskCode &task_code, TaskContext *task_context) {
+    PerformTask(task_code, task_context, false);
+  }
+
+ private:
+  void PerformTask(const TaskCode &task_code, TaskContext *task_context, bool terminate) {
+    std::unique_lock<std::mutex> caller_lock(caller_mutex_);
+
+    // New scope to limit scope of worker lock to this block.
+    {
+      // Set task command code and notify worker thread.
+      std::unique_lock<std::mutex> worker_lock(worker_mutex_);
+      task_code_ = task_code;
+      task_context_ = task_context;
+      worker_thread_exit_ = terminate;
+      pending_code_ = true;
+      worker_cv_.notify_one();
+    }
+
+    // Wait for worker thread to finish and signal.
+    caller_cv_.wait(caller_lock);
+  }
+
+  static void SyncTaskThread(SyncTask *sync_task) {
+    if (sync_task) {
+      sync_task->OnThreadCallback();
+    }
+  }
+
+  void OnThreadCallback() {
+    // Acquire worker lock and start waiting for events.
+    // Wait must start before caller thread can post events, otherwise posted events will be lost.
+    // Caller thread will be blocked until worker thread signals readiness.
+    std::unique_lock<std::mutex> worker_lock(worker_mutex_);
+
+    // New scope to limit scope of caller lock to this block.
+    {
+      // Signal caller thread that worker thread is ready to listen to events.
+      std::unique_lock<std::mutex> caller_lock(caller_mutex_);
+      caller_cv_.notify_one();
+    }
+
+    while (!worker_thread_exit_) {
+      // Add predicate to handle spurious interrupts.
+      // Wait for caller thread to signal new command codes.
+      worker_cv_.wait(worker_lock, [this] { return pending_code_; });
+
+      // Call task handler which is implemented by the caller.
+      if (!worker_thread_exit_) {
+        task_handler_.OnTask(task_code_, task_context_);
+      }
+
+      pending_code_ = false;
+      // Notify completion of current task to the caller thread which is blocked.
+      std::unique_lock<std::mutex> caller_lock(caller_mutex_);
+      caller_cv_.notify_one();
+    }
+  }
+
+  TaskHandler &task_handler_;
+  TaskCode task_code_;
+  TaskContext *task_context_ = nullptr;
+  std::thread worker_thread_;
+  std::mutex caller_mutex_;
+  std::mutex worker_mutex_;
+  std::condition_variable caller_cv_;
+  std::condition_variable worker_cv_;
+  bool worker_thread_exit_ = false;
+  bool pending_code_ = false;
+};
+
+}  // namespace sdm
+
+#endif  // __SYNC_TASK_H__
diff --git a/sdm/libs/core/Android.mk b/sdm/libs/core/Android.mk
index 1d55d96..c5002a9 100644
--- a/sdm/libs/core/Android.mk
+++ b/sdm/libs/core/Android.mk
@@ -7,7 +7,7 @@
 LOCAL_MODULE_TAGS             := optional
 LOCAL_C_INCLUDES              := $(common_includes) $(kernel_includes)
 LOCAL_HEADER_LIBRARIES        := display_headers
-LOCAL_CFLAGS                  := -Wno-unused-parameter -DLOG_TAG=\"SDM\" \
+LOCAL_CFLAGS                  := -fno-operator-names -Wno-unused-parameter -DLOG_TAG=\"SDM\" \
                                  $(common_flags)
 LOCAL_HW_INTF_PATH_1          := fb
 LOCAL_SHARED_LIBRARIES        := libdl libsdmutils
@@ -51,6 +51,7 @@
                                  $(LOCAL_HW_INTF_PATH_2)/hw_device_drm.cpp \
                                  $(LOCAL_HW_INTF_PATH_2)/hw_events_drm.cpp \
                                  $(LOCAL_HW_INTF_PATH_2)/hw_scale_drm.cpp \
+                                 $(LOCAL_HW_INTF_PATH_2)/hw_virtual_drm.cpp \
                                  $(LOCAL_HW_INTF_PATH_2)/hw_color_manager_drm.cpp
 endif
 
diff --git a/sdm/libs/core/display_base.cpp b/sdm/libs/core/display_base.cpp
index 618dd2e..f6540cd 100644
--- a/sdm/libs/core/display_base.cpp
+++ b/sdm/libs/core/display_base.cpp
@@ -590,21 +590,24 @@
     char idx[8] = { 0 };
     const char *comp_type = GetName(sdm_layer->composition);
     const char *buffer_format = GetFormatString(input_buffer->format);
-    const char *rotate_split[2] = { "Rot-1", "Rot-2" };
     const char *comp_split[2] = { "Comp-1", "Comp-2" };
 
     snprintf(idx, sizeof(idx), "%d", layer_index);
 
     for (uint32_t count = 0; count < hw_rotator_session.hw_block_count; count++) {
-      char writeback_id[8] = { 0 };
+      char writeback_id[8] = { "I" };
       HWRotateInfo &rotate = hw_rotator_session.hw_rotate_info[count];
       LayerRect &src_roi = rotate.src_roi;
       LayerRect &dst_roi = rotate.dst_roi;
+      const char *rotate_split[2] = { "Rot-1", "Rot-2" };
+      int pipe_id = 0;
 
-      snprintf(writeback_id, sizeof(writeback_id), "%d", rotate.writeback_id);
-
+      if (hw_rotator_session.mode == kRotatorOffline) {
+        snprintf(writeback_id, sizeof(writeback_id), "%d", rotate.writeback_id);
+        pipe_id = rotate.pipe_id;
+      }
       DumpImpl::AppendString(buffer, length, format, idx, comp_type, rotate_split[count],
-                             writeback_id, rotate.pipe_id, input_buffer->width,
+                             writeback_id, pipe_id, input_buffer->width,
                              input_buffer->height, buffer_format, INT(src_roi.left),
                              INT(src_roi.top), INT(src_roi.right), INT(src_roi.bottom),
                              INT(dst_roi.left), INT(dst_roi.top), INT(dst_roi.right),
@@ -633,8 +636,11 @@
         continue;
       }
 
-      LayerRect &src_roi = pipe.src_roi;
+      LayerRect src_roi = pipe.src_roi;
       LayerRect &dst_roi = pipe.dst_roi;
+      if (hw_rotator_session.mode == kRotatorInline) {
+        src_roi = hw_rotator_session.hw_rotate_info[count].dst_roi;
+      }
 
       snprintf(z_order, sizeof(z_order), "%d", pipe.z_order);
       snprintf(flags, sizeof(flags), "0x%08x", hw_layer.flags.flags);
diff --git a/sdm/libs/core/drm/hw_device_drm.cpp b/sdm/libs/core/drm/hw_device_drm.cpp
index 79d09bd..a6ed929 100644
--- a/sdm/libs/core/drm/hw_device_drm.cpp
+++ b/sdm/libs/core/drm/hw_device_drm.cpp
@@ -50,6 +50,7 @@
 #include <utils/sys.h>
 #include <drm/sde_drm.h>
 #include <private/color_params.h>
+#include <utils/rect.h>
 
 #include <algorithm>
 #include <string>
@@ -216,14 +217,6 @@
 }
 
 void HWDeviceDRM::Registry::RegisterCurrent(HWLayers *hw_layers) {
-  DRMMaster *master = nullptr;
-  DRMMaster::GetInstance(&master);
-
-  if (!master) {
-    DLOGE("Failed to acquire DRM Master instance");
-    return;
-  }
-
   HWLayersInfo &hw_layer_info = hw_layers->info;
   uint32_t hw_layer_count = UINT32(hw_layer_info.hw_layers.size());
 
@@ -233,32 +226,45 @@
     HWRotatorSession *hw_rotator_session = &hw_layers->config[i].hw_rotator_session;
     HWRotateInfo *hw_rotate_info = &hw_rotator_session->hw_rotate_info[0];
 
-    if (hw_rotate_info->valid) {
+    if (hw_rotator_session->mode == kRotatorOffline && hw_rotate_info->valid) {
       input_buffer = &hw_rotator_session->output_buffer;
     }
 
-    int fd = input_buffer->planes[0].fd;
-    if (fd >= 0 && hashmap_[current_index_].find(fd) == hashmap_[current_index_].end()) {
-      AllocatedBufferInfo buf_info {};
-      DRMBuffer layout {};
-      buf_info.fd = layout.fd = fd;
-      buf_info.aligned_width = layout.width = input_buffer->width;
-      buf_info.aligned_height = layout.height = input_buffer->height;
-      buf_info.format = input_buffer->format;
-      GetDRMFormat(buf_info.format, &layout.drm_format, &layout.drm_format_modifier);
-      buffer_allocator_->GetBufferLayout(buf_info, layout.stride, layout.offset,
-                                         &layout.num_planes);
-      uint32_t fb_id = 0;
-      int ret = master->CreateFbId(layout, &fb_id);
-      if (ret < 0) {
-        DLOGE("CreateFbId failed. width %d, height %d, format: %s, stride %u, error %d",
-              layout.width, layout.height, GetFormatString(buf_info.format), layout.stride[0],
-              errno);
-      } else {
-        hashmap_[current_index_][fd] = fb_id;
-      }
+    MapBufferToFbId(input_buffer);
+  }
+}
+
+void HWDeviceDRM::Registry::MapBufferToFbId(LayerBuffer* buffer) {
+  int fd = buffer->planes[0].fd;
+  DRMMaster *master = nullptr;
+  DRMMaster::GetInstance(&master);
+
+  if (!master) {
+    DLOGE("Failed to acquire DRM Master instance");
+    return;
+  }
+
+  if (fd >= 0 && hashmap_[current_index_].find(fd) == hashmap_[current_index_].end()) {
+    AllocatedBufferInfo buf_info{};
+    DRMBuffer layout{};
+    buf_info.fd = layout.fd = fd;
+    buf_info.aligned_width = layout.width = buffer->width;
+    buf_info.aligned_height = layout.height = buffer->height;
+    buf_info.format = buffer->format;
+    GetDRMFormat(buf_info.format, &layout.drm_format, &layout.drm_format_modifier);
+    buffer_allocator_->GetBufferLayout(buf_info, layout.stride, layout.offset,
+        &layout.num_planes);
+    uint32_t fb_id = 0;
+    int ret = master->CreateFbId(layout, &fb_id);
+    if (ret < 0) {
+      DLOGE("CreateFbId failed. width %d, height %d, format: %s, stride %u, error %d",
+          layout.width, layout.height, GetFormatString(buf_info.format), layout.stride[0],
+          errno);
+    } else {
+      hashmap_[current_index_][fd] = fb_id;
     }
   }
+  return;
 }
 
 void HWDeviceDRM::Registry::UnregisterNext() {
@@ -300,6 +306,7 @@
     : hw_info_intf_(hw_info_intf), buffer_sync_handler_(buffer_sync_handler),
       registry_(buffer_allocator) {
   device_type_ = kDevicePrimary;
+  disp_type_ = DRMDisplayType::PERIPHERAL;
   device_name_ = "Peripheral Display";
   hw_info_intf_ = hw_info_intf;
 }
@@ -309,15 +316,13 @@
 
   if (!default_mode_) {
     DRMMaster *drm_master = {};
-    int dev_fd = -1;
     DRMMaster::GetInstance(&drm_master);
-    drm_master->GetHandle(&dev_fd);
-    DRMLibLoader::GetInstance()->FuncGetDRMManager()(dev_fd, &drm_mgr_intf_);
-    if (drm_mgr_intf_->RegisterDisplay(DRMDisplayType::PERIPHERAL, &token_)) {
-      DLOGE("RegisterDisplay failed");
+    drm_master->GetHandle(&dev_fd_);
+    DRMLibLoader::GetInstance()->FuncGetDRMManager()(dev_fd_, &drm_mgr_intf_);
+    if (drm_mgr_intf_->RegisterDisplay(disp_type_, &token_)) {
+      DLOGE("RegisterDisplay failed for display %d", disp_type_);
       return kErrorResources;
     }
-
     drm_mgr_intf_->CreateAtomicReq(token_, &drm_atomic_intf_);
     drm_mgr_intf_->GetConnectorInfo(token_.conn_id, &connector_info_);
     InitializeConfigs();
@@ -325,17 +330,18 @@
 
     drm_atomic_intf_->Perform(DRMOps::CRTC_SET_ACTIVE, token_.crtc_id, 1);
     // Commit to setup pipeline with mode, which then tells us the topology etc
-    if (drm_atomic_intf_->Commit(true /* synchronous */)) {
-      DLOGE("Setting up CRTC %d, Connector %d for %s failed", token_.crtc_id, token_.conn_id,
-            device_name_);
-      return kErrorResources;
+
+    if (!deferred_initialize_) {
+      if (drm_atomic_intf_->Commit(true /* synchronous */)) {
+        DRM_LOGI("Setting up CRTC %d, Connector %d for %s failed", token_.crtc_id,
+          token_.conn_id, device_name_);
+        return kErrorResources;
+      }
+      // Reload connector info for updated info after 1st commit
+
+      drm_mgr_intf_->GetConnectorInfo(token_.conn_id, &connector_info_);
     }
-
-    // Reload connector info for updated info after 1st commit
-    drm_mgr_intf_->GetConnectorInfo(token_.conn_id, &connector_info_);
-    DLOGI("Setup CRTC %d, Connector %d for %s", token_.crtc_id, token_.conn_id, device_name_);
   }
-
   PopulateDisplayAttributes();
   PopulateHWPanelInfo();
   UpdateMixerAttributes();
@@ -409,7 +415,9 @@
   display_attributes_.h_total = mode.htotal;
   uint32_t h_blanking = mode.htotal - mode.hdisplay;
   display_attributes_.is_device_split =
-      (topology == DRMTopology::DUAL_LM || topology == DRMTopology::DUAL_LM_MERGE);
+      (topology == DRMTopology::DUAL_LM || topology == DRMTopology::DUAL_LM_MERGE ||
+       topology == DRMTopology::DUAL_LM_MERGE_DSC || topology == DRMTopology::DUAL_LM_DSC ||
+       topology == DRMTopology::DUAL_LM_DSCMERGE);
   display_attributes_.h_total += display_attributes_.is_device_split ? h_blanking : 0;
 
   display_attributes_.x_dpi = (FLOAT(mode.hdisplay) * 25.4f) / FLOAT(mm_width);
@@ -445,6 +453,15 @@
   hw_panel_info_.is_primary_panel = connector_info_.is_primary;
   hw_panel_info_.is_pluggable = 0;
 
+  // no supprt for 90 rotation only flips or 180 supported
+  hw_panel_info_.panel_orientation.rotation = 0;
+  hw_panel_info_.panel_orientation.flip_horizontal =
+    (connector_info_.panel_orientation == DRMRotation::FLIP_H) ||
+    (connector_info_.panel_orientation == DRMRotation::ROT_180);
+  hw_panel_info_.panel_orientation.flip_vertical =
+    (connector_info_.panel_orientation == DRMRotation::FLIP_V) ||
+    (connector_info_.panel_orientation == DRMRotation::ROT_180);
+
   GetHWDisplayPortAndMode();
   GetHWPanelMaxBrightness();
 
@@ -560,18 +577,19 @@
 
 DisplayError HWDeviceDRM::PowerOn() {
   DTRACE_SCOPED();
-/*
   drm_atomic_intf_->Perform(DRMOps::CRTC_SET_ACTIVE, token_.crtc_id, 1);
   drm_atomic_intf_->Perform(DRMOps::CONNECTOR_SET_POWER_MODE, token_.conn_id, DRMPowerMode::ON);
-*/
+  int ret = drm_atomic_intf_->Commit(false /* synchronous */);
+  if (ret) {
+    DLOGE("%s failed with error %d", __FUNCTION__, ret);
+    return kErrorHardware;
+  }
   return kErrorNone;
 }
 
 DisplayError HWDeviceDRM::PowerOff() {
-/*
   drm_atomic_intf_->Perform(DRMOps::CONNECTOR_SET_POWER_MODE, token_.conn_id, DRMPowerMode::OFF);
   drm_atomic_intf_->Perform(DRMOps::CRTC_SET_ACTIVE, token_.crtc_id, 0);
-*/
   int ret = drm_atomic_intf_->Commit(false /* synchronous */);
   if (ret) {
     DLOGE("%s failed with error %d", __FUNCTION__, ret);
@@ -637,15 +655,13 @@
     HWPipeInfo *left_pipe = &hw_layers->config[i].left_pipe;
     HWPipeInfo *right_pipe = &hw_layers->config[i].right_pipe;
     HWRotatorSession *hw_rotator_session = &hw_layers->config[i].hw_rotator_session;
-    bool needs_rotation = false;
 
     for (uint32_t count = 0; count < 2; count++) {
       HWPipeInfo *pipe_info = (count == 0) ? left_pipe : right_pipe;
       HWRotateInfo *hw_rotate_info = &hw_rotator_session->hw_rotate_info[count];
 
-      if (hw_rotate_info->valid) {
+      if (hw_rotator_session->mode == kRotatorOffline && hw_rotate_info->valid) {
         input_buffer = &hw_rotator_session->output_buffer;
-        needs_rotation = true;
       }
 
       uint32_t fb_id = registry_.GetFbId(input_buffer->planes[0].fd);
@@ -659,21 +675,17 @@
         DRMRect src = {};
         SetRect(pipe_info->src_roi, &src);
         drm_atomic_intf_->Perform(DRMOps::PLANE_SET_SRC_RECT, pipe_id, src);
+        DRMRect rot_dst = {0, 0, 0, 0};
+        if (hw_rotator_session->mode == kRotatorInline && hw_rotate_info->valid) {
+          SetRect(hw_rotate_info->dst_roi, &rot_dst);
+          drm_atomic_intf_->Perform(DRMOps::PLANE_SET_ROTATION_DST_RECT, pipe_id, rot_dst);
+        }
         DRMRect dst = {};
         SetRect(pipe_info->dst_roi, &dst);
         drm_atomic_intf_->Perform(DRMOps::PLANE_SET_DST_RECT, pipe_id, dst);
 
         uint32_t rot_bit_mask = 0;
-        // In case of rotation, rotator handles flips
-        if (!needs_rotation) {
-          if (layer.transform.flip_horizontal) {
-            rot_bit_mask |= UINT32(DRMRotation::FLIP_H);
-          }
-          if (layer.transform.flip_vertical) {
-            rot_bit_mask |= UINT32(DRMRotation::FLIP_V);
-          }
-        }
-
+        SetRotation(layer.transform, hw_rotator_session->mode, &rot_bit_mask);
         drm_atomic_intf_->Perform(DRMOps::PLANE_SET_ROTATION, pipe_id, rot_bit_mask);
         drm_atomic_intf_->Perform(DRMOps::PLANE_SET_H_DECIMATION, pipe_id,
                                   pipe_info->horizontal_decimation);
@@ -796,7 +808,7 @@
 
   int ret = drm_atomic_intf_->Commit(false /* synchronous */);
   if (ret) {
-    DLOGE("%s failed with error %d", __FUNCTION__, ret);
+    DLOGE("%s failed with error %d crtc %d", __FUNCTION__, ret, token_.crtc_id);
     return kErrorHardware;
   }
 
@@ -813,7 +825,7 @@
   for (uint32_t i = 0; i < hw_layer_info.hw_layers.size(); i++) {
     Layer &layer = hw_layer_info.hw_layers.at(i);
     HWRotatorSession *hw_rotator_session = &hw_layers->config[i].hw_rotator_session;
-    if (hw_rotator_session->hw_block_count) {
+    if (hw_rotator_session->mode == kRotatorOffline) {
       hw_rotator_session->output_buffer.release_fence_fd = Sys::dup_(release_fence);
     } else {
       layer.input_buffer.release_fence_fd = Sys::dup_(release_fence);
@@ -859,6 +871,36 @@
   target->bottom = UINT32(source.bottom);
 }
 
+void HWDeviceDRM::SetRotation(LayerTransform transform, const HWRotatorMode &mode,
+                              uint32_t* rot_bit_mask) {
+  // In offline rotation case, rotator will handle flips set via offline rotator interface.
+  if (mode == kRotatorOffline) {
+    *rot_bit_mask = 0;
+    return;
+  }
+
+  // In no rotation case or inline rotation case, plane will handle flips
+  // In DRM framework rotation is applied in counter-clockwise direction.
+  if (transform.rotation == 90) {
+    // a) rotate 90 clockwise = rotate 270 counter-clockwise in DRM
+    // rotate 270 is translated as hflip + vflip + rotate90
+    // b) rotate 270 clockwise = rotate 90 counter-clockwise in DRM
+    // c) hflip + rotate 90 clockwise = vflip + rotate 90 counter-clockwise in DRM
+    // d) vflip + rotate 90 clockwise = hflip + rotate 90 counter-clockwise in DRM
+    *rot_bit_mask = UINT32(DRMRotation::ROT_90);
+    transform.flip_horizontal = !transform.flip_horizontal;
+    transform.flip_vertical = !transform.flip_vertical;
+  }
+
+  if (transform.flip_horizontal) {
+    *rot_bit_mask |= UINT32(DRMRotation::FLIP_H);
+  }
+
+  if (transform.flip_vertical) {
+    *rot_bit_mask |= UINT32(DRMRotation::FLIP_V);
+  }
+}
+
 bool HWDeviceDRM::EnableHotPlugDetection(int enable) {
   return true;
 }
@@ -872,7 +914,6 @@
 
 DisplayError HWDeviceDRM::GetPPFeaturesVersion(PPFeatureVersion *vers) {
   struct DRMPPFeatureInfo info = {};
-
   for (uint32_t i = 0; i < kMaxNumPPFeatures; i++) {
     memset(&info, 0, sizeof(struct DRMPPFeatureInfo));
     info.id = HWColorManagerDrm::ToDrmFeatureId(i);
diff --git a/sdm/libs/core/drm/hw_device_drm.h b/sdm/libs/core/drm/hw_device_drm.h
index d318786..2199426 100644
--- a/sdm/libs/core/drm/hw_device_drm.h
+++ b/sdm/libs/core/drm/hw_device_drm.h
@@ -49,7 +49,7 @@
 
 class HWDeviceDRM : public HWInterface {
  public:
-  explicit HWDeviceDRM(BufferSyncHandler *buffer_sync_handler, BufferAllocator *buffer_allocator,
+  HWDeviceDRM(BufferSyncHandler *buffer_sync_handler, BufferAllocator *buffer_allocator,
                        HWInfoInterface *hw_info_intf);
   virtual ~HWDeviceDRM() {}
   virtual DisplayError Init();
@@ -94,6 +94,7 @@
   virtual DisplayError SetScaleLutConfig(HWScaleLutInfo *lut_info);
   virtual DisplayError SetMixerAttributes(const HWMixerAttributes &mixer_attributes);
   virtual DisplayError GetMixerAttributes(HWMixerAttributes *mixer_attributes);
+  virtual void InitializeConfigs();
 
   enum {
     kHWEventVSync,
@@ -116,10 +117,10 @@
   void ResetDisplayParams();
   bool EnableHotPlugDetection(int enable);
   void UpdateMixerAttributes();
-  void InitializeConfigs();
   void SetBlending(const LayerBlending &source, sde_drm::DRMBlendType *target);
   void SetSrcConfig(const LayerBuffer &input_buffer, uint32_t *config);
   void SetRect(const LayerRect &source, sde_drm::DRMRect *target);
+  void SetRotation(LayerTransform transform, const HWRotatorMode &mode, uint32_t* rot_bit_mask);
   DisplayError DefaultCommit(HWLayers *hw_layers);
   DisplayError AtomicCommit(HWLayers *hw_layers);
   void SetupAtomic(HWLayers *hw_layers, bool validate);
@@ -133,6 +134,8 @@
     void UnregisterNext();
     // Call on display disconnect to release all gem handles and fb_ids
     void Clear();
+    // Maps given fd to FB ID
+    void MapBufferToFbId(LayerBuffer* buffer);
     // Finds an fb_id corresponding to an fd in current map
     uint32_t GetFbId(int fd);
 
@@ -145,24 +148,30 @@
     BufferAllocator *buffer_allocator_ = {};
   };
 
-  HWResourceInfo hw_resource_ = {};
-  HWPanelInfo hw_panel_info_ = {};
+ protected:
+  const char *device_name_ = {};
+  bool deferred_initialize_ = false;
+  sde_drm::DRMDisplayType disp_type_ = {};
   HWInfoInterface *hw_info_intf_ = {};
   BufferSyncHandler *buffer_sync_handler_ = {};
+  int dev_fd_ = -1;
+  Registry registry_;
+  sde_drm::DRMDisplayToken token_ = {};
+  HWResourceInfo hw_resource_ = {};
+  HWPanelInfo hw_panel_info_ = {};
   HWDeviceType device_type_ = {};
-  const char *device_name_ = {};
-  bool synchronous_commit_ = false;
-  HWDisplayAttributes display_attributes_ = {};
-  HWMixerAttributes mixer_attributes_ = {};
   sde_drm::DRMManagerInterface *drm_mgr_intf_ = {};
   sde_drm::DRMAtomicReqInterface *drm_atomic_intf_ = {};
-  sde_drm::DRMDisplayToken token_ = {};
-  drmModeModeInfo current_mode_ = {};
-  bool default_mode_ = false;
   sde_drm::DRMConnectorInfo connector_info_ = {};
+  drmModeModeInfo current_mode_ = {};
+  HWDisplayAttributes display_attributes_ = {};
+
+ private:
+  bool synchronous_commit_ = false;
+  HWMixerAttributes mixer_attributes_ = {};
+  bool default_mode_ = false;
   std::string interface_str_ = "DSI";
   HWScaleDRM *hw_scale_ = {};
-  Registry registry_;
 };
 
 }  // namespace sdm
diff --git a/sdm/libs/core/drm/hw_info_drm.cpp b/sdm/libs/core/drm/hw_info_drm.cpp
index 6258d73..d5bd5cf 100644
--- a/sdm/libs/core/drm/hw_info_drm.cpp
+++ b/sdm/libs/core/drm/hw_info_drm.cpp
@@ -332,6 +332,7 @@
   hw_resource->max_scale_up = info.max_upscale;
   hw_resource->has_decimation = info.max_horizontal_deci > 1 && info.max_vertical_deci > 1;
   hw_resource->max_pipe_bw = info.max_pipe_bandwidth / kKiloUnit;
+  hw_resource->cache_size = info.cache_size;
 }
 
 void HWInfoDRM::PopulateSupportedFmts(HWSubBlockType sub_blk_type,
diff --git a/sdm/libs/core/drm/hw_virtual_drm.cpp b/sdm/libs/core/drm/hw_virtual_drm.cpp
new file mode 100644
index 0000000..5fe1d86
--- /dev/null
+++ b/sdm/libs/core/drm/hw_virtual_drm.cpp
@@ -0,0 +1,188 @@
+/*
+Copyright (c) 2017, 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 <ctype.h>
+#include <drm_logger.h>
+#include <utils/debug.h>
+#include "hw_device_drm.h"
+#include "hw_virtual_drm.h"
+#include "hw_info_drm.h"
+
+#define __CLASS__ "HWVirtualDRM"
+
+using sde_drm::DRMDisplayType;
+using sde_drm::DRMConnectorInfo;
+using sde_drm::DRMRect;
+using sde_drm::DRMOps;
+
+namespace sdm {
+
+HWVirtualDRM::HWVirtualDRM(BufferSyncHandler *buffer_sync_handler,
+                           BufferAllocator *buffer_allocator,
+                           HWInfoInterface *hw_info_intf)
+                           : HWDeviceDRM(buffer_sync_handler, buffer_allocator, hw_info_intf) {
+  HWDeviceDRM::deferred_initialize_ = true;
+  HWDeviceDRM::device_name_ = "Virtual Display Device";
+  HWDeviceDRM::hw_info_intf_ = hw_info_intf;
+  HWDeviceDRM::disp_type_ = DRMDisplayType::VIRTUAL;
+}
+
+DisplayError HWVirtualDRM::Init() {
+  return kErrorNone;
+}
+
+DisplayError HWVirtualDRM::DeferredInit() {
+  if (HWDeviceDRM::Init() != kErrorNone)
+    return kErrorResources;
+
+  drm_mgr_intf_->SetScalerLUT(drm_lut_info_);
+  DLOGI_IF(kTagDriverConfig, "Setup CRTC %d, Connector %d for %s",
+            token_.crtc_id, token_.conn_id, device_name_);
+
+  return kErrorNone;
+}
+
+void HWVirtualDRM::ConfigureWbConnectorFbId(uint32_t fb_id) {
+  drm_atomic_intf_->Perform(DRMOps::CONNECTOR_SET_OUTPUT_FB_ID, token_.conn_id, fb_id);
+  return;
+}
+
+void HWVirtualDRM::ConfigureWbConnectorDestRect() {
+  DRMRect dst = {};
+  dst.left = 0;
+  dst.bottom = height_;
+  dst.top = 0;
+  dst.right = width_;
+  drm_atomic_intf_->Perform(DRMOps::CONNECTOR_SET_OUTPUT_RECT, token_.conn_id, dst);
+  return;
+}
+
+void HWVirtualDRM::InitializeConfigs() {
+  current_mode_.hdisplay = current_mode_.hsync_start = current_mode_.hsync_end \
+  = current_mode_.htotal = (uint16_t) width_;
+  current_mode_.vdisplay = current_mode_.vsync_start = current_mode_.vsync_end \
+  = current_mode_.vtotal = (uint16_t) height_;
+  // Not sure SF has a way to configure refresh rate. Hardcoding to 60 fps for now.
+  // TODO(user): Make this configurable.
+  current_mode_.vrefresh = 60;
+  current_mode_.clock = (current_mode_.htotal * current_mode_.vtotal \
+  * current_mode_.vrefresh) / 1000;
+  struct sde_drm_wb_cfg wb_cfg;
+  wb_cfg.connector_id = token_.conn_id;
+  wb_cfg.flags |= SDE_DRM_WB_CFG_FLAGS_CONNECTED;
+  wb_cfg.count_modes = 1;
+  wb_cfg.modes = (uint64_t)&current_mode_;
+  #ifdef DRM_IOCTL_SDE_WB_CONFIG
+  int ret = drmIoctl(dev_fd_, DRM_IOCTL_SDE_WB_CONFIG, &wb_cfg);
+  #endif
+  if (ret) {
+    DLOGE("WB config failed\n");
+  } else {
+    drm_mgr_intf_->GetConnectorInfo(token_.conn_id, &connector_info_);
+    current_mode_ = connector_info_.modes[0];
+    DumpConfigs();
+  }
+}
+
+void HWVirtualDRM::DumpConfigs() {
+  for (uint32_t i = 0; i < (uint32_t)connector_info_.num_modes; i++) {
+  DLOGI(
+    "Name: %s\tvref: %d\thdisp: %d\t hsync_s: %d\thsync_e:%d\thtotal: %d\t"
+    "vdisp: %d\tvsync_s: %d\tvsync_e: %d\tvtotal: %d\n",
+    connector_info_.modes[i].name, connector_info_.modes[i].vrefresh,
+    connector_info_.modes[i].hdisplay,
+    connector_info_.modes[i].hsync_start, connector_info_.modes[i].hsync_end,
+    connector_info_.modes[i].htotal, connector_info_.modes[i].vdisplay,
+    connector_info_.modes[i].vsync_start, connector_info_.modes[i].vsync_end,
+    connector_info_.modes[i].vtotal);
+  }
+}
+
+DisplayError HWVirtualDRM::Commit(HWLayers *hw_layers) {
+  LayerBuffer *output_buffer = hw_layers->info.stack->output_buffer;
+  DisplayError err = kErrorNone;
+
+  registry_.RegisterCurrent(hw_layers);
+  registry_.MapBufferToFbId(output_buffer);
+  uint32_t fb_id = registry_.GetFbId(output_buffer->planes[0].fd);
+
+  ConfigureWbConnectorFbId(fb_id);
+  ConfigureWbConnectorDestRect();
+
+  err = HWDeviceDRM::AtomicCommit(hw_layers);
+  registry_.UnregisterNext();
+  return(err);
+}
+
+DisplayError HWVirtualDRM::Validate(HWLayers *hw_layers) {
+  // TODO(user) : Add validate support
+  return kErrorNone;
+}
+
+
+DisplayError HWVirtualDRM::SetDisplayAttributes(const HWDisplayAttributes &display_attributes) {
+  if (display_attributes.x_pixels == 0 || display_attributes.y_pixels == 0) {
+    return kErrorParameters;
+  }
+
+  display_attributes_ = display_attributes;
+
+  if (display_attributes_.x_pixels > hw_resource_.max_mixer_width) {
+    display_attributes_.is_device_split = true;
+  }
+
+  width_ = display_attributes_.x_pixels;
+  height_ = display_attributes_.y_pixels;
+  DeferredInit();
+
+  return kErrorNone;
+}
+
+DisplayError HWVirtualDRM::GetPPFeaturesVersion(PPFeatureVersion *vers) {
+  return kErrorNone;
+}
+
+DisplayError HWVirtualDRM::SetScaleLutConfig(HWScaleLutInfo *lut_info) {
+  drm_lut_info_.cir_lut = lut_info->cir_lut;
+  drm_lut_info_.dir_lut = lut_info->dir_lut;
+  drm_lut_info_.sep_lut = lut_info->sep_lut;
+  drm_lut_info_.cir_lut_size = lut_info->cir_lut_size;
+  drm_lut_info_.dir_lut_size = lut_info->dir_lut_size;
+  drm_lut_info_.sep_lut_size = lut_info->sep_lut_size;
+
+  // Due to differed Init in WB case, we cannot set scaler config immediately as we
+  // won't have SDE DRM initialized at this point. Hence have to cache LUT info here
+  // and set it in ::DeferredInit
+
+  return kErrorNone;
+}
+
+}  // namespace sdm
+
diff --git a/sdm/libs/core/drm/hw_virtual_drm.h b/sdm/libs/core/drm/hw_virtual_drm.h
new file mode 100644
index 0000000..b63519a
--- /dev/null
+++ b/sdm/libs/core/drm/hw_virtual_drm.h
@@ -0,0 +1,71 @@
+/*
+Copyright (c) 2017, 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_VIRTUAL_DRM_H__
+#define __HW_VIRTUAL_DRM_H__
+
+#include "hw_device_drm.h"
+#include <drm/msm_drm.h>
+#include <drm/sde_drm.h>
+
+namespace sdm {
+
+class HWVirtualDRM : public HWDeviceDRM {
+ public:
+  HWVirtualDRM(BufferSyncHandler *buffer_sync_handler,
+               BufferAllocator *buffer_allocator, HWInfoInterface *hw_info_intf);
+  virtual ~HWVirtualDRM() {}
+  virtual DisplayError SetVSyncState(bool enable) { return kErrorNotSupported; }
+  virtual DisplayError SetMixerAttributes(const HWMixerAttributes &mixer_attributes) {
+    return kErrorNotSupported;
+  }
+  virtual DisplayError SetDisplayAttributes(const HWDisplayAttributes &display_attributes);
+
+ protected:
+  virtual DisplayError Init();
+  virtual DisplayError Validate(HWLayers *hw_layers);
+  virtual DisplayError DeferredInit();
+  virtual void InitializeConfigs();
+  virtual DisplayError Commit(HWLayers *hw_layers);
+  virtual DisplayError GetPPFeaturesVersion(PPFeatureVersion *vers);
+  virtual DisplayError SetScaleLutConfig(HWScaleLutInfo *lut_info);
+  void ConfigureWbConnectorFbId(uint32_t fb_id);
+  void ConfigureWbConnectorDestRect();
+  void DumpConfigs();
+
+ private:
+  uint32_t width_ = 0;
+  uint32_t height_ = 0;
+  sde_drm::DRMScalerLUTInfo drm_lut_info_ = {};
+};
+
+}  // namespace sdm
+
+#endif  // __HW_VIRTUAL_DRM_H__
+
diff --git a/sdm/libs/core/hw_interface.cpp b/sdm/libs/core/hw_interface.cpp
index b5c9fe9..a7bcabd 100644
--- a/sdm/libs/core/hw_interface.cpp
+++ b/sdm/libs/core/hw_interface.cpp
@@ -37,6 +37,7 @@
 #include "fb/hw_virtual.h"
 #ifdef COMPILE_DRM
 #include "drm/hw_device_drm.h"
+#include "drm/hw_virtual_drm.h"
 #endif
 
 #define __CLASS__ "HWInterface"
@@ -71,7 +72,9 @@
       if (driver_type == DriverType::FB) {
         hw = new HWVirtual(buffer_sync_handler, hw_info_intf);
       } else {
-        return kErrorNotSupported;
+#ifdef COMPILE_DRM
+        hw = new HWVirtualDRM(buffer_sync_handler, buffer_allocator, hw_info_intf);
+#endif
       }
       break;
     default:
diff --git a/sdm/libs/hwc/hwc_display.cpp b/sdm/libs/hwc/hwc_display.cpp
index 07ec2ce..2c5c885 100644
--- a/sdm/libs/hwc/hwc_display.cpp
+++ b/sdm/libs/hwc/hwc_display.cpp
@@ -58,15 +58,6 @@
 
 namespace sdm {
 
-static void ApplyDeInterlaceAdjustment(Layer *layer) {
-  // De-interlacing adjustment
-  if (layer->input_buffer.flags.interlace) {
-    float height = (layer->src_rect.bottom - layer->src_rect.top) / 2.0f;
-    layer->src_rect.top = ROUND_UP_ALIGN_DOWN(layer->src_rect.top / 2.0f, 2);
-    layer->src_rect.bottom = layer->src_rect.top + floorf(height);
-  }
-}
-
 void HWCColorMode::Init() {
   int ret = PopulateColorModes();
   if (ret != 0) {
@@ -575,7 +566,6 @@
         }
     }
     SetRect(hwc_layer.sourceCropf, &layer->src_rect);
-    ApplyDeInterlaceAdjustment(layer);
 
     uint32_t num_visible_rects = UINT32(hwc_layer.visibleRegionScreen.numRects);
     uint32_t num_dirty_rects = UINT32(hwc_layer.surfaceDamage.numRects);
diff --git a/sdm/libs/hwc/hwc_session.cpp b/sdm/libs/hwc/hwc_session.cpp
index 8a5bd81..98e14ee 100644
--- a/sdm/libs/hwc/hwc_session.cpp
+++ b/sdm/libs/hwc/hwc_session.cpp
@@ -289,7 +289,7 @@
     }
 
     if (hwc_session->need_invalidate_) {
-      hwc_procs->invalidate(hwc_procs);
+      hwc_session->AsyncRefresh();
       hwc_session->need_invalidate_ = false;
     }
 
@@ -420,6 +420,8 @@
     hwc_session->bw_mode_release_fd_ = dup(content_list->retireFenceFd);
   }
 
+  locker_.Signal();
+
   // This is only indicative of how many times SurfaceFlinger posts
   // frames to the display.
   CALC_FPS();
@@ -693,6 +695,14 @@
   return 0;
 }
 
+static void PostRefresh(hwc_procs_t const *hwc_procs) {
+  hwc_procs->invalidate(hwc_procs);
+}
+
+void HWCSession::AsyncRefresh() {
+  future_ = std::async(PostRefresh, hwc_procs_);
+}
+
 android::status_t HWCSession::notifyCallback(uint32_t command, const android::Parcel *input_parcel,
                                              android::Parcel *output_parcel) {
   SEQUENCE_WAIT_SCOPE_LOCK(locker_);
@@ -705,7 +715,7 @@
     break;
 
   case qService::IQService::SCREEN_REFRESH:
-    hwc_procs_->invalidate(hwc_procs_);
+    AsyncRefresh();
     break;
 
   case qService::IQService::SET_IDLE_TIMEOUT:
@@ -856,7 +866,7 @@
 }
 
 android::status_t HWCSession::ControlPartialUpdate(const android::Parcel *input_parcel,
-                                                   android::Parcel *out) {
+                                                   android::Parcel *output_parcel) {
   DisplayError error = kErrorNone;
   int ret = 0;
   uint32_t disp_id = UINT32(input_parcel->readInt32());
@@ -865,14 +875,14 @@
   if (disp_id != HWC_DISPLAY_PRIMARY) {
     DLOGW("CONTROL_PARTIAL_UPDATE is not applicable for display = %d", disp_id);
     ret = -EINVAL;
-    out->writeInt32(ret);
+    output_parcel->writeInt32(ret);
     return ret;
   }
 
   if (!hwc_display_[HWC_DISPLAY_PRIMARY]) {
     DLOGE("primary display object is not instantiated");
     ret = -EINVAL;
-    out->writeInt32(ret);
+    output_parcel->writeInt32(ret);
     return ret;
   }
 
@@ -881,31 +891,30 @@
 
   if (error == kErrorNone) {
     if (!pending) {
-      out->writeInt32(ret);
+      output_parcel->writeInt32(ret);
       return ret;
     }
   } else if (error == kErrorNotSupported) {
-    out->writeInt32(ret);
+    output_parcel->writeInt32(ret);
     return ret;
   } else {
     ret = -EINVAL;
-    out->writeInt32(ret);
+    output_parcel->writeInt32(ret);
     return ret;
   }
 
-  // Todo(user): Unlock it before sending events to client. It may cause deadlocks in future.
-  hwc_procs_->invalidate(hwc_procs_);
+  AsyncRefresh();
 
   // Wait until partial update control is complete
   ret = locker_.WaitFinite(kPartialUpdateControlTimeoutMs);
 
-  out->writeInt32(ret);
+  output_parcel->writeInt32(ret);
 
   return ret;
 }
 
 android::status_t HWCSession::HandleSetActiveDisplayConfig(const android::Parcel *input_parcel,
-                                                     android::Parcel *output_parcel) {
+                                                           android::Parcel *output_parcel) {
   int config = input_parcel->readInt32();
   int dpy = input_parcel->readInt32();
   int error = android::BAD_VALUE;
@@ -917,7 +926,7 @@
   if (hwc_display_[dpy]) {
     error = hwc_display_[dpy]->SetActiveDisplayConfig(config);
     if (error == 0) {
-      hwc_procs_->invalidate(hwc_procs_);
+      AsyncRefresh();
     }
   }
 
@@ -1128,7 +1137,7 @@
   HWBwModes mode = camera_status > 0 ? kBwCamera : kBwDefault;
 
   // trigger invalidate to apply new bw caps.
-  hwc_procs_->invalidate(hwc_procs_);
+  AsyncRefresh();
 
     error = core_intf_->SetMaxBandwidthMode(mode);
   if (error != kErrorNone) {
@@ -1325,7 +1334,7 @@
 
   switch (pending_action.action) {
     case kInvalidating:
-      hwc_procs_->invalidate(hwc_procs_);
+      AsyncRefresh();
       break;
     case kEnterQDCMMode:
       ret = color_mgr_->EnableQDCMMode(true, hwc_display_[HWC_DISPLAY_PRIMARY]);
@@ -1336,12 +1345,12 @@
     case kApplySolidFill:
       ret = color_mgr_->SetSolidFill(pending_action.params,
                                      true, hwc_display_[HWC_DISPLAY_PRIMARY]);
-      hwc_procs_->invalidate(hwc_procs_);
+      AsyncRefresh();
       break;
     case kDisableSolidFill:
       ret = color_mgr_->SetSolidFill(pending_action.params,
                                      false, hwc_display_[HWC_DISPLAY_PRIMARY]);
-      hwc_procs_->invalidate(hwc_procs_);
+      AsyncRefresh();
       break;
     case kSetPanelBrightness:
       brightness_value = reinterpret_cast<int32_t*>(resp_payload.payload);
@@ -1355,7 +1364,7 @@
     case kEnableFrameCapture:
       ret = color_mgr_->SetFrameCapture(pending_action.params,
                                         true, hwc_display_[HWC_DISPLAY_PRIMARY]);
-      hwc_procs_->invalidate(hwc_procs_);
+      AsyncRefresh();
       break;
     case kDisableFrameCapture:
       ret = color_mgr_->SetFrameCapture(pending_action.params,
@@ -1364,7 +1373,7 @@
     case kConfigureDetailedEnhancer:
       ret = color_mgr_->SetDetailedEnhancer(pending_action.params,
                                             hwc_display_[HWC_DISPLAY_PRIMARY]);
-      hwc_procs_->invalidate(hwc_procs_);
+      AsyncRefresh();
       break;
     case kInvalidatingAndkSetPanelBrightness:
       brightness_value = reinterpret_cast<int32_t*>(resp_payload.payload);
@@ -1374,7 +1383,7 @@
       }
       if (HWC_DISPLAY_PRIMARY == display_id)
         ret = hwc_display_[HWC_DISPLAY_PRIMARY]->CachePanelBrightness(*brightness_value);
-      hwc_procs_->invalidate(hwc_procs_);
+      AsyncRefresh();
       break;
     case kNoAction:
       break;
@@ -1460,7 +1469,7 @@
       if (panel_reset == 0) {
         if (hwc_procs_) {
           reset_panel_ = true;
-          hwc_procs_->invalidate(hwc_procs_);
+          AsyncRefresh();
         } else {
           DLOGW("Ignore resetpanel - hwc_proc not registered");
         }
diff --git a/sdm/libs/hwc/hwc_session.h b/sdm/libs/hwc/hwc_session.h
index 1cd3e33..ce21c46 100644
--- a/sdm/libs/hwc/hwc_session.h
+++ b/sdm/libs/hwc/hwc_session.h
@@ -28,6 +28,7 @@
 #include <hardware/hwcomposer.h>
 #include <core/core_interface.h>
 #include <utils/locker.h>
+#include <future>   // NOLINT
 
 #include "hwc_display_primary.h"
 #include "hwc_display_external.h"
@@ -88,6 +89,7 @@
   int GetVsyncPeriod(int disp);
   int CreateExternalDisplay(int disp, uint32_t primary_width, uint32_t primary_height,
                             bool use_primary_res);
+  void AsyncRefresh();
 
   // QClient methods
   virtual android::status_t notifyCallback(uint32_t command, const android::Parcel *input_parcel,
@@ -103,7 +105,8 @@
   android::status_t ConfigureRefreshRate(const android::Parcel *input_parcel);
   android::status_t QdcmCMDHandler(const android::Parcel *input_parcel,
                                    android::Parcel *output_parcel);
-  android::status_t ControlPartialUpdate(const android::Parcel *input_parcel, android::Parcel *out);
+  android::status_t ControlPartialUpdate(const android::Parcel *input_parcel,
+                                         android::Parcel *output_parcel);
   android::status_t OnMinHdcpEncryptionLevelChange(const android::Parcel *input_parcel,
                                                    android::Parcel *output_parcel);
   android::status_t SetPanelBrightness(const android::Parcel *input_parcel,
@@ -151,6 +154,7 @@
   qService::QService *qservice_ = NULL;
   bool is_hdmi_primary_ = false;
   bool is_hdmi_yuv_ = false;
+  std::future<void> future_;
   std::bitset<HWC_NUM_DISPLAY_TYPES> connected_displays_;  // Bit mask of connected displays
   HWCSocketHandler socket_handler_;
   Locker uevent_locker_;
diff --git a/sdm/libs/hwc2/hwc_buffer_allocator.cpp b/sdm/libs/hwc2/hwc_buffer_allocator.cpp
index da40a34..3c8d460 100644
--- a/sdm/libs/hwc2/hwc_buffer_allocator.cpp
+++ b/sdm/libs/hwc2/hwc_buffer_allocator.cpp
@@ -114,10 +114,6 @@
   alloc_buffer_info->fd = -1;
   alloc_buffer_info->stride = 0;
   alloc_buffer_info->size = 0;
-  // Works around b/36355756
-  if (hnd != nullptr) {
-    delete hnd;
-  }
   buffer_info->private_data = NULL;
   return err;
 }
diff --git a/sdm/libs/hwc2/hwc_display.cpp b/sdm/libs/hwc2/hwc_display.cpp
index f48ed2d..4f623ce 100644
--- a/sdm/libs/hwc2/hwc_display.cpp
+++ b/sdm/libs/hwc2/hwc_display.cpp
@@ -54,26 +54,6 @@
 
 namespace sdm {
 
-static void ApplyDeInterlaceAdjustment(HWCLayer *hwc_layer, Layer *sdm_layer) {
-  // De-interlacing adjustment
-  if (sdm_layer->input_buffer.flags.interlace) {
-    // Adjust src_rect only if new source crop was set
-    if (hwc_layer->GetGeometryChanges() & kSourceCrop) {
-      float height = (sdm_layer->src_rect.bottom - sdm_layer->src_rect.top) / 2.0f;
-      sdm_layer->src_rect.top = ROUND_UP_ALIGN_DOWN(sdm_layer->src_rect.top / 2.0f, 2);
-      sdm_layer->src_rect.bottom = sdm_layer->src_rect.top + floorf(height);
-    }
-
-    // Handle deinterlacing for UBWC Interlaced format.
-    if (IsUBWCFormat(sdm_layer->input_buffer.format)) {
-      sdm_layer->input_buffer.height /= 2;
-      sdm_layer->input_buffer.unaligned_height /= 2;
-      // After adjustments layer needs to be treated as UBWC progressive. Reset interlace flag.
-      sdm_layer->input_buffer.flags.interlace = 0;
-    }
-  }
-}
-
 // This weight function is needed because the color primaries are not sorted by gamut size
 static ColorPrimaries WidestPrimaries(ColorPrimaries p1, ColorPrimaries p2) {
   int weight = 10;
@@ -417,6 +397,7 @@
   HWCLayer *layer = *layer_set_.emplace(new HWCLayer(id_, buffer_allocator_));
   layer_map_.emplace(std::make_pair(layer->GetId(), layer));
   *out_layer_id = layer->GetId();
+  validated_ = false;
   geometry_changes_ |= GeometryChanges::kAdded;
   return HWC2::Error::None;
 }
@@ -447,6 +428,7 @@
       break;
     }
   }
+  validated_ = false;
 
   geometry_changes_ |= GeometryChanges::kRemoved;
   return HWC2::Error::None;
@@ -462,6 +444,9 @@
   // Add one layer for fb target
   // TODO(user): Add blit target layers
   for (auto hwc_layer : layer_set_) {
+    // Reset layer data which SDM may change
+    hwc_layer->ResetPerFrameData();
+
     Layer *layer = hwc_layer->GetSDMLayer();
     layer->flags = {};   // Reset earlier flags
     if (hwc_layer->GetClientRequestedCompositionType() == HWC2::Composition::Client) {
@@ -536,7 +521,6 @@
                                        INT(layer->dst_rect.right), INT(layer->dst_rect.bottom)};
     ApplyScanAdjustment(&scaled_display_frame);
     hwc_layer->SetLayerDisplayFrame(scaled_display_frame);
-    ApplyDeInterlaceAdjustment(hwc_layer, layer);
     // SDM requires these details even for solid fill
     if (layer->flags.solid_fill) {
       LayerBuffer *layer_buffer = &layer->input_buffer;
@@ -604,6 +588,7 @@
     DLOGE("[%" PRIu64 "] updateLayerZ failed to find layer", id_);
     return HWC2::Error::BadLayer;
   }
+  validated_ = false;
 
   const auto layer = map_layer->second;
   const auto z_range = layer_set_.equal_range(layer);
diff --git a/sdm/libs/hwc2/hwc_display.h b/sdm/libs/hwc2/hwc_display.h
index a375831..1b04c84 100644
--- a/sdm/libs/hwc2/hwc_display.h
+++ b/sdm/libs/hwc2/hwc_display.h
@@ -137,6 +137,18 @@
   virtual int GetDisplayConfigCount(uint32_t *count);
   virtual int GetDisplayAttributesForConfig(int config,
                                             DisplayConfigVariableInfo *display_attributes);
+  template <typename... Args>
+  int32_t CallLayerFunction(hwc2_layer_t layer, HWC2::Error (HWCLayer::*member)(Args... ),
+                            Args... args) {
+    auto status = HWC2::Error::BadLayer;
+    validated_ = false;
+    auto hwc_layer = GetHWCLayer(layer);
+    if (hwc_layer != nullptr) {
+      status = (hwc_layer->*member)(std::forward<Args>(args)...);
+    }
+
+    return INT32(status);
+  }
 
   int SetPanelBrightness(int level);
   int GetPanelBrightness(int *level);
diff --git a/sdm/libs/hwc2/hwc_layers.cpp b/sdm/libs/hwc2/hwc_layers.cpp
index 7fef9cf..7fcd56b 100644
--- a/sdm/libs/hwc2/hwc_layers.cpp
+++ b/sdm/libs/hwc2/hwc_layers.cpp
@@ -258,14 +258,21 @@
 
 HWC2::Error HWCLayer::SetLayerDisplayFrame(hwc_rect_t frame) {
   LayerRect dst_rect = {};
+
   SetRect(frame, &dst_rect);
-  if (layer_->dst_rect != dst_rect) {
+  if (dst_rect_ != dst_rect) {
     geometry_changes_ |= kDisplayFrame;
-    layer_->dst_rect = dst_rect;
+    dst_rect_ = dst_rect;
   }
+
   return HWC2::Error::None;
 }
 
+void HWCLayer::ResetPerFrameData() {
+  layer_->dst_rect = dst_rect_;
+  layer_->transform = layer_transform_;
+}
+
 HWC2::Error HWCLayer::SetLayerPlaneAlpha(float alpha) {
   // Conversion of float alpha in range 0.0 to 1.0 similar to the HWC Adapter
   uint8_t plane_alpha = static_cast<uint8_t>(std::round(255.0f * alpha));
@@ -322,10 +329,11 @@
       break;
   }
 
-  if (layer_->transform != layer_transform) {
+  if (layer_transform_ != layer_transform) {
     geometry_changes_ |= kTransform;
-    layer_->transform = layer_transform;
+    layer_transform_ = layer_transform;
   }
+
   return HWC2::Error::None;
 }
 
diff --git a/sdm/libs/hwc2/hwc_layers.h b/sdm/libs/hwc2/hwc_layers.h
index bc3d84d..82bf466 100644
--- a/sdm/libs/hwc2/hwc_layers.h
+++ b/sdm/libs/hwc2/hwc_layers.h
@@ -61,6 +61,7 @@
   uint32_t GetZ() const { return z_; }
   hwc2_layer_t GetId() const { return id_; }
   Layer *GetSDMLayer() { return layer_; }
+  void ResetPerFrameData();
 
   HWC2::Error SetLayerBlendMode(HWC2::BlendMode mode);
   HWC2::Error SetLayerBuffer(buffer_handle_t buffer, int32_t acquire_fence);
@@ -96,6 +97,8 @@
   int ion_fd_ = -1;
   HWCBufferAllocator *buffer_allocator_ = NULL;
   int32_t dataspace_ =  HAL_DATASPACE_UNKNOWN;
+  LayerTransform layer_transform_ = {};
+  LayerRect dst_rect_ = {};
 
   // Composition requested by client(SF)
   HWC2::Composition client_requested_ = HWC2::Composition::Device;
diff --git a/sdm/libs/hwc2/hwc_session.h b/sdm/libs/hwc2/hwc_session.h
index 87d41d7..7d31a3a 100644
--- a/sdm/libs/hwc2/hwc_session.h
+++ b/sdm/libs/hwc2/hwc_session.h
@@ -70,15 +70,11 @@
     }
 
     HWCSession *hwc_session = static_cast<HWCSession *>(device);
-    auto status = HWC2::Error::BadDisplay;
+    int32_t status = INT32(HWC2::Error::BadDisplay);
     if (hwc_session->hwc_display_[display]) {
-      status = HWC2::Error::BadLayer;
-      auto hwc_layer = hwc_session->hwc_display_[display]->GetHWCLayer(layer);
-      if (hwc_layer != nullptr) {
-        status = (hwc_layer->*member)(std::forward<Args>(args)...);
-      }
+      status = hwc_session->hwc_display_[display]->CallLayerFunction(layer, member, args...);
     }
-    return INT32(status);
+    return status;
   }
 
   // HWC2 Functions that require a concrete implementation in hwc session
diff --git a/sdm/libs/utils/Android.mk b/sdm/libs/utils/Android.mk
index fe89104..09e1414 100644
--- a/sdm/libs/utils/Android.mk
+++ b/sdm/libs/utils/Android.mk
@@ -25,6 +25,7 @@
                                  $(SDM_HEADER_PATH)/utils/locker.h \
                                  $(SDM_HEADER_PATH)/utils/rect.h \
                                  $(SDM_HEADER_PATH)/utils/sys.h \
+                                 $(SDM_HEADER_PATH)/utils/sync_task.h \
                                  $(SDM_HEADER_PATH)/utils/utils.h \
                                  $(SDM_HEADER_PATH)/utils/factory.h
 
diff --git a/sdm/libs/utils/formats.cpp b/sdm/libs/utils/formats.cpp
index b7deb18..4b7190b 100644
--- a/sdm/libs/utils/formats.cpp
+++ b/sdm/libs/utils/formats.cpp
@@ -127,5 +127,87 @@
   }
 }
 
+float GetBufferFormatBpp(LayerBufferFormat format) {
+  float bpp = 0.0f;
+  switch (format) {
+    case kFormatARGB8888:
+    case kFormatRGBA8888:
+    case kFormatBGRA8888:
+    case kFormatXRGB8888:
+    case kFormatRGBX8888:
+    case kFormatBGRX8888:
+    case kFormatRGBA8888Ubwc:
+    case kFormatRGBX8888Ubwc:
+    case kFormatRGBA1010102:
+    case kFormatARGB2101010:
+    case kFormatRGBX1010102:
+    case kFormatXRGB2101010:
+    case kFormatBGRA1010102:
+    case kFormatABGR2101010:
+    case kFormatBGRX1010102:
+    case kFormatXBGR2101010:
+    case kFormatRGBA1010102Ubwc:
+    case kFormatRGBX1010102Ubwc:
+      return 4.0f;
+    case kFormatRGB888:
+    case kFormatBGR888:
+    case kFormatYCbCr420P010:
+    case kFormatYCbCr420P010Ubwc:
+      return 3.0f;
+    case kFormatRGB565:
+    case kFormatBGR565:
+    case kFormatRGBA5551:
+    case kFormatRGBA4444:
+    case kFormatBGR565Ubwc:
+    case kFormatYCbCr422H2V1Packed:
+    case kFormatCbYCrY422H2V1Packed:
+    case kFormatYCrCb422H2V1SemiPlanar:
+    case kFormatYCbCr422H2V1SemiPlanar:
+    case kFormatYCbCr420TP10Ubwc:
+    case kFormatYCbCr422H1V2SemiPlanar:
+    case kFormatYCrCb422H1V2SemiPlanar:
+      return 2.0f;
+    case kFormatYCbCr420Planar:
+    case kFormatYCrCb420Planar:
+    case kFormatYCrCb420PlanarStride16:
+    case kFormatYCbCr420SemiPlanar:
+    case kFormatYCrCb420SemiPlanar:
+    case kFormatYCbCr420SemiPlanarVenus:
+    case kFormatYCrCb420SemiPlanarVenus:
+    case kFormatYCbCr420SPVenusUbwc:
+      return 1.5f;
+    default:
+      return 0.0f;
+  }
+
+  return bpp;
+}
+
+DisplayError GetBufferFormatTileSize(LayerBufferFormat format, FormatTileSize *tile_size) {
+  switch (format) {
+  case kFormatYCbCr420SPVenusUbwc:
+    tile_size->tile_width = 32;
+    tile_size->tile_height = 8;
+    tile_size->uv_tile_width = 16;
+    tile_size->uv_tile_height = 8;
+    break;
+  case kFormatYCbCr420TP10Ubwc:
+    tile_size->tile_width = 48;
+    tile_size->tile_height = 4;
+    tile_size->uv_tile_width = 24;
+    tile_size->uv_tile_height = 4;
+    break;
+  case kFormatYCbCr420P010Ubwc:
+    tile_size->tile_width = 32;
+    tile_size->tile_height = 4;
+    tile_size->uv_tile_width = 16;
+    tile_size->uv_tile_height = 4;
+    break;
+  default:
+    return kErrorNotSupported;
+  }
+  return kErrorNone;
+}
+
 }  // namespace sdm
 
diff --git a/sdm/libs/utils/rect.cpp b/sdm/libs/utils/rect.cpp
index b0cd536..c227cb3 100644
--- a/sdm/libs/utils/rect.cpp
+++ b/sdm/libs/utils/rect.cpp
@@ -261,5 +261,47 @@
   return kOrientationLandscape;
 }
 
+DisplayError GetCropAndDestination(const LayerRect &crop, const LayerRect &dst,
+                                   const bool rotated90, float *crop_width,
+                                   float *crop_height, float *dst_width,
+                                   float *dst_height) {
+  if (!IsValid(crop)) {
+    Log(kTagResources, "Invalid crop rect", crop);
+    return kErrorNotSupported;
+  }
+
+  if (!IsValid(dst)) {
+    Log(kTagResources, "Invalid dst rect", dst);
+    return kErrorNotSupported;
+  }
+
+  *crop_width = crop.right - crop.left;
+  *crop_height = crop.bottom - crop.top;
+  if (rotated90) {
+    std::swap(*crop_width, *crop_height);
+  }
+
+  *dst_width = dst.right - dst.left;
+  *dst_height = dst.bottom - dst.top;
+
+  return kErrorNone;
+}
+
+DisplayError GetScaleFactor(const LayerRect &crop, const LayerRect &dst,
+                            bool rotated90, float *scale_x, float *scale_y) {
+  float crop_width = 1.0f, crop_height = 1.0f, dst_width = 1.0f, dst_height = 1.0f;
+
+  DisplayError error = GetCropAndDestination(crop, dst, rotated90, &crop_width, &crop_height,
+                                             &dst_width, &dst_height);
+  if (error != kErrorNone) {
+    return error;
+  }
+
+  *scale_x = crop_width / dst_width;
+  *scale_y = crop_height / dst_height;
+
+  return kErrorNone;
+}
+
 }  // namespace sdm