Merge "hwc2: Fix NULL pointer dereference and out of bound access"
diff --git a/libgralloc1/gr_buf_mgr.cpp b/libgralloc1/gr_buf_mgr.cpp
index d78cc94..35c4b02 100644
--- a/libgralloc1/gr_buf_mgr.cpp
+++ b/libgralloc1/gr_buf_mgr.cpp
@@ -540,6 +540,10 @@
       int format = va_arg(args, int);
 
       native_handle_t **handle = va_arg(args, native_handle_t **);
+      if (!handle) {
+        return GRALLOC1_ERROR_BAD_HANDLE;
+      }
+
       private_handle_t *hnd = reinterpret_cast<private_handle_t *>(
           native_handle_create(private_handle_t::kNumFds, private_handle_t::NumInts()));
       if (hnd) {
@@ -549,7 +553,7 @@
         hnd->flags = private_handle_t::PRIV_FLAGS_USES_ION;
         hnd->size = size;
         hnd->offset = offset;
-        hnd->base = uint64_t(base) + offset;
+        hnd->base = uint64_t(base);
         hnd->gpuaddr = 0;
         BufferInfo info(width, height, format);
         GetAlignedWidthAndHeight(info, &alignedw, &alignedh);
@@ -567,6 +571,11 @@
       int format = va_arg(args, int);
       int *stride = va_arg(args, int *);
       unsigned int alignedw = 0, alignedh = 0;
+
+      if (!stride) {
+        return GRALLOC1_ERROR_BAD_VALUE;
+      }
+
       BufferInfo info(width, width, format);
       GetAlignedWidthAndHeight(info, &alignedw, &alignedh);
       *stride = INT(alignedw);
@@ -579,6 +588,10 @@
         return GRALLOC1_ERROR_BAD_HANDLE;
       }
 
+      if (!stride) {
+        return GRALLOC1_ERROR_BAD_VALUE;
+      }
+
       BufferDim_t buffer_dim;
       if (getMetaData(hnd, GET_BUFFER_GEOMETRY, &buffer_dim) == 0) {
         *stride = buffer_dim.sliceWidth;
@@ -596,6 +609,10 @@
         return GRALLOC1_ERROR_BAD_HANDLE;
       }
 
+      if (!stride || !height) {
+        return GRALLOC1_ERROR_BAD_VALUE;
+      }
+
       BufferDim_t buffer_dim;
       int interlaced = 0;
 
@@ -631,6 +648,10 @@
       int *aligned_width = va_arg(args, int *);
       int *aligned_height = va_arg(args, int *);
       int *tile_enabled = va_arg(args, int *);
+      if (!aligned_width || !aligned_height || !tile_enabled) {
+        return GRALLOC1_ERROR_BAD_VALUE;
+      }
+
       unsigned int alignedw, alignedh;
       BufferInfo info(width, height, format, prod_usage, cons_usage);
       *tile_enabled = IsUBwcEnabled(format, prod_usage, cons_usage);
