Merge "sdm: Support HDR Capabilites thru DisplayFixedConfig"
diff --git a/common.mk b/common.mk
index c18046d..fadc5d8 100644
--- a/common.mk
+++ b/common.mk
@@ -24,6 +24,7 @@
 
 common_includes += $(display_top)/include
 common_includes += $(display_top)/sdm/include
+common_includes += system/core/base/include
 
 common_header_export_path := qcom/display
 
diff --git a/libgralloc/gpu.cpp b/libgralloc/gpu.cpp
index 545903a..5911314 100644
--- a/libgralloc/gpu.cpp
+++ b/libgralloc/gpu.cpp
@@ -280,6 +280,7 @@
             grallocFormat = HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS_UBWC;
         else if(usage & GRALLOC_USAGE_HW_VIDEO_ENCODER) {
             if(MDPCapabilityInfo::getInstance().isWBUBWCSupportedByMDP() &&
+               !IAllocController::getInstance()->isDisableUBWCForEncoder() &&
                usage & GRALLOC_USAGE_HW_COMPOSER)
               grallocFormat = HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS_UBWC;
             else
diff --git a/libqdutils/qd_utils.cpp b/libqdutils/qd_utils.cpp
index 43fb715..b84ae87 100644
--- a/libqdutils/qd_utils.cpp
+++ b/libqdutils/qd_utils.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013, 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
@@ -31,7 +31,7 @@
 
 namespace qdutils {
 
-int parseLine(char *input, char *tokens[], const uint32_t maxToken, uint32_t *count) {
+static int parseLine(char *input, char *tokens[], const uint32_t maxToken, uint32_t *count) {
     char *tmpToken = NULL;
     char *tmpPtr;
     uint32_t index = 0;
@@ -49,6 +49,38 @@
     return 0;
 }
 
+static int getExternalNode(const char *type) {
+    FILE *displayDeviceFP = NULL;
+    char fbType[MAX_FRAME_BUFFER_NAME_SIZE];
+    char msmFbTypePath[MAX_FRAME_BUFFER_NAME_SIZE];
+    int j = 0;
+
+    for(j = 0; j < HWC_NUM_DISPLAY_TYPES; j++) {
+        snprintf (msmFbTypePath, sizeof(msmFbTypePath),
+                  "/sys/class/graphics/fb%d/msm_fb_type", j);
+        displayDeviceFP = fopen(msmFbTypePath, "r");
+        if(displayDeviceFP) {
+            fread(fbType, sizeof(char), MAX_FRAME_BUFFER_NAME_SIZE,
+                    displayDeviceFP);
+            if(strncmp(fbType, type, strlen(type)) == 0) {
+                ALOGD("%s: %s is at fb%d", __func__, type, j);
+                fclose(displayDeviceFP);
+                break;
+            }
+            fclose(displayDeviceFP);
+        } else {
+            ALOGE("%s: Failed to open fb node %d", __func__, j);
+        }
+    }
+
+    if (j < HWC_NUM_DISPLAY_TYPES)
+        return j;
+    else
+        ALOGE("%s: Failed to find %s node", __func__, type);
+
+    return -1;
+}
+
 int querySDEInfo(HWQueryType type, int *value) {
     FILE *fileptr = NULL;
     const char *featureName;
@@ -102,37 +134,8 @@
     return 0;
 }
 
-int getHDMINode(void)
-{
-    FILE *displayDeviceFP = NULL;
-    char fbType[MAX_FRAME_BUFFER_NAME_SIZE];
-    char msmFbTypePath[MAX_FRAME_BUFFER_NAME_SIZE];
-    int j = 0;
-
-    for(j = 0; j < HWC_NUM_DISPLAY_TYPES; j++) {
-        snprintf (msmFbTypePath, sizeof(msmFbTypePath),
-                  "/sys/class/graphics/fb%d/msm_fb_type", j);
-        displayDeviceFP = fopen(msmFbTypePath, "r");
-        if(displayDeviceFP) {
-            fread(fbType, sizeof(char), MAX_FRAME_BUFFER_NAME_SIZE,
-                    displayDeviceFP);
-            if(strncmp(fbType, "dtv panel", strlen("dtv panel")) == 0) {
-                ALOGD("%s: HDMI is at fb%d", __func__, j);
-                fclose(displayDeviceFP);
-                break;
-            }
-            fclose(displayDeviceFP);
-        } else {
-            ALOGE("%s: Failed to open fb node %d", __func__, j);
-        }
-    }
-
-    if (j < HWC_NUM_DISPLAY_TYPES)
-        return j;
-    else
-        ALOGE("%s: Failed to find HDMI node", __func__);
-
-    return -1;
+int getHDMINode(void) {
+    return getExternalNode("dtv panel");
 }
 
 int getEdidRawData(char *buffer)
@@ -162,4 +165,80 @@
     return size;
 }
 
+bool isDPConnected() {
+    char connectPath[MAX_FRAME_BUFFER_NAME_SIZE];
+    FILE *connectFile = NULL;
+    size_t len = MAX_STRING_LENGTH;
+    char stringBuffer[MAX_STRING_LENGTH];
+    char *line = stringBuffer;
+
+    int nodeId = getExternalNode("dp panel");
+    if (nodeId < 0) {
+        ALOGE("%s no DP node found", __func__);
+        return false;
+    }
+
+    snprintf(connectPath, sizeof(connectPath),
+             "/sys/class/graphics/fb%d/connected", nodeId);
+
+    connectFile = fopen(connectPath, "rb");
+    if (!connectFile) {
+        ALOGW("Failed to open connect node for device node %d", nodeId);
+        return false;
+    }
+
+    if (getline(&line, &len, connectFile) < 0) {
+        fclose(connectFile);
+        return false;
+    }
+
+    fclose(connectFile);
+
+    return atoi(line);
+}
+
+int getDPTestConfig(uint32_t *panelBpp, uint32_t *patternType) {
+    if (!panelBpp || !patternType) {
+        return -1;
+    }
+
+    char configPath[MAX_FRAME_BUFFER_NAME_SIZE];
+    FILE *configFile = NULL;
+    uint32_t tokenCount = 0;
+    const uint32_t maxCount = 10;
+    char *tokens[maxCount] = { NULL };
+    size_t len = MAX_STRING_LENGTH;
+    char stringBuffer[MAX_STRING_LENGTH];
+    char *line = stringBuffer;
+
+    int nodeId = getExternalNode("dp panel");
+    if (nodeId < 0) {
+        ALOGE("%s no DP node found", __func__);
+        return -EINVAL;
+    }
+
+    snprintf(configPath, sizeof(configPath),
+             "/sys/class/graphics/fb%d/config", nodeId);
+
+    configFile = fopen(configPath, "rb");
+    if (!configFile) {
+        ALOGW("Failed to open config node for device node %d", nodeId);
+        return -EINVAL;
+    }
+
+    while (getline(&line, &len, configFile) != -1) {
+        if (!parseLine(line, tokens, maxCount, &tokenCount)) {
+            if (!strncmp(tokens[0], "bpp", strlen("bpp"))) {
+                *panelBpp = static_cast<uint32_t>(atoi(tokens[1]));
+            } else  if (!strncmp(tokens[0], "pattern", strlen("pattern"))) {
+                *patternType = static_cast<uint32_t>(atoi(tokens[1]));
+            }
+        }
+    }
+
+    fclose(configFile);
+
+    return 0;
+}
+
 }; //namespace qdutils
diff --git a/libqdutils/qd_utils.h b/libqdutils/qd_utils.h
index 4722dcd..5e8d1bf 100644
--- a/libqdutils/qd_utils.h
+++ b/libqdutils/qd_utils.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2013, 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
@@ -59,10 +59,11 @@
     MAX_STRING_LENGTH = 1024,
 };
 
-int parseLine(char *input, char *tokens[], const uint32_t maxToken, uint32_t *count);
 int querySDEInfo(HWQueryType type, int *value);
 int getEdidRawData(char *buffer);
 int getHDMINode(void);
+bool isDPConnected();
+int getDPTestConfig(uint32_t *panelBpp, uint32_t *patternType);
 
 }; //namespace qdutils
 #endif