@@ -642,9 +663,15 @@
     case GRALLOC_MODULE_PERFORM_GET_COLOR_SPACE_FROM_HANDLE: {
       private_handle_t *hnd = va_arg(args, private_handle_t *);
       int *color_space = va_arg(args, int *);
+
       if (private_handle_t::validate(hnd) != 0) {
         return GRALLOC1_ERROR_BAD_HANDLE;
       }
+
+      if (!color_space) {
+        return GRALLOC1_ERROR_BAD_VALUE;
+      }
+
       *color_space = 0;
 #ifdef USE_COLOR_METADATA
       ColorMetaData color_metadata;
@@ -676,6 +703,11 @@
       if (private_handle_t::validate(hnd) != 0) {
         return GRALLOC1_ERROR_BAD_HANDLE;
       }
+
+      if (!ycbcr) {
+        return GRALLOC1_ERROR_BAD_VALUE;
+      }
+
       if (GetYUVPlaneInfo(hnd, ycbcr)) {
         return GRALLOC1_ERROR_UNDEFINED;
       }
@@ -684,10 +716,15 @@
     case GRALLOC_MODULE_PERFORM_GET_MAP_SECURE_BUFFER_INFO: {
       private_handle_t *hnd = va_arg(args, private_handle_t *);
       int *map_secure_buffer = va_arg(args, int *);
+
       if (private_handle_t::validate(hnd) != 0) {
         return GRALLOC1_ERROR_BAD_HANDLE;
       }
 
+      if (!map_secure_buffer) {
+        return GRALLOC1_ERROR_BAD_VALUE;
+      }
+
       if (getMetaData(hnd, GET_MAP_SECURE_BUFFER, map_secure_buffer) == 0) {
         *map_secure_buffer = 0;
       }
@@ -696,9 +733,15 @@
     case GRALLOC_MODULE_PERFORM_GET_UBWC_FLAG: {
       private_handle_t *hnd = va_arg(args, private_handle_t *);
       int *flag = va_arg(args, int *);
+
       if (private_handle_t::validate(hnd) != 0) {
         return GRALLOC1_ERROR_BAD_HANDLE;
       }
+
+      if (!flag) {
+        return GRALLOC1_ERROR_BAD_VALUE;
+      }
+
       *flag = hnd->flags &private_handle_t::PRIV_FLAGS_UBWC_ALIGNED;
       int linear_format = 0;
       if (getMetaData(hnd, GET_LINEAR_FORMAT, &linear_format) == 0) {
@@ -711,9 +754,15 @@
     case GRALLOC_MODULE_PERFORM_GET_RGB_DATA_ADDRESS: {
       private_handle_t *hnd = va_arg(args, private_handle_t *);
       void **rgb_data = va_arg(args, void **);
+
       if (private_handle_t::validate(hnd) != 0) {
         return GRALLOC1_ERROR_BAD_HANDLE;
       }
+
+      if (!rgb_data) {
+        return GRALLOC1_ERROR_BAD_VALUE;
+      }
+
       if (GetRgbDataAddress(hnd, rgb_data)) {
         return GRALLOC1_ERROR_UNDEFINED;
       }
@@ -730,6 +779,11 @@
       uint32_t *aligned_width = va_arg(args, uint32_t *);
       uint32_t *aligned_height = va_arg(args, uint32_t *);
       uint32_t *size = va_arg(args, uint32_t *);
+
+      if (!aligned_width || !aligned_height || !size) {
+        return GRALLOC1_ERROR_BAD_VALUE;
+      }
+
       auto info = BufferInfo(width, height, format, producer_usage, consumer_usage);
       GetBufferSizeAndDimensions(info, size, aligned_width, aligned_height);
       // Align size
@@ -737,14 +791,18 @@
       *size = ALIGN(*size, align);
     } break;
 
-      // TODO(user): Break out similar functionality, preferably moving to a common lib.
-
     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 (!flag) {
+        return GRALLOC1_ERROR_BAD_VALUE;
+      }
+
       if (getMetaData(hnd, GET_PP_PARAM_INTERLACED, flag) != 0) {
         *flag = 0;
       }
diff --git a/libgralloc1/gr_device_impl.cpp b/libgralloc1/gr_device_impl.cpp
index 03e42ab..d421ff1 100644
--- a/libgralloc1/gr_device_impl.cpp
+++ b/libgralloc1/gr_device_impl.cpp
@@ -347,6 +347,10 @@
 
 gralloc1_error_t GrallocImpl::GetProducerUsage(gralloc1_device_t *device, buffer_handle_t buffer,
                                                gralloc1_producer_usage_t *outUsage) {
+  if (!outUsage) {
+    return GRALLOC1_ERROR_BAD_VALUE;
+  }
+
   gralloc1_error_t status = CheckDeviceAndHandle(device, buffer);
   if (status == GRALLOC1_ERROR_NONE) {
     const private_handle_t *hnd = PRIV_HANDLE_CONST(buffer);
@@ -358,6 +362,10 @@
 
 gralloc1_error_t GrallocImpl::GetBufferStride(gralloc1_device_t *device, buffer_handle_t buffer,
                                               uint32_t *outStride) {
+  if (!outStride) {
+    return GRALLOC1_ERROR_BAD_VALUE;
+  }
+
   gralloc1_error_t status = CheckDeviceAndHandle(device, buffer);
   if (status == GRALLOC1_ERROR_NONE) {
     *outStride = UINT(PRIV_HANDLE_CONST(buffer)->GetStride());
@@ -373,6 +381,10 @@
     return GRALLOC1_ERROR_BAD_DESCRIPTOR;
   }
 
+  if (!device) {
+    return GRALLOC1_ERROR_BAD_VALUE;
+  }
+
   GrallocImpl const *dev = GRALLOC_IMPL(device);
   gralloc1_error_t status = dev->buf_mgr_->AllocateBuffers(num_descriptors, descriptors,
                                                            out_buffers);
@@ -403,6 +415,10 @@
 
 gralloc1_error_t GrallocImpl::GetNumFlexPlanes(gralloc1_device_t *device, buffer_handle_t buffer,
                                                uint32_t *out_num_planes) {
+  if (!out_num_planes) {
+    return GRALLOC1_ERROR_BAD_VALUE;
+  }
+
   gralloc1_error_t status = CheckDeviceAndHandle(device, buffer);
   if (status == GRALLOC1_ERROR_NONE) {
     GrallocImpl const *dev = GRALLOC_IMPL(device);
@@ -425,7 +441,8 @@
                                          int32_t acquire_fence) {
   ATRACE_CALL();
   gralloc1_error_t status = CheckDeviceAndHandle(device, buffer);
-  if (status != GRALLOC1_ERROR_NONE) {
+  if (status != GRALLOC1_ERROR_NONE || !out_data ||
+      !region) {  // currently we ignore the region/rect client wants to lock
     CloseFdIfValid(acquire_fence);
     return status;
   }
@@ -452,13 +469,8 @@
     // return GRALLOC1_ERROR_BAD_VALUE;
   }
 
-  // currently we ignore the region/rect client wants to lock
-  if (region == NULL) {
-    return GRALLOC1_ERROR_BAD_VALUE;
-  }
   // TODO(user): Need to check if buffer was allocated with the same flags
   status = dev->buf_mgr_->LockBuffer(hnd, prod_usage, cons_usage);
-
   *out_data = reinterpret_cast<void *>(hnd->base);
 
   return status;
@@ -470,7 +482,12 @@
                                        const gralloc1_rect_t *region,
                                        struct android_flex_layout *out_flex_layout,
                                        int32_t acquire_fence) {
-  void *out_data;
+  if (!out_flex_layout) {
+    CloseFdIfValid(acquire_fence);
+    return GRALLOC1_ERROR_BAD_VALUE;
+  }
+
+  void *out_data {};
   gralloc1_error_t status = GrallocImpl::LockBuffer(device, buffer, prod_usage, cons_usage, region,
                                                     &out_data, acquire_fence);
   if (status != GRALLOC1_ERROR_NONE) {
@@ -486,11 +503,14 @@
 gralloc1_error_t GrallocImpl::UnlockBuffer(gralloc1_device_t *device, buffer_handle_t buffer,
                                            int32_t *release_fence) {
   gralloc1_error_t status = CheckDeviceAndHandle(device, buffer);
-
   if (status != GRALLOC1_ERROR_NONE) {
     return status;
   }
 
+  if (!release_fence) {
+    return GRALLOC1_ERROR_BAD_VALUE;
+  }
+
   const private_handle_t *hnd = PRIV_HANDLE_CONST(buffer);
   GrallocImpl const *dev = GRALLOC_IMPL(device);
 
@@ -500,6 +520,10 @@
 }
 
 gralloc1_error_t GrallocImpl::Gralloc1Perform(gralloc1_device_t *device, int operation, ...) {
+  if (!device) {
+    return GRALLOC1_ERROR_BAD_VALUE;
+  }
+
   va_list args;
   va_start(args, operation);
   GrallocImpl const *dev = GRALLOC_IMPL(device);
diff --git a/libqdutils/qd_utils.cpp b/libqdutils/qd_utils.cpp
index 10ac90b..2cce39c 100644
--- a/libqdutils/qd_utils.cpp
+++ b/libqdutils/qd_utils.cpp
@@ -31,6 +31,7 @@
 #include <gralloc_priv.h>
 #include "qd_utils.h"
 
+static const int kFBNodeMax = 4;
 namespace qdutils {
 
 static int parseLine(char *input, char *tokens[], const uint32_t maxToken, uint32_t *count) {
@@ -57,9 +58,9 @@
     char msmFbTypePath[MAX_FRAME_BUFFER_NAME_SIZE];
     int j = 0;
 
-    for(j = 0; j < HWC_NUM_DISPLAY_TYPES; j++) {
+    for(j = 0; j < kFBNodeMax; j++) {
         snprintf (msmFbTypePath, sizeof(msmFbTypePath),
-                  "/sys/class/graphics/fb%d/msm_fb_type", j);
+                  "/sys/devices/virtual/graphics/fb%d/msm_fb_type", j);
         displayDeviceFP = fopen(msmFbTypePath, "r");
         if(displayDeviceFP) {
             fread(fbType, sizeof(char), MAX_FRAME_BUFFER_NAME_SIZE,
@@ -71,11 +72,11 @@
             }
             fclose(displayDeviceFP);
         } else {
-            ALOGE("%s: Failed to open fb node %d", __func__, j);
+            ALOGE("%s: Failed to open fb node %s", __func__, msmFbTypePath);
         }
     }
 
-    if (j < HWC_NUM_DISPLAY_TYPES)
+    if (j < kFBNodeMax)
         return j;
     else
         ALOGE("%s: Failed to find %s node", __func__, type);
@@ -186,12 +187,12 @@
     }
 
     snprintf(msmFbTypePath, sizeof(msmFbTypePath),
-                 "/sys/class/graphics/fb%d/edid_raw_data", node_id);
+                 "/sys/devices/virtual/graphics/fb%d/edid_raw_data", node_id);
 
     edidFile = open(msmFbTypePath, O_RDONLY, 0);
 
     if (edidFile < 0) {
-        ALOGE("%s no edid raw data found", __func__);
+        ALOGE("%s no edid raw data found %s", __func__,msmFbTypePath);
         return 0;
     }
 
@@ -214,11 +215,11 @@
     }
 
     snprintf(connectPath, sizeof(connectPath),
-             "/sys/class/graphics/fb%d/connected", nodeId);
+             "/sys/devices/virtual/graphics/fb%d/connected", nodeId);
 
     connectFile = fopen(connectPath, "rb");
     if (!connectFile) {
-        ALOGW("Failed to open connect node for device node %d", nodeId);
+        ALOGW("Failed to open connect node for device node %s", connectPath);
         return false;
     }
 
@@ -253,11 +254,11 @@
     }
 
     snprintf(configPath, sizeof(configPath),
-             "/sys/class/graphics/fb%d/config", nodeId);
+             "/sys/devices/virtual/graphics/fb%d/config", nodeId);
 
     configFile = fopen(configPath, "rb");
     if (!configFile) {
-        ALOGW("Failed to open config node for device node %d", nodeId);
+        ALOGW("Failed to open config node for device node %s", configPath);
         return -EINVAL;
     }
 
diff --git a/sdm/libs/core/drm/hw_device_drm.cpp b/sdm/libs/core/drm/hw_device_drm.cpp
index 345aeb2..961a745 100644
--- a/sdm/libs/core/drm/hw_device_drm.cpp
+++ b/sdm/libs/core/drm/hw_device_drm.cpp
@@ -391,8 +391,15 @@
 }
 
 void HWDeviceDRM::InitializeConfigs() {
-  // TODO(user): Choose Best Mode
   current_mode_index_ = 0;
+  // Update current mode with preferred mode
+  for (uint32_t mode_index = 0; mode_index < connector_info_.modes.size(); mode_index++) {
+      if (connector_info_.modes[mode_index].type & DRM_MODE_TYPE_PREFERRED) {
+        current_mode_index_ = mode_index;
+        break;
+      }
+  }
+
   display_attributes_.resize(connector_info_.modes.size());
 
   uint32_t width = connector_info_.modes[current_mode_index_].hdisplay;
@@ -499,8 +506,28 @@
   hw_panel_info_.min_roi_height = connector_info_.hmin;
   hw_panel_info_.needs_roi_merge = connector_info_.roi_merge;
   hw_panel_info_.dynamic_fps = connector_info_.dynamic_fps;
-  hw_panel_info_.min_fps = 60;
-  hw_panel_info_.max_fps = 60;
+  drmModeModeInfo current_mode = connector_info_.modes[current_mode_index_];
+  if (hw_panel_info_.dynamic_fps) {
+    uint32_t min_fps = current_mode.vrefresh;
+    uint32_t max_fps = current_mode.vrefresh;
+    for (uint32_t mode_index = 0; mode_index < connector_info_.modes.size(); mode_index++) {
+      if ((current_mode.vdisplay == connector_info_.modes[mode_index].vdisplay) &&
+          (current_mode.hdisplay == connector_info_.modes[mode_index].hdisplay)) {
+        if (min_fps > connector_info_.modes[mode_index].vrefresh)  {
+          min_fps = connector_info_.modes[mode_index].vrefresh;
+        }
+        if (max_fps < connector_info_.modes[mode_index].vrefresh)  {
+          max_fps = connector_info_.modes[mode_index].vrefresh;
+        }
+      }
+    }
+    hw_panel_info_.min_fps = min_fps;
+    hw_panel_info_.max_fps = max_fps;
+  } else {
+    hw_panel_info_.min_fps = current_mode.vrefresh;
+    hw_panel_info_.max_fps = current_mode.vrefresh;
+  }
+
   hw_panel_info_.is_primary_panel = connector_info_.is_primary;
   hw_panel_info_.is_pluggable = 0;
   hw_panel_info_.hdr_enabled = connector_info_.panel_hdr_prop.hdr_enabled;
@@ -537,7 +564,7 @@
         hw_panel_info_.width_align, hw_panel_info_.top_align, hw_panel_info_.height_align);
   DLOGI("ROI: min_width = %d, min_height = %d, need_merge = %d", hw_panel_info_.min_roi_width,
         hw_panel_info_.min_roi_height, hw_panel_info_.needs_roi_merge);
-  DLOGI("FPS: min = %d, max =%d", hw_panel_info_.min_fps, hw_panel_info_.max_fps);
+  DLOGI("FPS: min = %d, max = %d", hw_panel_info_.min_fps, hw_panel_info_.max_fps);
   DLOGI("Left Split = %d, Right Split = %d", hw_panel_info_.split_info.left_split,
         hw_panel_info_.split_info.right_split);
   DLOGI("Panel Transfer time = %d us", hw_panel_info_.transfer_time_us);
@@ -865,6 +892,21 @@
            qos_data.clock_hz, qos_data.core_ab_bps, qos_data.core_ib_bps, qos_data.llcc_ab_bps,
            qos_data.llcc_ib_bps, qos_data.dram_ab_bps, qos_data.dram_ib_bps,
            qos_data.rot_clock_hz);
+
+  // Set refresh rate
+  if (vrefresh_) {
+    drmModeModeInfo mode = {};
+    drmModeModeInfo current_mode = connector_info_.modes[current_mode_index_];
+    for (uint32_t mode_index = 0; mode_index < connector_info_.modes.size(); mode_index++) {
+      if ((current_mode.vdisplay == connector_info_.modes[mode_index].vdisplay) &&
+          (current_mode.hdisplay == connector_info_.modes[mode_index].hdisplay) &&
+          (vrefresh_ == connector_info_.modes[mode_index].vrefresh)) {
+        mode = connector_info_.modes[mode_index];
+        break;
+      }
+    }
+    drm_atomic_intf_->Perform(DRMOps::CRTC_SET_MODE, token_.crtc_id, &mode);
+  }
 }
 
 void HWDeviceDRM::AddSolidfillStage(const HWSolidfillStage &sf, uint32_t plane_alpha) {
@@ -910,6 +952,7 @@
   int ret = drm_atomic_intf_->Validate();
   if (ret) {
     DLOGE("%s failed with error %d", __FUNCTION__, ret);
+    vrefresh_ = 0;
     return kErrorHardware;
   }
 
@@ -989,6 +1032,7 @@
   int ret = drm_atomic_intf_->Commit(false /* synchronous */, false /* retain_planes*/);
   if (ret) {
     DLOGE("%s failed with error %d crtc %d", __FUNCTION__, ret, token_.crtc_id);
+    vrefresh_ = 0;
     return kErrorHardware;
   }
 
@@ -1014,6 +1058,20 @@
 
   hw_layer_info.sync_handle = release_fence;
 
+  if (vrefresh_) {
+    // Update current mode index if refresh rate is changed
+    drmModeModeInfo current_mode = connector_info_.modes[current_mode_index_];
+    for (uint32_t mode_index = 0; mode_index < connector_info_.modes.size(); mode_index++) {
+      if ((current_mode.vdisplay == connector_info_.modes[mode_index].vdisplay) &&
+          (current_mode.hdisplay == connector_info_.modes[mode_index].hdisplay) &&
+          (vrefresh_ == connector_info_.modes[mode_index].vrefresh)) {
+        current_mode_index_ = mode_index;
+        break;
+      }
+    }
+    vrefresh_ = 0;
+  }
+
   return kErrorNone;
 }
 
@@ -1181,6 +1239,17 @@
 }
 
 DisplayError HWDeviceDRM::SetRefreshRate(uint32_t refresh_rate) {
+  // Check if requested refresh rate is valid
+  drmModeModeInfo current_mode = connector_info_.modes[current_mode_index_];
+  for (uint32_t mode_index = 0; mode_index < connector_info_.modes.size(); mode_index++) {
+    if ((current_mode.vdisplay == connector_info_.modes[mode_index].vdisplay) &&
+        (current_mode.hdisplay == connector_info_.modes[mode_index].hdisplay) &&
+        (refresh_rate == connector_info_.modes[mode_index].vrefresh)) {
+      vrefresh_ = refresh_rate;
+      DLOGV_IF(kTagDriverConfig, "Set refresh rate to %d", refresh_rate);
+      return kErrorNone;
+    }
+  }
   return kErrorNotSupported;
 }
 
diff --git a/sdm/libs/core/drm/hw_device_drm.h b/sdm/libs/core/drm/hw_device_drm.h
index cc17668..5130fc5 100644
--- a/sdm/libs/core/drm/hw_device_drm.h
+++ b/sdm/libs/core/drm/hw_device_drm.h
@@ -184,6 +184,7 @@
   std::string interface_str_ = "DSI";
   std::vector<sde_drm::DRMSolidfillStage> solid_fills_ {};
   bool resolution_switch_enabled_ = false;
+  uint32_t vrefresh_ = 0;
 };
 
 }  // namespace sdm
diff --git a/sdm/libs/hwc2/Android.mk b/sdm/libs/hwc2/Android.mk
index b451d2c..9db57ab 100644
--- a/sdm/libs/hwc2/Android.mk
+++ b/sdm/libs/hwc2/Android.mk
@@ -36,7 +36,8 @@
                                  hwc_tonemapper.cpp \
                                  display_null.cpp \
                                  hwc_socket_handler.cpp \
-                                 hwc_buffer_allocator.cpp
+                                 hwc_buffer_allocator.cpp \
+                                 hwc_display_external_test.cpp
 
 ifeq ($(TARGET_HAS_WIDE_COLOR_DISPLAY), true)
     LOCAL_CFLAGS += -DFEATURE_WIDE_COLOR
diff --git a/sdm/libs/hwc2/hwc_buffer_allocator.cpp b/sdm/libs/hwc2/hwc_buffer_allocator.cpp
index 62b64be..1c264aa 100644
--- a/sdm/libs/hwc2/hwc_buffer_allocator.cpp
+++ b/sdm/libs/hwc2/hwc_buffer_allocator.cpp
@@ -165,6 +165,8 @@
   hnd = (private_handle_t *)buf;  // NOLINT
   alloc_buffer_info->fd = hnd->fd;
   alloc_buffer_info->stride = UINT32(hnd->width);
+  alloc_buffer_info->aligned_width = UINT32(hnd->width);
+  alloc_buffer_info->aligned_height = UINT32(hnd->height);
   alloc_buffer_info->size = hnd->size;
 
   buffer_info->private_data = reinterpret_cast<void *>(hnd);
diff --git a/sdm/libs/hwc2/hwc_display.cpp b/sdm/libs/hwc2/hwc_display.cpp
index f58fe8b..af02c56 100644
--- a/sdm/libs/hwc2/hwc_display.cpp
+++ b/sdm/libs/hwc2/hwc_display.cpp
@@ -54,8 +54,6 @@
 
 namespace sdm {
 
-std::bitset<kDisplayMax> HWCDisplay::validated_ = 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;
@@ -357,7 +355,7 @@
     return -EINVAL;
   }
 
-  validated_.reset();
+  validated_ = false;
   HWCDebugHandler::Get()->GetProperty("sys.hwc_disable_hdr", &disable_hdr_handling_);
   if (disable_hdr_handling_) {
     DLOGI("HDR Handling disabled");
@@ -420,7 +418,7 @@
   layer_map_.emplace(std::make_pair(layer->GetId(), layer));
   *out_layer_id = layer->GetId();
   geometry_changes_ |= GeometryChanges::kAdded;
-  validated_.reset();
+  validated_ = false;
   return HWC2::Error::None;
 }
 
@@ -452,7 +450,7 @@
   }
 
   geometry_changes_ |= GeometryChanges::kRemoved;
-  validated_.reset();
+  validated_ = false;
   return HWC2::Error::None;
 }
 
@@ -718,7 +716,7 @@
 
   ATRACE_INT("SetPowerMode ", state);
   DisplayError error = display_intf_->SetDisplayState(state);
-  validated_.reset();
+  validated_ = false;
 
   if (error == kErrorNone) {
     flush_on_error_ = flush_on_error;
@@ -902,7 +900,7 @@
     return HWC2::Error::BadConfig;
   }
 
-  validated_.reset();
+  validated_ = false;
   return HWC2::Error::None;
 }
 
@@ -920,7 +918,7 @@
   }
 
   DLOGI("num_frame_dump %d, input_layer_dump_enable %d", dump_frame_count_, dump_input_layers_);
-  validated_.reset();
+  validated_ = false;
 }
 
 HWC2::PowerMode HWCDisplay::GetLastPowerMode() {
@@ -950,10 +948,10 @@
   switch (event) {
     case kIdleTimeout:
     case kThermalEvent:
-    case kIdlePowerCollapse:
-      HWCSession::WaitForSequence(id_);
-      validated_.reset();
-      break;
+    case kIdlePowerCollapse: {
+      SEQUENCE_WAIT_SCOPE_LOCK(HWCSession::locker_[type_]);
+      validated_ = false;
+    } break;
     default:
       DLOGW("Unknown event: %d", event);
       break;
@@ -982,7 +980,7 @@
       }
       return HWC2::Error::BadDisplay;
     } else {
-      validated_.set(type_);
+      validated_ = true;
     }
   } else {
     // Skip is not set
@@ -1029,7 +1027,7 @@
     return HWC2::Error::None;
   }
 
-  if (!validated_.test(type_)) {
+  if (!validated_) {
     return HWC2::Error::NotValidated;
   }
 
@@ -1051,7 +1049,7 @@
     return HWC2::Error::None;
   }
 