diff --git a/sdm/include/private/hw_info_types.h b/sdm/include/private/hw_info_types.h
index 11b93cc..657d4ac 100644
--- a/sdm/include/private/hw_info_types.h
+++ b/sdm/include/private/hw_info_types.h
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+* Copyright (c) 2015-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:
@@ -224,6 +224,12 @@
   uint32_t blue[2] = {};              // Blue color primary
 };
 
+struct HWPanelOrientation {
+  bool rotation = false;
+  bool flip_horizontal = false;
+  bool flip_vertical = false;
+};
+
 struct HWPanelInfo {
   DisplayPort port = kPortDefault;    // Display port
   HWDisplayMode mode = kModeDefault;  // Display mode
@@ -253,6 +259,7 @@
   uint32_t average_luminance = 0;     // Panel's average luminance level
   uint32_t blackness_level = 0;       // Panel's blackness level
   HWColorPrimaries primaries = {};    // WRGB color primaries
+  HWPanelOrientation panel_orientation = {};  // Panel Orientation
 
   bool operator !=(const HWPanelInfo &panel_info) {
     return ((port != panel_info.port) || (mode != panel_info.mode) ||
diff --git a/sdm/include/utils/rect.h b/sdm/include/utils/rect.h
index 6d3d482..16a9ba9 100644
--- a/sdm/include/utils/rect.h
+++ b/sdm/include/utils/rect.h
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+* Copyright (c) 2015-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
@@ -57,6 +57,7 @@
                       bool flip_horizontal, LayerRect *out_rects);
   void MapRect(const LayerRect &src_domain, const LayerRect &dst_domain, const LayerRect &in_rect,
                LayerRect *out_rect);
+  void TransformHV(const LayerRect &src_domain, const LayerRect &in_rect, LayerRect *out_rect);
   RectOrientation GetOrientation(const LayerRect &in_rect);
 }  // namespace sdm
 
diff --git a/sdm/libs/core/Android.mk b/sdm/libs/core/Android.mk
index 50f087d..f13c862 100644
--- a/sdm/libs/core/Android.mk
+++ b/sdm/libs/core/Android.mk
@@ -7,7 +7,7 @@
 LOCAL_C_INCLUDES              := $(common_includes) $(kernel_includes) $(common_header_export_path)
 LOCAL_CFLAGS                  := -Wno-unused-parameter -DLOG_TAG=\"SDM\" $(common_flags)
 LOCAL_HW_INTF_PATH            := fb
-LOCAL_SHARED_LIBRARIES        := libdl libsdmutils
+LOCAL_SHARED_LIBRARIES        := libdl liblog libsdmutils
 LOCAL_ADDITIONAL_DEPENDENCIES := $(common_deps) $(kernel_deps)
 LOCAL_SRC_FILES               := core_interface.cpp \
                                  core_impl.cpp \
diff --git a/sdm/libs/core/display_primary.cpp b/sdm/libs/core/display_primary.cpp
index 9f6c917..99aa3d5 100644
--- a/sdm/libs/core/display_primary.cpp
+++ b/sdm/libs/core/display_primary.cpp
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2014 - 2016, The Linux Foundation. All rights reserved.
+* Copyright (c) 2014 - 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:
@@ -88,6 +88,29 @@
   uint32_t new_mixer_height = 0;
   uint32_t display_width = display_attributes_.x_pixels;
   uint32_t display_height = display_attributes_.y_pixels;
+  bool needs_hv_flip = hw_panel_info_.panel_orientation.flip_horizontal &&
+                          hw_panel_info_.panel_orientation.flip_vertical;
+  LayerRect src_domain = {};
+  DisplayConfigVariableInfo variable_info = {};
+
+  if (needs_hv_flip) {
+    DisplayBase::GetFrameBufferConfig(&variable_info);
+    src_domain.right = variable_info.x_pixels;
+    src_domain.bottom = variable_info.y_pixels;
+
+    for (Layer *layer : layer_stack->layers) {
+      // Modify destination based on panel flip
+      TransformHV(src_domain, layer->dst_rect, &layer->dst_rect);
+
+      if (layer->flags.solid_fill) {
+        continue;
+      }
+
+      layer->transform.flip_horizontal ^= (hw_panel_info_.panel_orientation.flip_horizontal);
+      layer->transform.flip_vertical ^= (hw_panel_info_.panel_orientation.flip_vertical);
+     // TODO(user): Check how to handle rotation, if panel has rotation.
+    }
+  }
 
   if (NeedsMixerReconfiguration(layer_stack, &new_mixer_width, &new_mixer_height)) {
     error = ReconfigureMixer(new_mixer_width, new_mixer_height);
diff --git a/sdm/libs/core/fb/hw_device.cpp b/sdm/libs/core/fb/hw_device.cpp
index 111e386..f6c9ae0 100644
--- a/sdm/libs/core/fb/hw_device.cpp
+++ b/sdm/libs/core/fb/hw_device.cpp
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2014 - 2016, The Linux Foundation. All rights reserved.
+* Copyright (c) 2014 - 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
@@ -899,6 +899,11 @@
         panel_info->primaries.blue[0] = UINT32(atoi(tokens[1]));
       } else if (!strncmp(tokens[0], "blue_chromaticity_y", strlen("blue_chromaticity_y"))) {
         panel_info->primaries.blue[1] = UINT32(atoi(tokens[1]));
+      } else if (!strncmp(tokens[0], "panel_orientation", strlen("panel_orientation"))) {
+        int32_t panel_orient = atoi(tokens[1]);
+        panel_info->panel_orientation.flip_horizontal = ((panel_orient & MDP_FLIP_LR) > 0);
+        panel_info->panel_orientation.flip_vertical = ((panel_orient & MDP_FLIP_UD) > 0);
+        panel_info->panel_orientation.rotation = ((panel_orient & MDP_ROT_90) > 0);
       }
     }
   }
@@ -1041,7 +1046,7 @@
 
 bool HWDevice::EnableHotPlugDetection(int enable) {
   char hpdpath[kMaxStringLength];
-  char value = enable ? '1' : '0';
+  const char *value = enable ? "1" : "0";
 
   // Enable HPD for all pluggable devices.
   for (int i = 0; i < kFBNodeMax; i++) {
@@ -1050,7 +1055,7 @@
     if (panel_info.is_pluggable == true) {
       snprintf(hpdpath , sizeof(hpdpath), "%s%d/hpd", fb_path_, i);
 
-      ssize_t length = SysFsWrite(hpdpath, &value, sizeof(value));
+      ssize_t length = SysFsWrite(hpdpath, value, 1);
       if (length <= 0) {
         return false;
       }
diff --git a/sdm/libs/core/fb/hw_info.cpp b/sdm/libs/core/fb/hw_info.cpp
index a3435ca..2931638 100644
--- a/sdm/libs/core/fb/hw_info.cpp
+++ b/sdm/libs/core/fb/hw_info.cpp
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+* Copyright (c) 2015-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:
@@ -150,6 +150,10 @@
 }
 
 DisplayError HWInfo::GetHWResourceInfo(HWResourceInfo *hw_resource) {
+  if (hw_resource_) {
+    *hw_resource = *hw_resource_;
+    return kErrorNone;
+  }
   string fb_path = "/sys/devices/virtual/graphics/fb"
                       + to_string(kHWCapabilitiesNode) + "/mdp/caps";
 
@@ -159,8 +163,10 @@
     return kErrorHardware;
   }
 
-  InitSupportedFormatMap(hw_resource);
-  hw_resource->hw_version = kHWMdssVersion5;
+  hw_resource_ = new HWResourceInfo;
+
+  InitSupportedFormatMap(hw_resource_);
+  hw_resource_->hw_version = kHWMdssVersion5;
 
   uint32_t token_count = 0;
   const uint32_t max_count = 256;
@@ -170,91 +176,91 @@
     // parse the line and update information accordingly
     if (!ParseString(line.c_str(), tokens, max_count, ":, =\n", &token_count)) {
       if (!strncmp(tokens[0], "hw_rev", strlen("hw_rev"))) {
-        hw_resource->hw_revision = UINT32(atoi(tokens[1]));  // HW Rev, v1/v2
+        hw_resource_->hw_revision = UINT32(atoi(tokens[1]));  // HW Rev, v1/v2
       } else if (!strncmp(tokens[0], "rot_input_fmts", strlen("rot_input_fmts"))) {
-        ParseFormats(&tokens[1], (token_count - 1), kHWRotatorInput, hw_resource);
+        ParseFormats(&tokens[1], (token_count - 1), kHWRotatorInput, hw_resource_);
       } else if (!strncmp(tokens[0], "rot_output_fmts", strlen("rot_output_fmts"))) {
-        ParseFormats(&tokens[1], (token_count - 1), kHWRotatorOutput, hw_resource);
+        ParseFormats(&tokens[1], (token_count - 1), kHWRotatorOutput, hw_resource_);
       } else if (!strncmp(tokens[0], "wb_output_fmts", strlen("wb_output_fmts"))) {
-        ParseFormats(&tokens[1], (token_count - 1), kHWWBIntfOutput, hw_resource);
+        ParseFormats(&tokens[1], (token_count - 1), kHWWBIntfOutput, hw_resource_);
       } else if (!strncmp(tokens[0], "blending_stages", strlen("blending_stages"))) {
-        hw_resource->num_blending_stages = UINT8(atoi(tokens[1]));
+        hw_resource_->num_blending_stages = UINT8(atoi(tokens[1]));
       } else if (!strncmp(tokens[0], "max_downscale_ratio", strlen("max_downscale_ratio"))) {
-        hw_resource->max_scale_down = UINT32(atoi(tokens[1]));
+        hw_resource_->max_scale_down = UINT32(atoi(tokens[1]));
       } else if (!strncmp(tokens[0], "max_upscale_ratio", strlen("max_upscale_ratio"))) {
-        hw_resource->max_scale_up = UINT32(atoi(tokens[1]));
+        hw_resource_->max_scale_up = UINT32(atoi(tokens[1]));
       } else if (!strncmp(tokens[0], "max_bandwidth_low", strlen("max_bandwidth_low"))) {
-        hw_resource->max_bandwidth_low = UINT64(atol(tokens[1]));
+        hw_resource_->max_bandwidth_low = UINT64(atol(tokens[1]));
       } else if (!strncmp(tokens[0], "max_bandwidth_high", strlen("max_bandwidth_high"))) {
-        hw_resource->max_bandwidth_high = UINT64(atol(tokens[1]));
+        hw_resource_->max_bandwidth_high = UINT64(atol(tokens[1]));
       } else if (!strncmp(tokens[0], "max_mixer_width", strlen("max_mixer_width"))) {
-        hw_resource->max_mixer_width = UINT32(atoi(tokens[1]));
+        hw_resource_->max_mixer_width = UINT32(atoi(tokens[1]));
       } else if (!strncmp(tokens[0], "max_pipe_width", strlen("max_pipe_width"))) {
-        hw_resource->max_pipe_width = UINT32(atoi(tokens[1]));
+        hw_resource_->max_pipe_width = UINT32(atoi(tokens[1]));
       } else if (!strncmp(tokens[0], "max_cursor_size", strlen("max_cursor_size"))) {
-        hw_resource->max_cursor_size = UINT32(atoi(tokens[1]));
+        hw_resource_->max_cursor_size = UINT32(atoi(tokens[1]));
       } else if (!strncmp(tokens[0], "max_pipe_bw", strlen("max_pipe_bw"))) {
-        hw_resource->max_pipe_bw = UINT32(atoi(tokens[1]));
+        hw_resource_->max_pipe_bw = UINT32(atoi(tokens[1]));
       } else if (!strncmp(tokens[0], "max_mdp_clk", strlen("max_mdp_clk"))) {
-        hw_resource->max_sde_clk = UINT32(atoi(tokens[1]));
+        hw_resource_->max_sde_clk = UINT32(atoi(tokens[1]));
       } else if (!strncmp(tokens[0], "clk_fudge_factor", strlen("clk_fudge_factor"))) {
-        hw_resource->clk_fudge_factor = FLOAT(atoi(tokens[1])) / FLOAT(atoi(tokens[2]));
+        hw_resource_->clk_fudge_factor = FLOAT(atoi(tokens[1])) / FLOAT(atoi(tokens[2]));
       } else if (!strncmp(tokens[0], "fmt_mt_nv12_factor", strlen("fmt_mt_nv12_factor"))) {
-        hw_resource->macrotile_nv12_factor = UINT32(atoi(tokens[1]));
+        hw_resource_->macrotile_nv12_factor = UINT32(atoi(tokens[1]));
       } else if (!strncmp(tokens[0], "fmt_mt_factor", strlen("fmt_mt_factor"))) {
-        hw_resource->macrotile_factor = UINT32(atoi(tokens[1]));
+        hw_resource_->macrotile_factor = UINT32(atoi(tokens[1]));
       } else if (!strncmp(tokens[0], "fmt_linear_factor", strlen("fmt_linear_factor"))) {
-        hw_resource->linear_factor = UINT32(atoi(tokens[1]));
+        hw_resource_->linear_factor = UINT32(atoi(tokens[1]));
       } else if (!strncmp(tokens[0], "scale_factor", strlen("scale_factor"))) {
-        hw_resource->scale_factor = UINT32(atoi(tokens[1]));
+        hw_resource_->scale_factor = UINT32(atoi(tokens[1]));
       } else if (!strncmp(tokens[0], "xtra_ff_factor", strlen("xtra_ff_factor"))) {
-        hw_resource->extra_fudge_factor = UINT32(atoi(tokens[1]));
+        hw_resource_->extra_fudge_factor = UINT32(atoi(tokens[1]));
       } else if (!strncmp(tokens[0], "amortizable_threshold", strlen("amortizable_threshold"))) {
-        hw_resource->amortizable_threshold = UINT32(atoi(tokens[1]));
+        hw_resource_->amortizable_threshold = UINT32(atoi(tokens[1]));
       } else if (!strncmp(tokens[0], "system_overhead_lines", strlen("system_overhead_lines"))) {
-        hw_resource->system_overhead_lines = UINT32(atoi(tokens[1]));
+        hw_resource_->system_overhead_lines = UINT32(atoi(tokens[1]));
       } else if (!strncmp(tokens[0], "wb_intf_index", strlen("wb_intf_index"))) {
-        hw_resource->writeback_index = UINT32(atoi(tokens[1]));
+        hw_resource_->writeback_index = UINT32(atoi(tokens[1]));
       } else if (!strncmp(tokens[0], "dest_scaler_count", strlen("dest_scaler_count"))) {
-        hw_resource->hw_dest_scalar_info.count = UINT32(atoi(tokens[1]));
+        hw_resource_->hw_dest_scalar_info.count = UINT32(atoi(tokens[1]));
       } else if (!strncmp(tokens[0], "max_dest_scale_up", strlen("max_dest_scale_up"))) {
-        hw_resource->hw_dest_scalar_info.max_scale_up = UINT32(atoi(tokens[1]));
+        hw_resource_->hw_dest_scalar_info.max_scale_up = UINT32(atoi(tokens[1]));
       } else if (!strncmp(tokens[0], "max_dest_scaler_input_width",
                  strlen("max_dest_scaler_input_width"))) {
-        hw_resource->hw_dest_scalar_info.max_input_width = UINT32(atoi(tokens[1]));
+        hw_resource_->hw_dest_scalar_info.max_input_width = UINT32(atoi(tokens[1]));
       } else if (!strncmp(tokens[0], "max_dest_scaler_output_width",
                  strlen("max_dest_scaler_output_width"))) {
-        hw_resource->hw_dest_scalar_info.max_output_width = UINT32(atoi(tokens[1]));
+        hw_resource_->hw_dest_scalar_info.max_output_width = UINT32(atoi(tokens[1]));
       } else if (!strncmp(tokens[0], "features", strlen("features"))) {
         for (uint32_t i = 0; i < token_count; i++) {
           if (!strncmp(tokens[i], "bwc", strlen("bwc"))) {
-            hw_resource->has_bwc = true;
+            hw_resource_->has_bwc = true;
           } else if (!strncmp(tokens[i], "ubwc", strlen("ubwc"))) {
-            hw_resource->has_ubwc = true;
+            hw_resource_->has_ubwc = true;
           } else if (!strncmp(tokens[i], "decimation", strlen("decimation"))) {
-            hw_resource->has_decimation = true;
+            hw_resource_->has_decimation = true;
           } else if (!strncmp(tokens[i], "tile_format", strlen("tile_format"))) {
-            hw_resource->has_macrotile = true;
+            hw_resource_->has_macrotile = true;
           } else if (!strncmp(tokens[i], "src_split", strlen("src_split"))) {
-            hw_resource->is_src_split = true;
+            hw_resource_->is_src_split = true;
           } else if (!strncmp(tokens[i], "non_scalar_rgb", strlen("non_scalar_rgb"))) {
-            hw_resource->has_non_scalar_rgb = true;
+            hw_resource_->has_non_scalar_rgb = true;
           } else if (!strncmp(tokens[i], "perf_calc", strlen("perf_calc"))) {
-            hw_resource->perf_calc = true;
+            hw_resource_->perf_calc = true;
           } else if (!strncmp(tokens[i], "dynamic_bw_limit", strlen("dynamic_bw_limit"))) {
-            hw_resource->has_dyn_bw_support = true;
+            hw_resource_->has_dyn_bw_support = true;
           } else if (!strncmp(tokens[i], "separate_rotator", strlen("separate_rotator"))) {
-            hw_resource->separate_rotator = true;
+            hw_resource_->separate_rotator = true;
           } else if (!strncmp(tokens[i], "qseed3", strlen("qseed3"))) {
-            hw_resource->has_qseed3 = true;
+            hw_resource_->has_qseed3 = true;
           } else if (!strncmp(tokens[i], "has_ppp", strlen("has_ppp"))) {
-            hw_resource->has_ppp = true;
+            hw_resource_->has_ppp = true;
           } else if (!strncmp(tokens[i], "concurrent_writeback", strlen("concurrent_writeback"))) {
-            hw_resource->has_concurrent_writeback = true;
+            hw_resource_->has_concurrent_writeback = true;
           } else if (!strncmp(tokens[i], "avr", strlen("avr"))) {
-            hw_resource->has_avr = true;
+            hw_resource_->has_avr = true;
           } else if (!strncmp(tokens[i], "hdr", strlen("hdr"))) {
-            hw_resource->has_hdr = true;
+            hw_resource_->has_hdr = true;
           }
         }
       } else if (!strncmp(tokens[0], "pipe_count", strlen("pipe_count"))) {
@@ -268,16 +274,16 @@
               if (!strncmp(tokens[j], "pipe_type", strlen("pipe_type"))) {
                 if (!strncmp(tokens[j+1], "vig", strlen("vig"))) {
                   pipe_caps.type = kPipeTypeVIG;
-                  hw_resource->num_vig_pipe++;
+                  hw_resource_->num_vig_pipe++;
                 } else if (!strncmp(tokens[j+1], "rgb", strlen("rgb"))) {
                   pipe_caps.type = kPipeTypeRGB;
-                  hw_resource->num_rgb_pipe++;
+                  hw_resource_->num_rgb_pipe++;
                 } else if (!strncmp(tokens[j+1], "dma", strlen("dma"))) {
                   pipe_caps.type = kPipeTypeDMA;
-                  hw_resource->num_dma_pipe++;
+                  hw_resource_->num_dma_pipe++;
                 } else if (!strncmp(tokens[j+1], "cursor", strlen("cursor"))) {
                   pipe_caps.type = kPipeTypeCursor;
-                  hw_resource->num_cursor_pipe++;
+                  hw_resource_->num_cursor_pipe++;
                 }
               } else if (!strncmp(tokens[j], "pipe_ndx", strlen("pipe_ndx"))) {
                 pipe_caps.id = UINT32(atoi(tokens[j+1]));
@@ -288,18 +294,18 @@
                 uint32_t token_fmt_count = 0;
                 if (!ParseString(tokens[j+1], tokens_fmt, max_count, ",\n", &token_fmt_count)) {
                   if (pipe_caps.type == kPipeTypeVIG) {
-                    ParseFormats(tokens_fmt, token_fmt_count, kHWVIGPipe, hw_resource);
+                    ParseFormats(tokens_fmt, token_fmt_count, kHWVIGPipe, hw_resource_);
                   } else if (pipe_caps.type == kPipeTypeRGB) {
-                    ParseFormats(tokens_fmt, token_fmt_count, kHWRGBPipe, hw_resource);
+                    ParseFormats(tokens_fmt, token_fmt_count, kHWRGBPipe, hw_resource_);
                   } else if (pipe_caps.type == kPipeTypeDMA) {
-                    ParseFormats(tokens_fmt, token_fmt_count, kHWDMAPipe, hw_resource);
+                    ParseFormats(tokens_fmt, token_fmt_count, kHWDMAPipe, hw_resource_);
                   } else if (pipe_caps.type == kPipeTypeCursor) {
-                    ParseFormats(tokens_fmt, token_fmt_count, kHWCursorPipe, hw_resource);
+                    ParseFormats(tokens_fmt, token_fmt_count, kHWCursorPipe, hw_resource_);
                   }
                 }
               }
             }
-            hw_resource->hw_pipes.push_back(pipe_caps);
+            hw_resource_->hw_pipes.push_back(pipe_caps);
           }
         }
       }