-  if (!validated_.test(type_)) {
+  if (!validated_) {
     DLOGW("Display is not validated");
     return HWC2::Error::NotValidated;
   }
@@ -1093,7 +1091,7 @@
   // Use for sharing blit buffers and
   // writing wfd buffer directly to output if there is full GPU composition
   // and no color conversion needed
-  if (!validated_.test(type_)) {
+  if (!validated_) {
     DLOGW("Display is not validated");
     return HWC2::Error::NotValidated;
   }
@@ -1153,10 +1151,10 @@
   }
 
   if (skip_validate_ && !CanSkipValidate()) {
-    validated_.reset(type_);
+    validated_ = false;
   }
 
-  if (!validated_.test(type_)) {
+  if (!validated_) {
     DLOGV_IF(kTagCompManager, "Display %d is not validated", id_);
     return HWC2::Error::NotValidated;
   }
@@ -1186,7 +1184,7 @@
         shutdown_pending_ = true;
         return HWC2::Error::Unsupported;
       } else if (error == kErrorNotValidated) {
-        validated_.reset(type_);
+        validated_ = false;
         return HWC2::Error::NotValidated;
       } else if (error != kErrorPermission) {
         DLOGE("Commit failed. Error = %d", error);
@@ -1207,7 +1205,7 @@
   // Do no call flush on errors, if a successful buffer is never submitted.
   if (flush_ && flush_on_error_) {
     display_intf_->Flush();
-    validated_.reset();
+    validated_ = false;
   }
 
   if (tone_mapper_ && tone_mapper_->IsActive()) {
@@ -1285,7 +1283,7 @@
 
   if (display_intf_) {
     error = display_intf_->SetMaxMixerStages(max_mixer_stages);
-    validated_.reset();
+    validated_ = false;
   }
 
   return error;
@@ -1648,7 +1646,7 @@
 
   if (display_status == kDisplayStatusResume || display_status == kDisplayStatusPause) {
     callbacks_->Refresh(HWC_DISPLAY_PRIMARY);
-    validated_.reset();
+    validated_ = false;
   }
 
   return status;
@@ -1666,7 +1664,7 @@
   if (hwc_layer->GetDeviceSelectedCompositionType() != HWC2::Composition::Cursor) {
     return HWC2::Error::None;
   }
-  if (!skip_validate_ && validated_.test(type_)) {
+  if (!skip_validate_ && validated_) {
     // the device is currently in the middle of the validate/present sequence,
     // cannot set the Position(as per HWC2 spec)
     return HWC2::Error::NotValidated;
@@ -1704,7 +1702,7 @@
     return -1;
   }
 
-  validated_.reset();
+  validated_ = false;
   return 0;
 }
 
@@ -1713,7 +1711,7 @@
     auto layer = hwc_layer->GetSDMLayer();
     layer->composition = kCompositionSDE;
   }
-  validated_.set(type_);
+  validated_ = true;
 }
 
 void HWCDisplay::MarkLayersForClientComposition() {
@@ -1733,7 +1731,7 @@
   int ret = 0;
   if (display_intf_) {
     ret = display_intf_->SetPanelBrightness(level);
-    validated_.reset();
+    validated_ = false;
   } else {
     ret = -EINVAL;
   }
@@ -1748,7 +1746,7 @@
 int HWCDisplay::ToggleScreenUpdates(bool enable) {
   display_paused_ = enable ? false : true;
   callbacks_->Refresh(HWC_DISPLAY_PRIMARY);
-  validated_.reset();
+  validated_ = false;
   return 0;
 }
 
@@ -1852,7 +1850,7 @@
 
 int HWCDisplay::SetActiveDisplayConfig(uint32_t config) {
   int status = (display_intf_->SetActiveConfig(config) == kErrorNone) ? 0 : -1;
-  validated_.reset();
+  validated_ = false;
   return status;
 }
 
diff --git a/sdm/libs/hwc2/hwc_display.h b/sdm/libs/hwc2/hwc_display.h
index 7eefcd1..e90dbdd 100644
--- a/sdm/libs/hwc2/hwc_display.h
+++ b/sdm/libs/hwc2/hwc_display.h
@@ -161,7 +161,7 @@
   void BuildLayerStack(void);
   void BuildSolidFillStack(void);
   HWCLayer *GetHWCLayer(hwc2_layer_t layer);
-  void ResetValidation() { validated_.reset(); }
+  void ResetValidation() { validated_ = false; }
   uint32_t GetGeometryChanges() { return geometry_changes_; }
 
   // HWC2 APIs
@@ -249,7 +249,7 @@
     OUTPUT_LAYER_DUMP,
   };
 
-  static std::bitset<kDisplayMax> validated_;
+  bool validated_ = false;
   CoreInterface *core_intf_ = nullptr;
   HWCCallbacks *callbacks_  = nullptr;
   HWCBufferAllocator *buffer_allocator_ = NULL;
diff --git a/sdm/libs/hwc2/hwc_display_external.cpp b/sdm/libs/hwc2/hwc_display_external.cpp
index 97507ee..cfeb8a4 100644
--- a/sdm/libs/hwc2/hwc_display_external.cpp
+++ b/sdm/libs/hwc2/hwc_display_external.cpp
@@ -180,7 +180,7 @@
 
     if (secure_display_active_) {
       DisplayError error = display_intf_->Flush();
-      validated_.reset();
+      validated_ = false;
       if (error != kErrorNone) {
         DLOGE("Flush failed. Error = %d", error);
       }
@@ -234,7 +234,7 @@
 
       display_null_.GetDisplayState(&state);
       display_intf_->SetDisplayState(state);
-      validated_.reset();
+      validated_ = false;
 
       SetVsyncEnabled(HWC2::Vsync::Enable);
 
diff --git a/sdm/libs/hwc2/hwc_display_external_test.cpp b/sdm/libs/hwc2/hwc_display_external_test.cpp
new file mode 100644
index 0000000..8551854
--- /dev/null
+++ b/sdm/libs/hwc2/hwc_display_external_test.cpp
@@ -0,0 +1,751 @@
+/*
+* 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 <cutils/properties.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <utils/constants.h>
+#include <utils/debug.h>
+#include <utils/formats.h>
+#include <algorithm>
+#include <array>
+#include <sstream>
+#include <string>
+#include <fstream>
+
+#include "hwc_display_external_test.h"
+#include "hwc_debugger.h"
+
+#define __CLASS__ "HWCDisplayExternalTest"
+
+namespace sdm {
+
+using std::array;
+
+int HWCDisplayExternalTest::Create(CoreInterface *core_intf,
+                                   HWCBufferAllocator *buffer_allocator,
+                                   HWCCallbacks *callbacks,
+                                   qService::QService *qservice, uint32_t panel_bpp,
+                                   uint32_t pattern_type, HWCDisplay **hwc_display) {
+  HWCDisplay *hwc_external_test = new HWCDisplayExternalTest(core_intf, buffer_allocator,
+                                                             callbacks, qservice,
+                                                             panel_bpp, pattern_type);
+
+  int status = static_cast<HWCDisplayExternalTest *>(hwc_external_test)->Init();
+  if (status) {
+    delete hwc_external_test;
+    return status;
+  }
+
+  *hwc_display = hwc_external_test;
+
+  DLOGE("EXTERNAL panel_bpp %d, pattern_type %d", panel_bpp, pattern_type);
+
+  return status;
+}
+
+void HWCDisplayExternalTest::Destroy(HWCDisplay *hwc_display) {
+  static_cast<HWCDisplayExternalTest *>(hwc_display)->Deinit();
+
+  delete hwc_display;
+}
+
+HWCDisplayExternalTest::HWCDisplayExternalTest(CoreInterface *core_intf,
+                                               HWCBufferAllocator *buffer_allocator,
+                                               HWCCallbacks *callbacks,
+                                               qService::QService *qservice, uint32_t panel_bpp,
+                                               uint32_t pattern_type)
+  : HWCDisplay(core_intf, callbacks, kHDMI, HWC_DISPLAY_EXTERNAL, false, qservice,
+               DISPLAY_CLASS_EXTERNAL, buffer_allocator), panel_bpp_(panel_bpp),
+               pattern_type_(pattern_type) {
+}
+
+int HWCDisplayExternalTest::Init() {
+  uint32_t external_width = 0;
+  uint32_t external_height = 0;
+
+  int status = HWCDisplay::Init();
+  if (status) {
+      DLOGE("HWCDisplayExternalTest::Init  status = %d ", status);
+    return status;
+  }
+
+  status = CreateLayerStack();
+  if (status) {
+    Deinit();
+    return status;
+  }
+
+  DisplayError error = HWCDisplay::GetMixerResolution(&external_width, &external_height);
+  if (error != kErrorNone) {
+    Deinit();
+    return -EINVAL;
+  }
+
+  status = HWCDisplay::SetFrameBufferResolution(external_width, external_height);
+  if (status) {
+    Deinit();
+    DLOGE("HWCDisplayExternalTest:: set fb resolution status = %d ", status);
+    return status;
+  }
+
+  return status;
+}
+
+int HWCDisplayExternalTest::Deinit() {
+  DestroyLayerStack();
+  return HWCDisplay::Deinit();
+}
+
+
+HWC2::Error HWCDisplayExternalTest::Validate(uint32_t *out_num_types, uint32_t *out_num_requests) {
+  auto status = HWC2::Error::None;
+  if (secure_display_active_) {
+    MarkLayersForGPUBypass();
+    return status;
+  }
+
+  if (layer_set_.empty()) {
+      flush_ = true;
+      return status;
+  }
+
+  if (shutdown_pending_) {
+    return status;
+  }
+  DisplayError error = display_intf_->Prepare(&layer_stack_);
+  if (error != kErrorNone) {
+    if (error == kErrorShutDown) {
+      shutdown_pending_ = true;
+    } else if (error != kErrorPermission) {
+      DLOGE("Prepare failed. Error = %d", error);
+      // To prevent surfaceflinger infinite wait, flush the previous frame during Commit()
+      // so that previous buffer and fences are released, and override the error.
+      flush_ = true;
+    }
+  }
+
+  MarkLayersForGPUBypass();
+
+  return  status;
+}
+
+HWC2::Error HWCDisplayExternalTest::Present(int32_t *out_retire_fence) {
+  auto status = HWC2::Error::None;
+
+  if (secure_display_active_) {
+    return status;
+  }
+
+  if (shutdown_pending_) {
+    return status;
+  }
+
+  DumpInputBuffer();
+
+  if (!flush_) {
+    DisplayError error = kErrorUndefined;
+    error = display_intf_->Commit(&layer_stack_);
+    if (error == kErrorNone) {
+      // A commit is successfully submitted, start flushing on failure now onwards.
+      flush_on_error_ = true;
+    } else if (error == kErrorShutDown) {
+      shutdown_pending_ = true;
+      status = HWC2::Error::Unsupported;
+    } else if (error == kErrorNotValidated) {
+      status = HWC2::Error::NotValidated;
+    } else if (error != kErrorPermission) {
+      DLOGE("Commit failed. Error = %d", error);
+        // To prevent surfaceflinger infinite wait, flush the previous frame during Commit()
+        // so that previous buffer and fences are released, and override the error.
+        flush_ = true;
+    }
+  }
+  PostCommit(out_retire_fence);
+  return status;
+}
+
+void HWCDisplayExternalTest::SetSecureDisplay(bool secure_display_active) {
+  if (secure_display_active_ != secure_display_active) {
+    secure_display_active_ = secure_display_active;
+
+    if (secure_display_active_) {
+      DisplayError error = display_intf_->Flush();
+      if (error != kErrorNone) {
+        DLOGE("Flush failed. Error = %d", error);
+      }
+    }
+  }
+  return;
+}
+
+int HWCDisplayExternalTest::Perform(uint32_t operation, ...) {
+  return 0;
+}
+
+void HWCDisplayExternalTest::DumpInputBuffer() {
+  if (!dump_frame_count_ || flush_ || !dump_input_layers_) {
+    return;
+  }
+
+  const char *dir_path = "/data/vendor/display/frame_dump_external";
+  uint32_t width = buffer_info_.alloc_buffer_info.aligned_width;
+  uint32_t height = buffer_info_.alloc_buffer_info.aligned_height;
+  string format_str = GetFormatString(buffer_info_.buffer_config.format);
+
+  char *buffer = reinterpret_cast<char *>(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) {
+    DLOGW("mmap failed. err = %d", errno);
+    return;
+  }
+
+  if (mkdir(dir_path, 0777) != 0 && errno != EEXIST) {
+    DLOGW("Failed to create %s directory errno = %d, desc = %s", dir_path, errno, strerror(errno));
+    return;
+  }
+
+  // if directory exists already, need to explicitly change the permission.
+  if (errno == EEXIST && chmod(dir_path, 0777) != 0) {
+    DLOGW("Failed to change permissions on %s directory", dir_path);
+    return;
+  }
+
+  if (buffer) {
+    std::stringstream dump_file_name;
+    dump_file_name << dir_path;
+    dump_file_name << "/input_layer_" << width << "x" << height << "_" << format_str << ".raw";
+
+    std::fstream fs;
+    fs.open(dump_file_name.str().c_str(), std::fstream::in | std::fstream::out | std::fstream::app);
+    if (!fs.is_open()) {
+      DLOGI("File open failed %s", dump_file_name.str().c_str());
+      return;
+    }
+
+    fs.write(buffer, (std::streamsize)buffer_info_.alloc_buffer_info.size);
+    fs.close();
+
+    DLOGI("Frame Dump %s: is successful", dump_file_name.str().c_str());
+  }
+
+  // Dump only once as the content is going to be same for all draw cycles
+  if (dump_frame_count_) {
+    dump_frame_count_ = 0;
+  }
+
+  if (munmap(buffer, buffer_info_.alloc_buffer_info.size) != 0) {
+    DLOGW("munmap failed. err = %d", errno);
+    return;
+  }
+}
+
+void HWCDisplayExternalTest::CalcCRC(uint32_t color_val, std::bitset<16> *crc_data) {
+  std::bitset<16> color = {};
+  std::bitset<16> temp_crc = {};
+
+  switch (panel_bpp_) {
+    case kDisplayBpp18:
+      color = (color_val & 0xFC) << 8;
+      break;
+    case kDisplayBpp24:
+      color = color_val << 8;
+      break;
+    case kDisplayBpp30:
+      color = color_val << 6;
+      break;
+    default:
+      return;
+  }
+
+  temp_crc[15] = (*crc_data)[0] ^ (*crc_data)[1] ^ (*crc_data)[2] ^ (*crc_data)[3] ^
+                 (*crc_data)[4] ^ (*crc_data)[5] ^ (*crc_data)[6] ^ (*crc_data)[7] ^
+                 (*crc_data)[8] ^ (*crc_data)[9] ^ (*crc_data)[10] ^ (*crc_data)[11] ^
+                 (*crc_data)[12] ^ (*crc_data)[14] ^ (*crc_data)[15] ^ color[0] ^ color[1] ^
+                 color[2] ^ color[3] ^ color[4] ^ color[5] ^ color[6] ^ color[7] ^ color[8] ^
+                 color[9] ^ color[10] ^ color[11] ^ color[12] ^ color[14] ^ color[15];
+
+  temp_crc[14] = (*crc_data)[12] ^ (*crc_data)[13] ^ color[12] ^ color[13];
+  temp_crc[13] = (*crc_data)[11] ^ (*crc_data)[12] ^ color[11] ^ color[12];
+  temp_crc[12] = (*crc_data)[10] ^ (*crc_data)[11] ^ color[10] ^ color[11];
+  temp_crc[11] = (*crc_data)[9] ^ (*crc_data)[10] ^ color[9] ^ color[10];
+  temp_crc[10] = (*crc_data)[8] ^ (*crc_data)[9] ^ color[8] ^ color[9];
+  temp_crc[9] = (*crc_data)[7] ^ (*crc_data)[8] ^ color[7] ^ color[8];
+  temp_crc[8] = (*crc_data)[6] ^ (*crc_data)[7] ^ color[6] ^ color[7];
+  temp_crc[7] = (*crc_data)[5] ^ (*crc_data)[6] ^ color[5] ^ color[6];
+  temp_crc[6] = (*crc_data)[4] ^ (*crc_data)[5] ^ color[4] ^ color[5];
+  temp_crc[5] = (*crc_data)[3] ^ (*crc_data)[4] ^ color[3] ^ color[4];
+  temp_crc[4] = (*crc_data)[2] ^ (*crc_data)[3] ^ color[2] ^ color[3];
+  temp_crc[3] = (*crc_data)[1] ^ (*crc_data)[2] ^ (*crc_data)[15] ^ color[1] ^ color[2] ^ color[15];
+  temp_crc[2] = (*crc_data)[0] ^ (*crc_data)[1] ^ (*crc_data)[14] ^ color[0] ^ color[1] ^ color[14];
+
+  temp_crc[1] = (*crc_data)[1] ^ (*crc_data)[2] ^ (*crc_data)[3] ^ (*crc_data)[4] ^ (*crc_data)[5] ^
+                (*crc_data)[6] ^ (*crc_data)[7] ^ (*crc_data)[8] ^ (*crc_data)[9] ^
+                (*crc_data)[10] ^ (*crc_data)[11] ^ (*crc_data)[12] ^ (*crc_data)[13] ^
+                (*crc_data)[14] ^ color[1] ^ color[2] ^ color[3] ^ color[4] ^ color[5] ^ color[6] ^
+                color[7] ^ color[8] ^ color[9] ^ color[10] ^ color[11] ^ color[12] ^ color[13] ^
+                color[14];
+
+  temp_crc[0] = (*crc_data)[0] ^ (*crc_data)[1] ^ (*crc_data)[2] ^ (*crc_data)[3] ^ (*crc_data)[4] ^
+                (*crc_data)[5] ^ (*crc_data)[6] ^ (*crc_data)[7] ^ (*crc_data)[8] ^ (*crc_data)[9] ^
+                (*crc_data)[10] ^ (*crc_data)[11] ^ (*crc_data)[12] ^ (*crc_data)[13] ^
+                (*crc_data)[15] ^ color[0] ^ color[1] ^ color[2] ^ color[3] ^ color[4] ^ color[5] ^
+                color[6] ^ color[7] ^ color[8] ^ color[9] ^ color[10] ^ color[11] ^ color[12] ^
+                color[13] ^ color[15];
+
+  (*crc_data) = temp_crc;
+}
+
+int HWCDisplayExternalTest::FillBuffer() {
+  uint8_t *buffer = reinterpret_cast<uint8_t *>(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);
+    return -EFAULT;
+  }
+
+  switch (pattern_type_) {
+    case kPatternColorRamp:
+      GenerateColorRamp(buffer);
+      break;
+    case kPatternBWVertical:
+      GenerateBWVertical(buffer);
+      break;
+    case kPatternColorSquare:
+      GenerateColorSquare(buffer);
+      break;
+    default:
+      DLOGW("Invalid Pattern type %d", pattern_type_);
+      return -EINVAL;
+  }
+
+  if (munmap(buffer, buffer_info_.alloc_buffer_info.size) != 0) {
+    DLOGE("munmap failed. err = %d", errno);
+    return -EFAULT;
+  }
+
+  return 0;
+}
+
+int HWCDisplayExternalTest::GetStride(LayerBufferFormat format, uint32_t width, uint32_t *stride) {
+  switch (format) {
+  case kFormatRGBA8888:
+  case kFormatRGBA1010102:
+    *stride = width * 4;
+    break;
+  case kFormatRGB888:
+    *stride = width * 3;
+    break;
+  default:
+    DLOGE("Unsupported format type %d", format);
+    return -EINVAL;
+  }
+
+  return 0;
+}
+
+void HWCDisplayExternalTest::PixelCopy(uint32_t red, uint32_t green, uint32_t blue, uint32_t alpha,
+                                       uint8_t **buffer) {
+  LayerBufferFormat format = buffer_info_.buffer_config.format;
+
+  switch (format) {
+    case kFormatRGBA8888:
+      *(*buffer)++ = UINT8(red & 0xFF);
+      *(*buffer)++ = UINT8(green & 0xFF);
+      *(*buffer)++ = UINT8(blue & 0xFF);
+      *(*buffer)++ = UINT8(alpha & 0xFF);
+      break;
+    case kFormatRGB888:
+      *(*buffer)++ = UINT8(red & 0xFF);
+      *(*buffer)++ = UINT8(green & 0xFF);
+      *(*buffer)++ = UINT8(blue & 0xFF);
+      break;
+    case kFormatRGBA1010102:
+      // Lower 8 bits of red
+      *(*buffer)++ = UINT8(red & 0xFF);
+
+      // Upper 2 bits of Red + Lower 6 bits of green
+      *(*buffer)++ = UINT8(((green & 0x3F) << 2) | ((red >> 0x8) & 0x3));
+
+      // Upper 4 bits of green + Lower 4 bits of blue
+      *(*buffer)++ = UINT8(((blue & 0xF) << 4) | ((green >> 6) & 0xF));
+
+      // Upper 6 bits of blue + Lower 2 bits of alpha
+      *(*buffer)++ = UINT8(((alpha & 0x3) << 6) | ((blue >> 4) & 0x3F));
+      break;
+    default:
+      DLOGW("format not supported format = %d", format);
+      break;
+  }
+}
+
+void HWCDisplayExternalTest::GenerateColorRamp(uint8_t *buffer) {
+  uint32_t width = buffer_info_.buffer_config.width;
+  uint32_t height = buffer_info_.buffer_config.height;
+  LayerBufferFormat format = buffer_info_.buffer_config.format;
+  uint32_t aligned_width = buffer_info_.alloc_buffer_info.aligned_width;
+  uint32_t buffer_stride = 0;
+
+  uint32_t color_ramp = 0;
+  uint32_t start_color_val = 0;
+  uint32_t step_size = 1;
+  uint32_t ramp_width = 0;
+  uint32_t ramp_height = 0;
+  uint32_t shift_by = 0;
+
+  std::bitset<16> crc_red = {};
+  std::bitset<16> crc_green = {};
+  std::bitset<16> crc_blue = {};
+
+  switch (panel_bpp_) {
+    case kDisplayBpp18:
+      ramp_height = 64;
+      ramp_width = 64;
+      shift_by = 2;
+      break;
+    case kDisplayBpp24:
+      ramp_height = 64;
+      ramp_width = 256;
+      break;
+    case kDisplayBpp30:
+      ramp_height = 32;
+      ramp_width = 256;
+      start_color_val = 0x180;
+      break;
+    default:
+      return;
+  }
+
+  GetStride(format, aligned_width, &buffer_stride);
+
+  for (uint32_t loop_height = 0; loop_height < height; loop_height++) {
+    uint32_t color_value = start_color_val;
+    uint8_t *temp = buffer + (loop_height * buffer_stride);
+
+    for (uint32_t loop_width = 0; loop_width < width; loop_width++) {
+      if (color_ramp == kColorRedRamp) {
+        PixelCopy(color_value, 0, 0, 0, &temp);
+        CalcCRC(color_value, &crc_red);
+        CalcCRC(0, &crc_green);
+        CalcCRC(0, &crc_blue);
+      }
+      if (color_ramp == kColorGreenRamp) {
+        PixelCopy(0, color_value, 0, 0, &temp);
+        CalcCRC(0, &crc_red);
+        CalcCRC(color_value, &crc_green);
+        CalcCRC(0, &crc_blue);
+      }
+      if (color_ramp == kColorBlueRamp) {
+        PixelCopy(0, 0, color_value, 0, &temp);
+        CalcCRC(0, &crc_red);
+        CalcCRC(0, &crc_green);
+        CalcCRC(color_value, &crc_blue);
+      }
+      if (color_ramp == kColorWhiteRamp) {
+        PixelCopy(color_value, color_value, color_value, 0, &temp);
+        CalcCRC(color_value, &crc_red);
+        CalcCRC(color_value, &crc_green);
+        CalcCRC(color_value, &crc_blue);
+      }
+
+      color_value = (start_color_val + (((loop_width + 1) % ramp_width) * step_size)) << shift_by;
+    }
+
+    if (panel_bpp_ == kDisplayBpp30 && ((loop_height + 1) % ramp_height) == 0) {
+      if (start_color_val == 0x180) {
+        start_color_val = 0;
+        step_size = 4;
+      } else {
+        start_color_val = 0x180;
+        step_size = 1;
+        color_ramp = (color_ramp + 1) % 4;
+      }
+      continue;
+    }
+
+    if (((loop_height + 1) % ramp_height) == 0) {
+      color_ramp = (color_ramp + 1) % 4;
+    }
+  }
+
+  DLOGI("CRC red %x", crc_red.to_ulong());
+  DLOGI("CRC green %x", crc_green.to_ulong());
+  DLOGI("CRC blue %x", crc_blue.to_ulong());
+}
+
+void HWCDisplayExternalTest::GenerateBWVertical(uint8_t *buffer) {
+  uint32_t width = buffer_info_.buffer_config.width;
+  uint32_t height = buffer_info_.buffer_config.height;
+  LayerBufferFormat format = buffer_info_.buffer_config.format;
+  uint32_t aligned_width = buffer_info_.alloc_buffer_info.aligned_width;
+  uint32_t buffer_stride = 0;
+  uint32_t bits_per_component = panel_bpp_ / 3;
+  uint32_t max_color_val = (1 << bits_per_component) - 1;
+
+  std::bitset<16> crc_red = {};
+  std::bitset<16> crc_green = {};
+  std::bitset<16> crc_blue = {};
+
+  if (panel_bpp_ == kDisplayBpp18) {
+    max_color_val <<= 2;
+  }
+
+  GetStride(format, aligned_width, &buffer_stride);
+
+  for (uint32_t loop_height = 0; loop_height < height; loop_height++) {
+    uint32_t color = 0;
+    uint8_t *temp = buffer + (loop_height * buffer_stride);
+
+    for (uint32_t loop_width = 0; loop_width < width; loop_width++) {
+      if (color == kColorBlack) {
+        PixelCopy(0, 0, 0, 0, &temp);
+        CalcCRC(0, &crc_red);
+        CalcCRC(0, &crc_green);
+        CalcCRC(0, &crc_blue);
+      }
+      if (color == kColorWhite) {
+        PixelCopy(max_color_val, max_color_val, max_color_val, 0, &temp);
+        CalcCRC(max_color_val, &crc_red);
+        CalcCRC(max_color_val, &crc_green);
+        CalcCRC(max_color_val, &crc_blue);
+      }
+
+      color = (color + 1) % 2;
+    }
+  }
+
+  DLOGI("CRC red %x", crc_red.to_ulong());
+  DLOGI("CRC green %x", crc_green.to_ulong());
+  DLOGI("CRC blue %x", crc_blue.to_ulong());
+}
+
+void HWCDisplayExternalTest::GenerateColorSquare(uint8_t *buffer) {
+  uint32_t width = buffer_info_.buffer_config.width;
+  uint32_t height = buffer_info_.buffer_config.height;
+  LayerBufferFormat format = buffer_info_.buffer_config.format;
+  uint32_t aligned_width = buffer_info_.alloc_buffer_info.aligned_width;
+  uint32_t buffer_stride = 0;
+  uint32_t max_color_val = 0;
+  uint32_t min_color_val = 0;
+
+  std::bitset<16> crc_red = {};
+  std::bitset<16> crc_green = {};
+  std::bitset<16> crc_blue = {};
+
+  switch (panel_bpp_) {
+    case kDisplayBpp18:
+      max_color_val = 63 << 2;  // CEA Dynamic range for 18bpp 0 - 63
+      min_color_val = 0;
+      break;
+    case kDisplayBpp24:
+      max_color_val = 235;  // CEA Dynamic range for 24bpp 16 - 235
+      min_color_val = 16;
+      break;
+    case kDisplayBpp30:
+      max_color_val = 940;  // CEA Dynamic range for 30bpp 64 - 940
+      min_color_val = 64;
+      break;
+    default:
+      return;
+  }
+
+  array<array<uint32_t, 3>, 8> colors = {{
+    {{max_color_val, max_color_val, max_color_val}},  // White Color
+    {{max_color_val, max_color_val, min_color_val}},  // Yellow Color
+    {{min_color_val, max_color_val, max_color_val}},  // Cyan Color
+    {{min_color_val, max_color_val, min_color_val}},  // Green Color
+    {{max_color_val, min_color_val, max_color_val}},  // Megenta Color
+    {{max_color_val, min_color_val, min_color_val}},  // Red Color
+    {{min_color_val, min_color_val, max_color_val}},  // Blue Color
+    {{min_color_val, min_color_val, min_color_val}},  // Black Color
+  }};
+
+  GetStride(format, aligned_width, &buffer_stride);
+
+  for (uint32_t loop_height = 0; loop_height < height; loop_height++) {
+    uint32_t color = 0;
+    uint8_t *temp = buffer + (loop_height * buffer_stride);
+
+    for (uint32_t loop_width = 0; loop_width < width; loop_width++) {
+      PixelCopy(colors[color][0], colors[color][1], colors[color][2], 0, &temp);
+      CalcCRC(colors[color][0], &crc_red);
+      CalcCRC(colors[color][1], &crc_green);
+      CalcCRC(colors[color][2], &crc_blue);
+
+      if (((loop_width + 1) % 64) == 0) {
+        color = (color + 1) % colors.size();
+      }
+    }
+
+    if (((loop_height + 1) % 64) == 0) {
+      std::reverse(colors.begin(), (colors.end() - 1));
+    }
+  }
+
+  DLOGI("CRC red %x", crc_red.to_ulong());
+  DLOGI("CRC green %x", crc_green.to_ulong());
+  DLOGI("CRC blue %x", crc_blue.to_ulong());
+}
+
+int HWCDisplayExternalTest::InitLayer(Layer *layer) {
+  uint32_t active_config = 0;
+  DisplayConfigVariableInfo var_info = {};
+
+  GetActiveDisplayConfig(&active_config);
+
+  GetDisplayAttributesForConfig(INT32(active_config), &var_info);
+
+  layer->flags.updating = 1;
+  layer->src_rect = LayerRect(0, 0, var_info.x_pixels, var_info.y_pixels);
+  layer->dst_rect = layer->src_rect;
+  layer->frame_rate = var_info.fps;
+  layer->blending = kBlendingPremultiplied;
+
+  layer->input_buffer.unaligned_width = var_info.x_pixels;
+  layer->input_buffer.unaligned_height = var_info.y_pixels;
+  buffer_info_.buffer_config.format = kFormatRGBA8888;
+
+  if (layer->composition != kCompositionGPUTarget) {
+    buffer_info_.buffer_config.width = var_info.x_pixels;
+    buffer_info_.buffer_config.height = var_info.y_pixels;
+    switch (panel_bpp_) {
+      case kDisplayBpp18:
+      case kDisplayBpp24:
+        buffer_info_.buffer_config.format = kFormatRGB888;
+        break;
+      case kDisplayBpp30:
+        buffer_info_.buffer_config.format = kFormatRGBA1010102;
+        break;
+      default:
+        DLOGW("panel bpp not supported %d", panel_bpp_);
+        return -EINVAL;
+    }
+    buffer_info_.buffer_config.buffer_count = 1;
+
+    int ret = buffer_allocator_->AllocateBuffer(&buffer_info_);
+    if (ret != 0) {
+      DLOGE("Buffer allocation failed. ret: %d", ret);
+      return -ENOMEM;
+    }
+
+    ret = FillBuffer();
+    if (ret != 0) {
+      buffer_allocator_->FreeBuffer(&buffer_info_);
+      return ret;
+    }
+
+    layer->input_buffer.width = buffer_info_.alloc_buffer_info.aligned_width;
+    layer->input_buffer.height = buffer_info_.alloc_buffer_info.aligned_height;
+    layer->input_buffer.size = buffer_info_.alloc_buffer_info.size;
+    layer->input_buffer.planes[0].fd = buffer_info_.alloc_buffer_info.fd;
+    layer->input_buffer.planes[0].stride = buffer_info_.alloc_buffer_info.stride;
+    layer->input_buffer.format = buffer_info_.buffer_config.format;
+
+    DLOGI("Input buffer WxH %dx%d format %s size %d fd %d stride %d", layer->input_buffer.width,
+          layer->input_buffer.height, GetFormatString(layer->input_buffer.format),
+          layer->input_buffer.size, layer->input_buffer.planes[0].fd,
+          layer->input_buffer.planes[0].stride);
+  }
+
+  return 0;
+}
+
+int HWCDisplayExternalTest::DeinitLayer(Layer *layer) {
+  if (layer->composition != kCompositionGPUTarget) {
+    int ret = buffer_allocator_->FreeBuffer(&buffer_info_);
+    if (ret != 0) {
+      DLOGE("Buffer deallocation failed. ret: %d", ret);
+      return -ENOMEM;
+    }
+  }
+
+  return 0;
+}
+
+int HWCDisplayExternalTest::CreateLayerStack() {
+  for (uint32_t i = 0; i < (kTestLayerCnt + 1 /* one dummy gpu_target layer */); i++) {
+    Layer *layer = new Layer();
+
+    if (i == kTestLayerCnt) {
+      layer->composition = kCompositionGPUTarget;
+    }
+    DLOGE("External :: CreateLayerStack %d", i);
+    int ret = InitLayer(layer);
+    if (ret != 0) {
+      delete layer;
+      return ret;
+    }
+    layer_stack_.layers.push_back(layer);
+  }
+
+  return 0;
+}
+
+int HWCDisplayExternalTest::DestroyLayerStack() {
+  for (uint32_t i = 0; i < UINT32(layer_stack_.layers.size()); i++) {
+    Layer *layer = layer_stack_.layers.at(i);
+    int ret = DeinitLayer(layer);
+    if (ret != 0) {
+      return ret;
+    }
+    delete layer;
+  }
+  layer_stack_.layers = {};
+
+  return 0;
+}
+
+HWC2::Error HWCDisplayExternalTest::PostCommit(int32_t *out_retire_fence) {
+  auto status = HWC2::Error::None;
+  // Do no call flush on errors, if a successful buffer is never submitted.
+  if (flush_ && flush_on_error_) {
+    display_intf_->Flush();
+  }
+  if (!flush_) {
+    for (size_t i = 0; i < layer_stack_.layers.size(); i++) {
+      Layer *layer = layer_stack_.layers.at(i);
+      LayerBuffer &layer_buffer = layer->input_buffer;
+
+      close(layer_buffer.release_fence_fd);
+      layer_buffer.release_fence_fd = -1;
+    }
+    close(layer_stack_.retire_fence_fd);
+    layer_stack_.retire_fence_fd = -1;
+    *out_retire_fence = -1;
+  }
+  flush_ = false;
+
+  return status;
+}
+
+}  // namespace sdm
+
diff --git a/sdm/libs/hwc2/hwc_display_external_test.h b/sdm/libs/hwc2/hwc_display_external_test.h
new file mode 100644
index 0000000..ef8027b
--- /dev/null
+++ b/sdm/libs/hwc2/hwc_display_external_test.h
@@ -0,0 +1,107 @@
+/*
+* 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 __HWC_DISPLAY_EXTERNAL_TEST_H__
+#define __HWC_DISPLAY_EXTERNAL_TEST_H__
+
+#include <bitset>
+
+#include "hwc_display.h"
+#include "hwc_buffer_allocator.h"
+
+namespace sdm {
+
+class HWCDisplayExternalTest : public HWCDisplay {
+ public:
+  static int Create(CoreInterface *core_intf, HWCBufferAllocator *buffer_allocator,
+                    HWCCallbacks *callbacks, qService::QService *qservice,
+                    uint32_t panel_bpp, uint32_t pattern_type, HWCDisplay **hwc_display);
+  static void Destroy(HWCDisplay *hwc_display);
+  virtual HWC2::Error Validate(uint32_t *out_num_types, uint32_t *out_num_requests);
+  virtual HWC2::Error Present(int32_t *out_retire_fence);
+  virtual void SetSecureDisplay(bool secure_display_active);
+  virtual int Perform(uint32_t operation, ...);
+
+ protected:
+  BufferInfo buffer_info_ = {};
+  uint32_t panel_bpp_ = 0;
+  uint32_t pattern_type_ = 0;
+
+  enum ColorPatternType {
+    kPatternNone = 0,
+    kPatternColorRamp,
+    kPatternBWVertical,
+    kPatternColorSquare,
+  };
+
+  enum DisplayBpp {
+    kDisplayBpp18 = 18,
+    kDisplayBpp24 = 24,
+    kDisplayBpp30 = 30,
+  };
+
+  enum ColorRamp {
+    kColorRedRamp = 0,
+    kColorGreenRamp = 1,
+    kColorBlueRamp = 2,
+    kColorWhiteRamp = 3,
+  };
+
+  enum Colors {
+    kColorBlack = 0,
+    kColorWhite = 1,
+  };
+
+ private:
+  HWCDisplayExternalTest(CoreInterface *core_intf, HWCBufferAllocator *buffer_allocator,
+                         HWCCallbacks *callbacks, qService::QService *qservice,
+                         uint32_t panel_bpp, uint32_t pattern_type);
+  int Init();
+  int Deinit();
+  void DumpInputBuffer();
+  void CalcCRC(uint32_t color_value, std::bitset<16> *crc_data);
+  int FillBuffer();
+  int GetStride(LayerBufferFormat format, uint32_t width, uint32_t *stride);
+  void PixelCopy(uint32_t red, uint32_t green, uint32_t blue, uint32_t alpha, uint8_t **buffer);
+  void GenerateColorRamp(uint8_t *buffer);
+  void GenerateBWVertical(uint8_t *buffer);
+  void GenerateColorSquare(uint8_t *buffer);
+  int InitLayer(Layer *layer);
+  int DeinitLayer(Layer *layer);
+  int CreateLayerStack();
+  int DestroyLayerStack();
+  HWC2::Error PostCommit(int32_t *out_retire_fence);
+
+  static const uint32_t kTestLayerCnt = 1;
+};
+
+}  // namespace sdm
+
+#endif  // __HWC_DISPLAY_EXTERNAL_TEST_H__
+
diff --git a/sdm/libs/hwc2/hwc_display_primary.cpp b/sdm/libs/hwc2/hwc_display_primary.cpp
index d4917f6..71f6b4e 100644
--- a/sdm/libs/hwc2/hwc_display_primary.cpp
+++ b/sdm/libs/hwc2/hwc_display_primary.cpp
@@ -220,7 +220,7 @@
     // If we do not handle the frame set retireFenceFd to outbufAcquireFenceFd
     // Revisit this when validating display_paused
     DisplayError error = display_intf_->Flush();
-    validated_.reset();
+    validated_ = false;
     if (error != kErrorNone) {
       DLOGE("Flush failed. Error = %d", error);
     }
@@ -256,7 +256,7 @@
   }
 
   callbacks_->Refresh(HWC_DISPLAY_PRIMARY);
-  validated_.reset();
+  validated_ = false;
 
   return status;
 }
@@ -269,7 +269,7 @@
   }
 
   callbacks_->Refresh(HWC_DISPLAY_PRIMARY);
-  validated_.reset();
+  validated_ = false;
 
   return status;
 }
@@ -289,7 +289,7 @@
 
   callbacks_->Refresh(HWC_DISPLAY_PRIMARY);
   color_tranform_failed_ = false;
-  validated_.reset();
+  validated_ = false;
 
   return status;
 }
@@ -332,7 +332,7 @@
       return -EINVAL;
   }
   va_end(args);
-  validated_.reset();
+  validated_ = false;
 
   return 0;
 }
@@ -419,7 +419,7 @@
 
 void HWCDisplayPrimary::SetIdleTimeoutMs(uint32_t timeout_ms) {
   display_intf_->SetIdleTimeoutMs(timeout_ms);
-  validated_.reset();
+  validated_ = false;
 }
 
 static void SetLayerBuffer(const BufferInfo &output_buffer_info, LayerBuffer *output_buffer) {
@@ -519,7 +519,7 @@
   output_buffer_base_ = buffer;
   post_processed_output_ = true;
   DisablePartialUpdateOneFrame();
-  validated_.reset();
+  validated_ = false;
 }
 
 int HWCDisplayPrimary::FrameCaptureAsync(const BufferInfo &output_buffer_info,
@@ -564,7 +564,7 @@
 
   if (display_intf_) {
     error = display_intf_->SetDetailEnhancerData(de_data);
-    validated_.reset();
+    validated_ = false;
   }
   return error;
 }
@@ -574,7 +574,7 @@
 
   if (display_intf_) {
     error = display_intf_->ControlPartialUpdate(enable, pending);
-    validated_.reset();
+    validated_ = false;
   }
 
   return error;
@@ -585,7 +585,7 @@
 
   if (display_intf_) {
     error = display_intf_->DisablePartialUpdateOneFrame();
-    validated_.reset();
+    validated_ = false;
   }
 
   return error;
@@ -594,7 +594,7 @@
 
 DisplayError HWCDisplayPrimary::SetMixerResolution(uint32_t width, uint32_t height) {
   DisplayError error = display_intf_->SetMixerResolution(width, height);
-  validated_.reset();
+  validated_ = false;
   return error;
 }
 
diff --git a/sdm/libs/hwc2/hwc_display_virtual.cpp b/sdm/libs/hwc2/hwc_display_virtual.cpp
index 8837ad0..c523e31 100644
--- a/sdm/libs/hwc2/hwc_display_virtual.cpp
+++ b/sdm/libs/hwc2/hwc_display_virtual.cpp
@@ -131,7 +131,7 @@
   auto status = HWC2::Error::None;
   if (display_paused_) {
     DisplayError error = display_intf_->Flush();
-    validated_.reset();
+    validated_ = false;
     if (error != kErrorNone) {
       DLOGE("Flush failed. Error = %d", error);
     }
diff --git a/sdm/libs/hwc2/hwc_session.cpp b/sdm/libs/hwc2/hwc_session.cpp
index a114af9..390f39e 100644
--- a/sdm/libs/hwc2/hwc_session.cpp
+++ b/sdm/libs/hwc2/hwc_session.cpp
@@ -32,6 +32,8 @@
 #include <utils/debug.h>
 #include <sync/sync.h>
 #include <profiler.h>
+#include <qd_utils.h>
+#include <utils/utils.h>
 #include <algorithm>
 #include <string>
 #include <bitset>
@@ -44,6 +46,7 @@
 #include "hwc_debugger.h"
 #include "hwc_display_primary.h"
 #include "hwc_display_virtual.h"
+#include "hwc_display_external_test.h"
 
 #define __CLASS__ "HWCSession"
 
@@ -192,8 +195,7 @@
     hdmi_is_primary_ = true;
     // Create display if it is connected, else wait for hotplug connect event.
     if (hw_disp_info.is_connected) {
-      status = HWCDisplayExternal::Create(core_intf_, &buffer_allocator_, &callbacks_, qservice_,
-                                          &hwc_display_[HWC_DISPLAY_PRIMARY]);
+      status = CreateExternalDisplay(HWC_DISPLAY_PRIMARY, 0, 0, false);
     }
   } else {
     // Create and power on primary display
@@ -346,7 +348,6 @@
 int32_t HWCSession::CreateVirtualDisplay(hwc2_device_t *device, uint32_t width, uint32_t height,
                                          int32_t *format, hwc2_display_t *out_display_id) {
   // TODO(user): Handle concurrency with HDMI
-  SCOPE_LOCK(locker_[HWC_DISPLAY_VIRTUAL]);
   if (!device) {
     return HWC2_ERROR_BAD_DISPLAY;
   }
@@ -357,6 +358,7 @@
 
   HWCSession *hwc_session = static_cast<HWCSession *>(device);
   auto status = hwc_session->CreateVirtualDisplayObject(width, height, format);
+
   if (status == HWC2::Error::None) {
     *out_display_id = HWC_DISPLAY_VIRTUAL;
     DLOGI("Created virtual display id:% " PRIu64 " with res: %dx%d",
@@ -839,18 +841,24 @@
   return nullptr;
 }
 
-// TODO(user): handle locking
-
 HWC2::Error HWCSession::CreateVirtualDisplayObject(uint32_t width, uint32_t height,
                                                    int32_t *format) {
-  if (hwc_display_[HWC_DISPLAY_VIRTUAL]) {
-    return HWC2::Error::NoResources;
+  {
+    SCOPE_LOCK(locker_[HWC_DISPLAY_VIRTUAL]);
+    if (hwc_display_[HWC_DISPLAY_VIRTUAL]) {
+      return HWC2::Error::NoResources;
+    }
+
+    auto status = HWCDisplayVirtual::Create(core_intf_, &buffer_allocator_, &callbacks_, width,
+                                            height, format, &hwc_display_[HWC_DISPLAY_VIRTUAL]);
+    // TODO(user): validate width and height support
+    if (status) {
+      return HWC2::Error::Unsupported;
+    }
   }
-  auto status = HWCDisplayVirtual::Create(core_intf_, &buffer_allocator_, &callbacks_, width,
-                                          height, format, &hwc_display_[HWC_DISPLAY_VIRTUAL]);
-  // TODO(user): validate width and height support
-  if (status)
-    return HWC2::Error::Unsupported;
+
+  SEQUENCE_WAIT_SCOPE_LOCK(locker_[HWC_DISPLAY_PRIMARY]);
+  hwc_display_[HWC_DISPLAY_PRIMARY]->ResetValidation();
 
   return HWC2::Error::None;
 }
@@ -865,8 +873,7 @@
   hwc_display_[HWC_DISPLAY_PRIMARY]->GetFrameBufferResolution(&primary_width, &primary_height);
 
   if (disp == HWC_DISPLAY_EXTERNAL) {
-    status = HWCDisplayExternal::Create(core_intf_, &buffer_allocator_, &callbacks_, primary_width,
-                                        primary_height, qservice_, false, &hwc_display_[disp]);
+    status = CreateExternalDisplay(disp, primary_width, primary_height, false);
   } else {
     DLOGE("Invalid display type");
     return -1;
@@ -1415,18 +1422,27 @@
 
 void HWCSession::HandleExtHPD(const char *uevent_data, int length) {
   const char *pstr = GetTokenValue(uevent_data, length, "name=");
-  if (!pstr || (strcmp(pstr, "DP-1") != 0)) {
+  if (!pstr || (strncmp(pstr, "DP-1", strlen("DP-1")) != 0)) {
     return;
   }
 
   pstr = GetTokenValue(uevent_data, length, "status=");
   if (pstr) {
     bool connected = false;
-    if (strcmp(pstr, "connected") == 0) {
+    hpd_bpp_ = 0;
+    hpd_pattern_ = 0;
+    if (strncmp(pstr, "connected", strlen("connected")) == 0) {
       connected = true;
     }
+    int bpp = GetEventValue(uevent_data, length, "bpp=");
+    int pattern = GetEventValue(uevent_data, length, "pattern=");
+    if (bpp >=0 && pattern >= 0) {
+      hpd_bpp_ = bpp;
+      hpd_pattern_ = pattern;
+    }
 
-    DLOGI("Recived Ext HPD, connected:%d  status=%s", connected, pstr);
+    DLOGI("Recived Ext HPD, connected:%d  status=%s  bpp = %d pattern =%d ",
+          connected, pstr, hpd_bpp_, hpd_pattern_);
     HotPlugHandler(connected);
   }
 }
@@ -1484,8 +1500,7 @@
       if (hwc_display_[HWC_DISPLAY_PRIMARY]) {
         status = hwc_display_[HWC_DISPLAY_PRIMARY]->SetState(connected);
       } else {
-        status = HWCDisplayExternal::Create(core_intf_, &buffer_allocator_, &callbacks_,
-                                            qservice_, &hwc_display_[HWC_DISPLAY_PRIMARY]);
+        status = CreateExternalDisplay(HWC_DISPLAY_PRIMARY, 0, 0, false);
         notify_hotplug = true;
       }
 
@@ -1499,6 +1514,8 @@
         DLOGE("Primary display is not connected.");
         return -1;
       }
+
+      hwc_display_[HWC_DISPLAY_PRIMARY]->ResetValidation();
     }
 
     if (connected) {
@@ -1609,4 +1626,27 @@
   }
 }
 
+int HWCSession::CreateExternalDisplay(int disp_id, uint32_t primary_width,
+                                      uint32_t primary_height, bool use_primary_res) {
+  uint32_t panel_bpp = 0;
+  uint32_t pattern_type = 0;
+
+  if (GetDriverType() == DriverType::FB) {
+    qdutils::getDPTestConfig(&panel_bpp, &pattern_type);
+  } else {
+    panel_bpp = static_cast<uint32_t>(hpd_bpp_);
+    pattern_type = static_cast<uint32_t>(hpd_pattern_);
+  }
+
+  if (panel_bpp && pattern_type) {
+    return HWCDisplayExternalTest::Create(core_intf_, &buffer_allocator_, &callbacks_,
+                                          qservice_, panel_bpp, pattern_type,
+                                          &hwc_display_[disp_id]);
+  }
+
+  return  HWCDisplayExternal::Create(core_intf_, &buffer_allocator_, &callbacks_,
+                                     primary_width, primary_height, qservice_,
+                                     use_primary_res, &hwc_display_[disp_id]);
+}
+
 }  // namespace sdm
diff --git a/sdm/libs/hwc2/hwc_session.h b/sdm/libs/hwc2/hwc_session.h
index b9032a4..3659370 100644
--- a/sdm/libs/hwc2/hwc_session.h
+++ b/sdm/libs/hwc2/hwc_session.h
@@ -144,11 +144,7 @@
   static int32_t SetColorTransform(hwc2_device_t *device, hwc2_display_t display,
                                    const float *matrix, int32_t /*android_color_transform_t*/ hint);
 