@@ -309,38 +315,39 @@
   // Disable destination scalar count to 0 if extension library is not present
   DynLib extension_lib;
   if (!extension_lib.Open("libsdmextension.so")) {
-    hw_resource->hw_dest_scalar_info.count = 0;
+    hw_resource_->hw_dest_scalar_info.count = 0;
   }
 
   DLOGI("SDE Version = %d, SDE Revision = %x, RGB = %d, VIG = %d, DMA = %d, Cursor = %d",
-        hw_resource->hw_version, hw_resource->hw_revision, hw_resource->num_rgb_pipe,
-        hw_resource->num_vig_pipe, hw_resource->num_dma_pipe, hw_resource->num_cursor_pipe);
-  DLOGI("Upscale Ratio = %d, Downscale Ratio = %d, Blending Stages = %d", hw_resource->max_scale_up,
-        hw_resource->max_scale_down, hw_resource->num_blending_stages);
-  DLOGI("SourceSplit = %d QSEED3 = %d", hw_resource->is_src_split, hw_resource->has_qseed3);
+        hw_resource_->hw_version, hw_resource_->hw_revision, hw_resource_->num_rgb_pipe,
+        hw_resource_->num_vig_pipe, hw_resource_->num_dma_pipe, hw_resource_->num_cursor_pipe);
+  DLOGI("Upscale Ratio = %d, Downscale Ratio = %d, Blending Stages = %d",
+        hw_resource_->max_scale_up, hw_resource_->max_scale_down,
+        hw_resource_->num_blending_stages);
+  DLOGI("SourceSplit = %d QSEED3 = %d", hw_resource_->is_src_split, hw_resource_->has_qseed3);
   DLOGI("BWC = %d, UBWC = %d, Decimation = %d, Tile Format = %d Concurrent Writeback = %d",
-        hw_resource->has_bwc, hw_resource->has_ubwc, hw_resource->has_decimation,
-        hw_resource->has_macrotile, hw_resource->has_concurrent_writeback);
-  DLOGI("MaxLowBw = %" PRIu64 " , MaxHighBw = % " PRIu64 "", hw_resource->max_bandwidth_low,
-        hw_resource->max_bandwidth_high);
+        hw_resource_->has_bwc, hw_resource_->has_ubwc, hw_resource_->has_decimation,
+        hw_resource_->has_macrotile, hw_resource_->has_concurrent_writeback);
+  DLOGI("MaxLowBw = %" PRIu64 " , MaxHighBw = % " PRIu64 "", hw_resource_->max_bandwidth_low,
+        hw_resource_->max_bandwidth_high);
   DLOGI("MaxPipeBw = %" PRIu64 " KBps, MaxSDEClock = % " PRIu64 " Hz, ClockFudgeFactor = %f",
-        hw_resource->max_pipe_bw, hw_resource->max_sde_clk, hw_resource->clk_fudge_factor);
+        hw_resource_->max_pipe_bw, hw_resource_->max_sde_clk, hw_resource_->clk_fudge_factor);
   DLOGI("Prefill factors: Tiled_NV12 = %d, Tiled = %d, Linear = %d, Scale = %d, Fudge_factor = %d",
-        hw_resource->macrotile_nv12_factor, hw_resource->macrotile_factor,
-        hw_resource->linear_factor, hw_resource->scale_factor, hw_resource->extra_fudge_factor);
+        hw_resource_->macrotile_nv12_factor, hw_resource_->macrotile_factor,
+        hw_resource_->linear_factor, hw_resource_->scale_factor, hw_resource_->extra_fudge_factor);
 
-  if (hw_resource->separate_rotator || hw_resource->num_dma_pipe) {
-    GetHWRotatorInfo(hw_resource);
+  if (hw_resource_->separate_rotator || hw_resource_->num_dma_pipe) {
+    GetHWRotatorInfo(hw_resource_);
   }
 
   // If the driver doesn't spell out the wb index, assume it to be the number of rotators,
   // based on legacy implementation.
-  if (hw_resource->writeback_index == kHWBlockMax) {
-    hw_resource->writeback_index = hw_resource->hw_rot_info.num_rotator;
+  if (hw_resource_->writeback_index == kHWBlockMax) {
+    hw_resource_->writeback_index = hw_resource_->hw_rot_info.num_rotator;
   }
 
-  if (hw_resource->has_dyn_bw_support) {
-    DisplayError ret = GetDynamicBWLimits(hw_resource);
+  if (hw_resource_->has_dyn_bw_support) {
+    DisplayError ret = GetDynamicBWLimits(hw_resource_);
     if (ret != kErrorNone) {
       DLOGE("Failed to read dynamic band width info");
       return ret;
@@ -349,11 +356,13 @@
     DLOGI("Has Support for multiple bw limits shown below");
     for (int index = 0; index < kBwModeMax; index++) {
       DLOGI("Mode-index=%d  total_bw_limit=%d and pipe_bw_limit=%d",
-            index, hw_resource->dyn_bw_info.total_bw_limit[index],
-            hw_resource->dyn_bw_info.pipe_bw_limit[index]);
+            index, hw_resource_->dyn_bw_info.total_bw_limit[index],
+            hw_resource_->dyn_bw_info.pipe_bw_limit[index]);
     }
   }
 
+  *hw_resource = *hw_resource_;
+
   return kErrorNone;
 }
 
diff --git a/sdm/libs/core/fb/hw_info.h b/sdm/libs/core/fb/hw_info.h
index df9115d..cbceba0 100644
--- a/sdm/libs/core/fb/hw_info.h
+++ b/sdm/libs/core/fb/hw_info.h
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2015 - 2016, The Linux Foundation. All rights reserved.
+* Copyright (c) 2015 - 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:
@@ -41,6 +41,7 @@
 
 class HWInfo: public HWInfoInterface {
  public:
+  virtual ~HWInfo() { delete hw_resource_; }
   virtual DisplayError GetHWResourceInfo(HWResourceInfo *hw_resource);
   virtual DisplayError GetFirstDisplayInterfaceType(HWDisplayInterfaceInfo *hw_disp_info);
 
@@ -71,6 +72,7 @@
                     HWResourceInfo *hw_resource);
   void PopulateSupportedFormatMap(const std::bitset<8> *format_supported, uint32_t format_count,
                                   HWSubBlockType sub_blk_type, HWResourceInfo *hw_resource);
+  HWResourceInfo *hw_resource_ = NULL;
 };
 
 }  // namespace sdm