-  // Meant to be called by HWCDisplay to preserve sequence of validate/present during events from
-  // polling thread
-  static void WaitForSequence(hwc2_display_t display) {
-    SEQUENCE_WAIT_SCOPE_LOCK(locker_[display]);
-  }
+  static Locker locker_[HWC_NUM_DISPLAY_TYPES];
 
  private:
   static const int kExternalConnectionTimeoutMs = 500;
@@ -178,6 +174,8 @@
   int32_t SetSecondaryDisplayStatus(int disp_id, HWCDisplay::DisplayStatus status);
   int32_t GetPanelBrightness(int *level);
   int32_t MinHdcpEncryptionLevelChanged(int disp_id, uint32_t min_enc_level);
+  int32_t CreateExternalDisplay(int disp_id, uint32_t primary_width, uint32_t primary_height,
+                                bool use_primary_res);
 
   // service methods
   void StartServices();
@@ -232,7 +230,6 @@
   void Refresh(hwc2_display_t display);
   void HotPlug(hwc2_display_t display, HWC2::Connection state);
 
-  static Locker locker_[HWC_NUM_DISPLAY_TYPES];
   CoreInterface *core_intf_ = nullptr;
   HWCDisplay *hwc_display_[HWC_NUM_DISPLAY_TYPES] = {nullptr};
   HWCCallbacks callbacks_;
@@ -250,6 +247,8 @@
   bool hdmi_is_primary_ = false;
   bool is_composer_up_ = false;
   Locker callbacks_lock_;
+  int hpd_bpp_ = 0;
+  int hpd_pattern_ = 0;
 };
 
 }  // namespace sdm