diff --git a/sdm/libs/hwc/Android.mk b/sdm/libs/hwc/Android.mk
index 594ade9..c39e1f9 100644
--- a/sdm/libs/hwc/Android.mk
+++ b/sdm/libs/hwc/Android.mk
@@ -15,7 +15,7 @@
 
 LOCAL_SHARED_LIBRARIES        := libsdmcore libqservice libbinder libhardware libhardware_legacy \
                                  libutils libcutils libsync libmemalloc libqdutils libdl \
-                                 libpowermanager libsdmutils libgpu_tonemapper  libc++
+                                 libpowermanager libsdmutils libgpu_tonemapper  libc++ liblog
 
 LOCAL_SRC_FILES               := hwc_session.cpp \
                                  hwc_display.cpp \
@@ -30,7 +30,8 @@
                                  blit_engine_c2d.cpp \
                                  cpuhint.cpp \
                                  hwc_tonemapper.cpp \
-                                 hwc_socket_handler.cpp
+                                 hwc_socket_handler.cpp \
+                                 hwc_display_external_test.cpp
 
 include $(BUILD_SHARED_LIBRARY)
 endif
diff --git a/sdm/libs/hwc/hwc_display_external_test.cpp b/sdm/libs/hwc/hwc_display_external_test.cpp
new file mode 100644
index 0000000..0f84326
--- /dev/null
+++ b/sdm/libs/hwc/hwc_display_external_test.cpp
@@ -0,0 +1,760 @@
+/*
+* 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 <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, hwc_procs_t const **hwc_procs,
+                                   qService::QService *qservice, uint32_t panel_bpp,
+                                   uint32_t pattern_type, HWCDisplay **hwc_display) {
+  HWCDisplay *hwc_external_test = new HWCDisplayExternalTest(core_intf, hwc_procs, 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;
+
+  DLOGI("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,
+                                               hwc_procs_t const **hwc_procs,
+                                               qService::QService *qservice, uint32_t panel_bpp,
+                                               uint32_t pattern_type)
+  : HWCDisplay(core_intf, hwc_procs, kHDMI, HWC_DISPLAY_EXTERNAL, false, qservice,
+               DISPLAY_CLASS_EXTERNAL), 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) {
+    return status;
+  }
+
+  buffer_allocator_ = new HWCBufferAllocator();
+
+  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();
+    return status;
+  }
+
+  return status;
+}
+
+int HWCDisplayExternalTest::Deinit() {
+  DestroyLayerStack();
+
+  delete buffer_allocator_;
+  buffer_allocator_ = NULL;
+
+  return HWCDisplay::Deinit();
+}
+
+
+int HWCDisplayExternalTest::Prepare(hwc_display_contents_1_t *content_list) {
+  int status = 0;
+
+  if (secure_display_active_) {
+    MarkLayersForGPUBypass(content_list);
+    return status;
+  }
+
+  if (!content_list || !content_list->numHwLayers) {
+    DLOGW("Invalid content list");
+    return -EINVAL;
+  }
+
+  if (shutdown_pending_) {
+    return 0;
+  }
+
+  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(content_list);
+
+  return 0;
+}
+
+int HWCDisplayExternalTest::Commit(hwc_display_contents_1_t *content_list) {
+  int status = 0;
+
+  if (secure_display_active_) {
+    return status;
+  }
+
+  if (!content_list || !content_list->numHwLayers) {
+    DLOGW("Invalid content list");
+    return -EINVAL;
+  }
+
+  if (shutdown_pending_) {
+    return 0;
+  }
+
+  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;
+        return status;
+      } 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;
+      }
+    }
+  }
+
+  return PostCommit(content_list);
+}
+
+void HWCDisplayExternalTest::SetSecureDisplay(bool secure_display_active, bool force_flush) {
+  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/misc/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(dir_path);
+    dump_file_name << "/input_layer_" << width << "x" << height << "_" << format_str << ".raw";
+
+    std::fstream fs(dump_file_name.str().c_str());
+    if (!fs.is_open()) {
+      DLOGI("File open failed", 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;
+    }
+
+    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;
+}
+
+int HWCDisplayExternalTest::PostCommit(hwc_display_contents_1_t *content_list) {
+  int status = 0;
+
+  // 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;
+    content_list->retireFenceFd = -1;
+  }
+
+  flush_ = false;
+
+  return status;
+}
+
+}  // namespace sdm
+
diff --git a/sdm/libs/hwc/hwc_display_external_test.h b/sdm/libs/hwc/hwc_display_external_test.h
new file mode 100644
index 0000000..050823e
--- /dev/null
+++ b/sdm/libs/hwc/hwc_display_external_test.h
@@ -0,0 +1,102 @@
+/*
+* 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, hwc_procs_t const **hwc_procs,
+                     qService::QService *qservice,  uint32_t panel_bpp, uint32_t pattern_type,
+                     HWCDisplay **hwc_display);
+  static void Destroy(HWCDisplay *hwc_display);
+  virtual int Prepare(hwc_display_contents_1_t *content_list);
+  virtual int Commit(hwc_display_contents_1_t *content_list);
+  virtual void SetSecureDisplay(bool secure_display_active, bool force_flush);
+  virtual int Perform(uint32_t operation, ...);
+
+ protected:
+  HWCBufferAllocator *buffer_allocator_ = NULL;
+  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, hwc_procs_t const **hwc_procs,
+                         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();
+  int PostCommit(hwc_display_contents_1_t *content_list);
+
+  static const uint32_t kTestLayerCnt = 1;
+};
+
+}  // namespace sdm
+
+#endif  // __HWC_DISPLAY_EXTERNAL_TEST_H__
+
diff --git a/sdm/libs/hwc/hwc_session.cpp b/sdm/libs/hwc/hwc_session.cpp
index 34d923b..cac8a8f 100644
--- a/sdm/libs/hwc/hwc_session.cpp
+++ b/sdm/libs/hwc/hwc_session.cpp
@@ -54,6 +54,8 @@
 #include "hwc_display_null.h"
 #include "hwc_display_primary.h"
 #include "hwc_display_virtual.h"
+#include "hwc_display_external_test.h"
+#include "qd_utils.h"
 
 #define __CLASS__ "HWCSession"
 
@@ -148,8 +150,7 @@
       HWCDebugHandler::Get()->SetProperty("persist.sys.is_hdmi_primary", "1");
       is_hdmi_primary_ = true;
       if (hw_disp_info.is_connected) {
-        status = HWCDisplayExternal::Create(core_intf_, &hwc_procs_, qservice_,
-                                            &hwc_display_[HWC_DISPLAY_PRIMARY]);
+        status = CreateExternalDisplay(HWC_DISPLAY_PRIMARY, 0, 0, false);
         is_hdmi_yuv_ = IsDisplayYUV(HWC_DISPLAY_PRIMARY);
       } else {
         // NullDisplay simply closes all its fences, and advertizes a standard
@@ -640,8 +641,7 @@
   hwc_display_[HWC_DISPLAY_PRIMARY]->GetFrameBufferResolution(&primary_width, &primary_height);
 
   if (disp == HWC_DISPLAY_EXTERNAL) {
-    status = HWCDisplayExternal::Create(core_intf_, &hwc_procs_, primary_width, primary_height,
-                                        qservice_, false, &hwc_display_[disp]);
+    status = CreateExternalDisplay(disp, primary_width, primary_height, false);
     connected_displays_[HWC_DISPLAY_EXTERNAL] = 1;
   } else if (disp == HWC_DISPLAY_VIRTUAL) {
     status = HWCDisplayVirtual::Create(core_intf_, &hwc_procs_, primary_width, primary_height,
@@ -1443,7 +1443,7 @@
 int HWCSession::GetEventValue(const char *uevent_data, int length, const char *event_info) {
   const char *iterator_str = uevent_data;
   while (((iterator_str - uevent_data) <= length) && (*iterator_str)) {
-    char *pstr = strstr(iterator_str, event_info);
+    const char *pstr = strstr(iterator_str, event_info);
     if (pstr != NULL) {
       return (atoi(iterator_str + strlen(event_info)));
     }
@@ -1508,6 +1508,7 @@
       // If we are in HDMI as primary and the primary display just got plugged in
       if (is_hdmi_primary_ && null_display) {
         uint32_t primary_width, primary_height;
+        int status = 0;
         null_display->GetFrameBufferResolution(&primary_width, &primary_height);
         delete null_display;
         hwc_display_[HWC_DISPLAY_PRIMARY] = NULL;
@@ -1516,9 +1517,7 @@
         // display had. This is necessary because SurfaceFlinger does not dynamically update
         // framebuffer resolution once it reads it at bootup. So we always have to have the NULL
         // display/external display both at the bootup resolution.
-        int status = HWCDisplayExternal::Create(core_intf_, &hwc_procs_, primary_width,
-                                                primary_height, qservice_, true,
-                                                &hwc_display_[HWC_DISPLAY_PRIMARY]);
+        status = CreateExternalDisplay(HWC_DISPLAY_PRIMARY, primary_width, primary_height, true);
         if (status) {
           DLOGE("Could not create external display");
           return -1;
@@ -1681,5 +1680,23 @@
   return android::NO_ERROR;
 }
 
+int HWCSession::CreateExternalDisplay(int disp, uint32_t primary_width, uint32_t primary_height,
+                                      bool use_primary_res) {
+  uint32_t panel_bpp = 0;
+  uint32_t pattern_type = 0;
+
+  if (qdutils::isDPConnected()) {
+    qdutils::getDPTestConfig(&panel_bpp, &pattern_type);
+  }
+
+  if (panel_bpp && pattern_type) {
+    return HWCDisplayExternalTest::Create(core_intf_, &hwc_procs_, qservice_, panel_bpp,
+                                          pattern_type, &hwc_display_[disp]);
+  }
+
+  return HWCDisplayExternal::Create(core_intf_, &hwc_procs_, primary_width, primary_height,
+                                    qservice_, use_primary_res, &hwc_display_[disp]);
+}
+
 }  // namespace sdm
 
diff --git a/sdm/libs/hwc/hwc_session.h b/sdm/libs/hwc/hwc_session.h
index 6ef92eb..c3ffa98 100644
--- a/sdm/libs/hwc/hwc_session.h
+++ b/sdm/libs/hwc/hwc_session.h
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2014 - 2016, The Linux Foundation. All rights reserved.
+* Copyright (c) 2014 - 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:
@@ -86,6 +86,8 @@
   int DisconnectDisplay(int disp);
   void HandleSecureDisplaySession(hwc_display_contents_1_t **displays);
   int GetVsyncPeriod(int disp);
+  int CreateExternalDisplay(int disp, uint32_t primary_width, uint32_t primary_height,
+                            bool use_primary_res);
 
   // QClient methods
   virtual android::status_t notifyCallback(uint32_t command, const android::Parcel *input_parcel,
diff --git a/sdm/libs/hwc2/Android.mk b/sdm/libs/hwc2/Android.mk
index 75d7f9b..3351ced 100644
--- a/sdm/libs/hwc2/Android.mk
+++ b/sdm/libs/hwc2/Android.mk
@@ -17,7 +17,7 @@
 
 LOCAL_SHARED_LIBRARIES        := libsdmcore libqservice libbinder libhardware libhardware_legacy \
                                  libutils libcutils libsync libmemalloc libqdutils libdl \
-                                 libpowermanager libsdmutils libc++
+                                 libpowermanager libsdmutils libc++ liblog
 
 LOCAL_SRC_FILES               := hwc_session.cpp \
                                  hwc_display.cpp \
diff --git a/sdm/libs/utils/rect.cpp b/sdm/libs/utils/rect.cpp
index fc553a3..dd5a872 100644
--- a/sdm/libs/utils/rect.cpp
+++ b/sdm/libs/utils/rect.cpp
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+* Copyright (c) 2015-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
@@ -223,6 +223,20 @@
   out_rect->bottom = dst_domain.top + (height_ratio * modified_in_rect.bottom);
 }
 
+void TransformHV(const LayerRect &src_domain, const LayerRect &in_rect, LayerRect *out_rect) {
+  if (!IsValid(src_domain) || !IsValid(in_rect)) {
+    return;
+  }
+
+  float in_width = in_rect.right - in_rect.left;
+  float in_height = in_rect.bottom - in_rect.top;
+
+  out_rect->right = src_domain.right - in_rect.left;
+  out_rect->bottom = src_domain.bottom - in_rect.top;
+  out_rect->left = out_rect->right - in_width;
+  out_rect->top = out_rect->bottom - in_height;
+}
+
 RectOrientation GetOrientation(const LayerRect &in_rect) {
   if (!IsValid(in_rect)) {
     return kOrientationUnknown;