sde: Add support for custom framebuffer sizes

Set a custom framebuffer size for use cases such as panel
simulation. The framebuffer size is defined as the rendering
size or, alternatively, the size of the framebuffer target layer.

Change-Id: Idb30d16383ce334f4ffd629d48be1d3733d4bd16
diff --git a/displayengine/include/core/display_interface.h b/displayengine/include/core/display_interface.h
index c041e1a..651dd88 100644
--- a/displayengine/include/core/display_interface.h
+++ b/displayengine/include/core/display_interface.h
@@ -97,7 +97,7 @@
   float y_dpi;                //!< Dots per inch in Y-direction.
   float fps;                  //!< Frame rate per second.
   uint32_t vsync_period_ns;   //!< VSync period in nanoseconds.
-  uint32_t v_total;           //!< Total lines in Y-direction (vActive + vFP + vBP + vPulseWidth).
+  uint32_t v_total;           //!< Total height of panel (vActive + vFP + vBP + vPulseWidth).
   uint32_t h_total;           //!< Total width of panel (hActive + hFP + hBP + hPulseWidth).
 
   DisplayConfigVariableInfo() : x_pixels(0), y_pixels(0), x_dpi(0.0f), y_dpi(0.0f),
@@ -334,6 +334,13 @@
   */
   virtual DisplayError SetDisplayMode(uint32_t mode) = 0;
 
+  /*! @brief Method to determine whether scaling for a custom resolution is valid.
+
+    @return \link DisplayError \endlink
+  */
+  virtual DisplayError IsScalingValid(const LayerRect &crop, const LayerRect &dst,
+                                      bool rotate90) = 0;
+
  protected:
   virtual ~DisplayInterface() { }
 };
diff --git a/displayengine/libs/core/comp_manager.cpp b/displayengine/libs/core/comp_manager.cpp
index 82f7b8d..7dbb6b1 100644
--- a/displayengine/libs/core/comp_manager.cpp
+++ b/displayengine/libs/core/comp_manager.cpp
@@ -351,5 +351,10 @@
   SCOPE_LOCK(locker_);
 }
 
+DisplayError CompManager::ValidateScaling(const LayerRect &crop, const LayerRect &dst,
+                                          bool rotate90) {
+  return res_mgr_.ValidateScaling(crop, dst, rotate90);
+}
+
 }  // namespace sde
 
diff --git a/displayengine/libs/core/comp_manager.h b/displayengine/libs/core/comp_manager.h
index ae638e0..dd18a7f 100644
--- a/displayengine/libs/core/comp_manager.h
+++ b/displayengine/libs/core/comp_manager.h
@@ -49,6 +49,7 @@
   bool ProcessIdleTimeout(Handle display_ctx);
   void ProcessThermalEvent(Handle display_ctx, int64_t thermal_level);
   DisplayError SetMaxMixerStages(Handle display_ctx, uint32_t max_mixer_stages);
+  DisplayError ValidateScaling(const LayerRect &crop, const LayerRect &dst, bool rotate90);
 
   // DumpImpl method
   virtual void AppendDump(char *buffer, uint32_t length);
diff --git a/displayengine/libs/core/display_base.cpp b/displayengine/libs/core/display_base.cpp
index 47beac0..b65b8a5 100644
--- a/displayengine/libs/core/display_base.cpp
+++ b/displayengine/libs/core/display_base.cpp
@@ -407,6 +407,11 @@
   return kErrorNotSupported;
 }
 
+DisplayError DisplayBase::IsScalingValid(const LayerRect &crop, const LayerRect &dst,
+                                         bool rotate90) {
+  return comp_manager_->ValidateScaling(crop, dst, rotate90);
+}
+
 void DisplayBase::AppendDump(char *buffer, uint32_t length) {
   DumpImpl::AppendString(buffer, length, "\n-----------------------");
   DumpImpl::AppendString(buffer, length, "\ndevice type: %u", display_type_);
diff --git a/displayengine/libs/core/display_base.h b/displayengine/libs/core/display_base.h
index 0a33011..cd38d8a 100644
--- a/displayengine/libs/core/display_base.h
+++ b/displayengine/libs/core/display_base.h
@@ -59,6 +59,7 @@
   virtual DisplayError SetActiveConfig(uint32_t index);
   virtual DisplayError SetMaxMixerStages(uint32_t max_mixer_stages);
   virtual DisplayError SetDisplayMode(uint32_t mode);
+  virtual DisplayError IsScalingValid(const LayerRect &crop, const LayerRect &dst, bool rotate90);
 
  protected:
   // DumpImpl method
diff --git a/displayengine/libs/core/display_hdmi.cpp b/displayengine/libs/core/display_hdmi.cpp
index 65ea21f..f034719 100644
--- a/displayengine/libs/core/display_hdmi.cpp
+++ b/displayengine/libs/core/display_hdmi.cpp
@@ -152,6 +152,12 @@
   return DisplayBase::SetDisplayMode(mode);
 }
 
+DisplayError DisplayHDMI::IsScalingValid(const LayerRect &crop, const LayerRect &dst,
+                                         bool rotate90) {
+  SCOPE_LOCK(locker_);
+  return DisplayBase::IsScalingValid(crop, dst, rotate90);
+}
+
 int DisplayHDMI::GetBestConfig() {
   uint32_t best_config_mode = 0;
   HWDisplayAttributes *best = &display_attributes_[0];
diff --git a/displayengine/libs/core/display_hdmi.h b/displayengine/libs/core/display_hdmi.h
index ea1b80c..6543ee3 100644
--- a/displayengine/libs/core/display_hdmi.h
+++ b/displayengine/libs/core/display_hdmi.h
@@ -56,6 +56,7 @@
   virtual void SetIdleTimeoutMs(uint32_t timeout_ms);
   virtual DisplayError SetMaxMixerStages(uint32_t max_mixer_stages);
   virtual DisplayError SetDisplayMode(uint32_t mode);
+  virtual DisplayError IsScalingValid(const LayerRect &crop, const LayerRect &dst, bool rotate90);
   virtual void AppendDump(char *buffer, uint32_t length);
 
  private:
diff --git a/displayengine/libs/core/display_primary.cpp b/displayengine/libs/core/display_primary.cpp
index 0fa5d2b..2466b92 100644
--- a/displayengine/libs/core/display_primary.cpp
+++ b/displayengine/libs/core/display_primary.cpp
@@ -214,6 +214,12 @@
   return error;
 }
 
+DisplayError DisplayPrimary::IsScalingValid(const LayerRect &crop, const LayerRect &dst,
+                                            bool rotate90) {
+  SCOPE_LOCK(locker_);
+  return DisplayBase::IsScalingValid(crop, dst, rotate90);
+}
+
 void DisplayPrimary::AppendDump(char *buffer, uint32_t length) {
   SCOPE_LOCK(locker_);
   DisplayBase::AppendDump(buffer, length);
diff --git a/displayengine/libs/core/display_primary.h b/displayengine/libs/core/display_primary.h
index 1ed28b2..6f1b574 100644
--- a/displayengine/libs/core/display_primary.h
+++ b/displayengine/libs/core/display_primary.h
@@ -56,6 +56,7 @@
   virtual void SetIdleTimeoutMs(uint32_t timeout_ms);
   virtual DisplayError SetMaxMixerStages(uint32_t max_mixer_stages);
   virtual DisplayError SetDisplayMode(uint32_t mode);
+  virtual DisplayError IsScalingValid(const LayerRect &crop, const LayerRect &dst, bool rotate90);
   virtual void AppendDump(char *buffer, uint32_t length);
 
   // Implement the HWEventHandlers
diff --git a/displayengine/libs/core/display_virtual.cpp b/displayengine/libs/core/display_virtual.cpp
index 5480f0e..9e15469 100644
--- a/displayengine/libs/core/display_virtual.cpp
+++ b/displayengine/libs/core/display_virtual.cpp
@@ -152,6 +152,12 @@
   return DisplayBase::SetDisplayMode(mode);
 }
 
+DisplayError DisplayVirtual::IsScalingValid(const LayerRect &crop, const LayerRect &dst,
+                                            bool rotate90) {
+  SCOPE_LOCK(locker_);
+  return DisplayBase::IsScalingValid(crop, dst, rotate90);
+}
+
 void DisplayVirtual::AppendDump(char *buffer, uint32_t length) {
   SCOPE_LOCK(locker_);
   DisplayBase::AppendDump(buffer, length);
diff --git a/displayengine/libs/core/display_virtual.h b/displayengine/libs/core/display_virtual.h
index f868612..2a5c324 100644
--- a/displayengine/libs/core/display_virtual.h
+++ b/displayengine/libs/core/display_virtual.h
@@ -56,6 +56,7 @@
   virtual void SetIdleTimeoutMs(uint32_t timeout_ms);
   virtual DisplayError SetMaxMixerStages(uint32_t max_mixer_stages);
   virtual DisplayError SetDisplayMode(uint32_t mode);
+  virtual DisplayError IsScalingValid(const LayerRect &crop, const LayerRect &dst, bool rotate90);
   virtual void AppendDump(char *buffer, uint32_t length);
 
  private:
diff --git a/displayengine/libs/hwc/hwc_display.cpp b/displayengine/libs/hwc/hwc_display.cpp
index 724b720..bf82a75 100644
--- a/displayengine/libs/hwc/hwc_display.cpp
+++ b/displayengine/libs/hwc/hwc_display.cpp
@@ -46,7 +46,8 @@
                        int id)
   : core_intf_(core_intf), hwc_procs_(hwc_procs), type_(type), id_(id), display_intf_(NULL),
     flush_(false), output_buffer_(NULL), dump_frame_count_(0), dump_frame_index_(0),
-    dump_input_layers_(false), swap_interval_zero_(false) {
+    dump_input_layers_(false), swap_interval_zero_(false), framebuffer_config_(NULL),
+    display_paused_(false) {
 }
 
 int HWCDisplay::Init() {
@@ -64,6 +65,13 @@
     }
   }
 
+  framebuffer_config_ = new DisplayConfigVariableInfo();
+  if (!framebuffer_config_) {
+    DLOGV("Failed to allocate memory for custom framebuffer config.");
+    core_intf_->DestroyDisplay(display_intf_);
+    return -EINVAL;
+  }
+
   return 0;
 }
 
@@ -79,6 +87,8 @@
     layer_stack_memory_.raw = NULL;
   }
 
+  delete framebuffer_config_;
+
   return 0;
 }
 
@@ -150,10 +160,15 @@
   DisplayError error = kErrorNone;
 
   DisplayConfigVariableInfo variable_config;
-  error = display_intf_->GetConfig(config, &variable_config);
-  if (error != kErrorNone) {
-    DLOGE("GetConfig variable info failed. Error = %d", error);
-    return -EINVAL;
+  uint32_t active_config = UINT32(GetActiveConfig());
+  if (IsFrameBufferScaled() && config == active_config) {
+    variable_config = *framebuffer_config_;
+  } else {
+    error = display_intf_->GetConfig(config, &variable_config);
+    if (error != kErrorNone) {
+      DLOGE("GetConfig variable info failed. Error = %d", error);
+      return -EINVAL;
+    }
   }
 
   for (int i = 0; attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE; i++) {
@@ -368,7 +383,10 @@
       }
     }
 
-    SetRect(hwc_layer.displayFrame, &layer.dst_rect);
+    hwc_rect_t scaled_display_frame = hwc_layer.displayFrame;
+    ScaleDisplayFrame(&scaled_display_frame);
+
+    SetRect(scaled_display_frame, &layer.dst_rect);
     SetRect(hwc_layer.sourceCropf, &layer.src_rect);
     for (size_t j = 0; j < hwc_layer.visibleRegionScreen.numRects; j++) {
         SetRect(hwc_layer.visibleRegionScreen.rects[j], &layer.visible_regions.rect[j]);
@@ -810,5 +828,151 @@
   }
 }
 
+int HWCDisplay::SetFrameBufferResolution(uint32_t x_pixels, uint32_t y_pixels) {
+  if (x_pixels <= 0 || y_pixels <= 0) {
+    DLOGV("Unsupported config: x_pixels=%d, y_pixels=%d", x_pixels, y_pixels);
+    return -EINVAL;
+  }
+
+  if (framebuffer_config_->x_pixels == x_pixels && framebuffer_config_->y_pixels == y_pixels) {
+    return 0;
+  }
+
+  DisplayConfigVariableInfo active_config;
+  int active_config_index = GetActiveConfig();
+  DisplayError error = display_intf_->GetConfig(active_config_index, &active_config);
+  if (error != kErrorNone) {
+    DLOGV("GetConfig variable info failed. Error = %d", error);
+    return -EINVAL;
+  }
+
+  if (active_config.x_pixels <= 0 || active_config.y_pixels <= 0) {
+    DLOGV("Invalid panel resolution (%dx%d)", active_config.x_pixels, active_config.y_pixels);
+    return -EINVAL;
+  }
+
+  // Create rects to represent the new source and destination crops
+  LayerRect crop = LayerRect(0, 0, FLOAT(x_pixels), FLOAT(y_pixels));
+  LayerRect dst = LayerRect(0, 0, FLOAT(active_config.x_pixels), FLOAT(active_config.y_pixels));
+  // Set rotate90 to false since this is taken care of during regular composition.
+  bool rotate90 = false;
+  error = display_intf_->IsScalingValid(crop, dst, rotate90);
+  if (error != kErrorNone) {
+    DLOGV("Unsupported resolution: (%dx%d)", x_pixels, y_pixels);
+    return -EINVAL;
+  }
+
+  uint32_t panel_width =
+          UINT32((FLOAT(active_config.x_pixels) * 25.4f) / FLOAT(active_config.x_dpi));
+  uint32_t panel_height =
+          UINT32((FLOAT(active_config.y_pixels) * 25.4f) / FLOAT(active_config.y_dpi));
+  framebuffer_config_->x_pixels = x_pixels;
+  framebuffer_config_->y_pixels = y_pixels;
+  framebuffer_config_->vsync_period_ns = active_config.vsync_period_ns;
+  framebuffer_config_->x_dpi =
+          (FLOAT(framebuffer_config_->x_pixels) * 25.4f) / FLOAT(panel_width);
+  framebuffer_config_->y_dpi =
+          (FLOAT(framebuffer_config_->y_pixels) * 25.4f) / FLOAT(panel_height);
+
+  DLOGI("New framebuffer resolution (%dx%d)", framebuffer_config_->x_pixels,
+        framebuffer_config_->y_pixels);
+
+  return 0;
+}
+
+void HWCDisplay::GetFrameBufferResolution(uint32_t *x_pixels, uint32_t *y_pixels) {
+  *x_pixels = framebuffer_config_->x_pixels;
+  *y_pixels = framebuffer_config_->y_pixels;
+}
+
+void HWCDisplay::ScaleDisplayFrame(hwc_rect_t *display_frame) {
+  if (!IsFrameBufferScaled()) {
+    return;
+  }
+
+  int active_config_index = GetActiveConfig();
+  DisplayConfigVariableInfo active_config;
+  DisplayError error = display_intf_->GetConfig(active_config_index, &active_config);
+  if (error != kErrorNone) {
+    DLOGE("GetConfig variable info failed. Error = %d", error);
+    return;
+  }
+
+  float custom_x_pixels = FLOAT(framebuffer_config_->x_pixels);
+  float custom_y_pixels = FLOAT(framebuffer_config_->y_pixels);
+  float active_x_pixels = FLOAT(active_config.x_pixels);
+  float active_y_pixels = FLOAT(active_config.y_pixels);
+  float x_pixels_ratio = active_x_pixels / custom_x_pixels;
+  float y_pixels_ratio = active_y_pixels / custom_y_pixels;
+  float layer_width = FLOAT(display_frame->right - display_frame->left);
+  float layer_height = FLOAT(display_frame->bottom - display_frame->top);
+
+  display_frame->left = INT(x_pixels_ratio * FLOAT(display_frame->left));
+  display_frame->top = INT(y_pixels_ratio * FLOAT(display_frame->top));
+  display_frame->right = INT(FLOAT(display_frame->left) + layer_width * x_pixels_ratio);
+  display_frame->bottom = INT(FLOAT(display_frame->top) + layer_height * y_pixels_ratio);
+}
+
+bool HWCDisplay::IsFrameBufferScaled() {
+  if (framebuffer_config_->x_pixels == 0 || framebuffer_config_->y_pixels == 0) {
+    return false;
+  }
+  uint32_t panel_x_pixels = 0;
+  uint32_t panel_y_pixels = 0;
+  GetPanelResolution(&panel_x_pixels, &panel_y_pixels);
+  return (framebuffer_config_->x_pixels != panel_x_pixels) ||
+          (framebuffer_config_->y_pixels != panel_y_pixels);
+}
+
+void HWCDisplay::GetPanelResolution(uint32_t *x_pixels, uint32_t *y_pixels) {
+  DisplayConfigVariableInfo active_config;
+  int active_config_index = GetActiveConfig();
+  DisplayError error = display_intf_->GetConfig(active_config_index, &active_config);
+  if (error != kErrorNone) {
+    DLOGE("GetConfig variable info failed. Error = %d", error);
+    return;
+  }
+  *x_pixels = active_config.x_pixels;
+  *y_pixels = active_config.y_pixels;
+}
+
+int HWCDisplay::SetDisplayStatus(uint32_t display_status) {
+  int status = 0;
+
+  switch (display_status) {
+  case kDisplayStatusResume:
+    display_paused_ = false;
+  case kDisplayStatusOnline:
+    status = SetPowerMode(HWC_POWER_MODE_NORMAL);
+    break;
+  case kDisplayStatusPause:
+    display_paused_ = true;
+  case kDisplayStatusOffline:
+    status = SetPowerMode(HWC_POWER_MODE_OFF);
+    break;
+  default:
+    DLOGW("Invalid display status %d", display_status);
+    return -EINVAL;
+  }
+
+  return status;
+}
+
+void HWCDisplay::MarkLayersForGPUBypass(hwc_display_contents_1_t *content_list) {
+  for (size_t i = 0 ; i < (content_list->numHwLayers - 1); i++) {
+    hwc_layer_1_t *layer = &content_list->hwLayers[i];
+    layer->compositionType = HWC_OVERLAY;
+  }
+}
+
+void HWCDisplay::CloseAcquireFences(hwc_display_contents_1_t *content_list) {
+  for (size_t i = 0; i < content_list->numHwLayers; i++) {
+    if (content_list->hwLayers[i].acquireFenceFd >= 0) {
+      close(content_list->hwLayers[i].acquireFenceFd);
+      content_list->hwLayers[i].acquireFenceFd = -1;
+    }
+  }
+}
+
 }  // namespace sde
 
diff --git a/displayengine/libs/hwc/hwc_display.h b/displayengine/libs/hwc/hwc_display.h
index d14699f..bcb037f 100644
--- a/displayengine/libs/hwc/hwc_display.h
+++ b/displayengine/libs/hwc/hwc_display.h
@@ -47,8 +47,19 @@
   virtual void SetFrameDumpConfig(uint32_t count, uint32_t bit_mask_layer_type);
   virtual DisplayError SetMaxMixerStages(uint32_t max_mixer_stages);
   virtual uint32_t GetLastPowerMode();
+  virtual int SetFrameBufferResolution(uint32_t x_pixels, uint32_t y_pixels);
+  virtual void GetFrameBufferResolution(uint32_t *x_pixels, uint32_t *y_pixels);
+  virtual void GetPanelResolution(uint32_t *x_pixels, uint32_t *y_pixels);
+  virtual int SetDisplayStatus(uint32_t display_status);
 
  protected:
+  enum DisplayStatus {
+    kDisplayStatusOffline = 0,
+    kDisplayStatusOnline,
+    kDisplayStatusPause,
+    kDisplayStatusResume,
+  };
+
   // Maximum number of layers supported by display engine.
   static const uint32_t kMaxLayerCount = 32;
 
@@ -99,6 +110,10 @@
   void DumpInputBuffers(hwc_display_contents_1_t *content_list);
   const char *GetHALPixelFormatString(int format);
   const char *GetDisplayString();
+  void ScaleDisplayFrame(hwc_rect_t *display_frame);
+  bool IsFrameBufferScaled();
+  void MarkLayersForGPUBypass(hwc_display_contents_1_t *content_list);
+  void CloseAcquireFences(hwc_display_contents_1_t *content_list);
 
   enum {
     INPUT_LAYER_DUMP,
@@ -120,6 +135,8 @@
   bool dump_input_layers_;
   uint32_t last_power_mode_;
   bool swap_interval_zero_;
+  DisplayConfigVariableInfo *framebuffer_config_;
+  bool display_paused_;
 };
 
 }  // namespace sde
diff --git a/displayengine/libs/hwc/hwc_display_virtual.cpp b/displayengine/libs/hwc/hwc_display_virtual.cpp
index 9601e63..acd7948 100644
--- a/displayengine/libs/hwc/hwc_display_virtual.cpp
+++ b/displayengine/libs/hwc/hwc_display_virtual.cpp
@@ -28,7 +28,6 @@
 */
 
 #include <utils/constants.h>
-#include <gralloc_priv.h>
 #include <sync/sync.h>
 
 #include "hwc_display_virtual.h"
@@ -71,6 +70,11 @@
 
 int HWCDisplayVirtual::Prepare(hwc_display_contents_1_t *content_list) {
   int status = 0;
+  if (display_paused_) {
+    MarkLayersForGPUBypass(content_list);
+    return status;
+  }
+
   status = AllocateLayerStack(content_list);
   if (status) {
     return status;
@@ -91,6 +95,22 @@
 
 int HWCDisplayVirtual::Commit(hwc_display_contents_1_t *content_list) {
   int status = 0;
+  if (display_paused_) {
+    if (content_list->outbufAcquireFenceFd >= 0) {
+      // If we do not handle the frame set retireFenceFd to outbufAcquireFenceFd,
+      // which will make sure the framework waits on it and closes it.
+      content_list->retireFenceFd = dup(content_list->outbufAcquireFenceFd);
+      close(content_list->outbufAcquireFenceFd);
+      content_list->outbufAcquireFenceFd = -1;
+    }
+    CloseAcquireFences(content_list);
+
+    DisplayError error = display_intf_->Flush();
+    if (error != kErrorNone) {
+      DLOGE("Flush failed. Error = %d", error);
+    }
+    return status;
+  }
 
   status = HWCDisplay::CommitLayerStack(content_list);
   if (status) {
@@ -124,13 +144,15 @@
       return -EINVAL;
     }
 
-    if ((output_handle->width != INT(output_buffer_->width)) ||
-        (output_handle->height != INT(output_buffer_->height)) ||
+    int active_width = GetWidth(output_handle);
+    int active_height = GetHeight(output_handle);
+
+    if ((active_width != INT(output_buffer_->width)) ||
+        (active_height!= INT(output_buffer_->height)) ||
         (format != output_buffer_->format)) {
       DisplayConfigVariableInfo variable_info;
-
-      variable_info.x_pixels = output_handle->width;
-      variable_info.y_pixels = output_handle->height;
+      variable_info.x_pixels = active_width;
+      variable_info.y_pixels = active_height;
       // TODO(user): Need to get the framerate of primary display and update it.
       variable_info.fps = 60;
 
@@ -164,8 +186,8 @@
       return -EINVAL;
     }
 
-    output_buffer_->width = output_handle->width;
-    output_buffer_->height = output_handle->height;
+    output_buffer_->width = GetWidth(output_handle);
+    output_buffer_->height = GetHeight(output_handle);
     output_buffer_->flags.secure = 0;
     output_buffer_->flags.video = 0;
 
@@ -234,5 +256,23 @@
   DLOGI("output_layer_dump_enable %d", dump_output_layer_);
 }
 
+int HWCDisplayVirtual::GetWidth(const private_handle_t* handle) {
+  MetaData_t *metadata = reinterpret_cast<MetaData_t *>(handle->base_metadata);
+  if (metadata && metadata->operation & UPDATE_BUFFER_GEOMETRY) {
+    return metadata->bufferDim.sliceWidth;
+  }
+
+  return handle->width;
+}
+
+int HWCDisplayVirtual::GetHeight(const private_handle_t* handle) {
+  MetaData_t *metadata = reinterpret_cast<MetaData_t *>(handle->base_metadata);
+  if (metadata && metadata->operation & UPDATE_BUFFER_GEOMETRY) {
+    return metadata->bufferDim.sliceHeight;
+  }
+
+  return handle->height;
+}
+
 }  // namespace sde
 
diff --git a/displayengine/libs/hwc/hwc_display_virtual.h b/displayengine/libs/hwc/hwc_display_virtual.h
index 7ac7c22..3042b12 100644
--- a/displayengine/libs/hwc/hwc_display_virtual.h
+++ b/displayengine/libs/hwc/hwc_display_virtual.h
@@ -25,6 +25,8 @@
 #ifndef __HWC_DISPLAY_VIRTUAL_H__
 #define __HWC_DISPLAY_VIRTUAL_H__
 
+#include <qdMetaData.h>
+#include <gralloc_priv.h>
 #include "hwc_display.h"
 
 namespace sde {
@@ -42,6 +44,8 @@
  private:
   int SetOutputBuffer(hwc_display_contents_1_t *content_list);
   void DumpOutputBuffer(hwc_display_contents_1_t *content_list);
+  int GetWidth(const private_handle_t* handle);
+  int GetHeight(const private_handle_t* handle);
 
   bool dump_output_layer_;
 };
diff --git a/displayengine/libs/hwc/hwc_session.cpp b/displayengine/libs/hwc/hwc_session.cpp
index 450565e..f663889 100644
--- a/displayengine/libs/hwc/hwc_session.cpp
+++ b/displayengine/libs/hwc/hwc_session.cpp
@@ -28,14 +28,17 @@
 */
 
 #include <core/dump_interface.h>
+#include <core/buffer_allocator.h>
 #include <utils/constants.h>
 #include <utils/String16.h>
+#include <cutils/properties.h>
 #include <hardware_legacy/uevent.h>
 #include <sys/resource.h>
 #include <sys/prctl.h>
 #include <binder/Parcel.h>
 #include <QService.h>
-#include <core/buffer_allocator.h>
+#include <gr.h>
+#include <gralloc_priv.h>
 
 #include "hwc_buffer_allocator.h"
 #include "hwc_buffer_sync_handler.h"
@@ -153,6 +156,8 @@
     return -errno;
   }
 
+  SetFrameBufferResolution(HWC_DISPLAY_PRIMARY, NULL);
+
   return 0;
 }
 
@@ -556,6 +561,7 @@
   }
 
   if (display_virtual_) {
+    SetFrameBufferResolution(HWC_DISPLAY_VIRTUAL, content_list);
     status = display_virtual_->SetActiveConfig(content_list);
   }
 
@@ -618,6 +624,9 @@
   case qService::IQService::SET_DISPLAY_MODE:
     status = SetDisplayMode(input_parcel);
     break;
+  case qService::IQService::SET_SECONDARY_DISPLAY_STATUS:
+    status = SetSecondaryDisplayStatus(input_parcel);
+    break;
 
   default:
     DLOGW("QService command = %d is not supported", command);
@@ -627,6 +636,27 @@
   return status;
 }
 
+android::status_t HWCSession::SetSecondaryDisplayStatus(const android::Parcel *input_parcel) {
+  uint32_t display_id = UINT32(input_parcel->readInt32());
+  uint32_t display_status = UINT32(input_parcel->readInt32());
+  HWCDisplay *display = NULL;
+
+  DLOGI("Display %d Status %d", display_id, display_status);
+  switch (display_id) {
+  case HWC_DISPLAY_EXTERNAL:
+    display = display_external_;
+    break;
+  case HWC_DISPLAY_VIRTUAL:
+    display = display_virtual_;
+    break;
+  default:
+    DLOGW("Not supported for display %d", display_id);
+    return -EINVAL;
+  }
+
+  return display->SetDisplayStatus(display_status);
+}
+
 android::status_t HWCSession::SetDisplayMode(const android::Parcel *input_parcel) {
   DisplayError error = kErrorNone;
   uint32_t mode = UINT32(input_parcel->readInt32());
@@ -771,8 +801,9 @@
         if (hwc_procs_) {
           reset_panel_ = true;
           hwc_procs_->invalidate(hwc_procs_);
-        } else
+        } else {
           DLOGW("Ignore resetpanel - hwc_proc not registered");
+        }
       }
     }
   }
@@ -800,19 +831,19 @@
   DLOGI("Powering off primary");
   status = display_primary_->SetPowerMode(HWC_POWER_MODE_OFF);
   if (status) {
-    DLOGE("power-off on primary failed with error = %d",status);
+    DLOGE("power-off on primary failed with error = %d", status);
   }
 
   DLOGI("Restoring power mode on primary");
   uint32_t mode = display_primary_->GetLastPowerMode();
   status = display_primary_->SetPowerMode(mode);
   if (status) {
-    DLOGE("Setting power mode = %d on primary failed with error = %d", mode,status);
+    DLOGE("Setting power mode = %d on primary failed with error = %d", mode, status);
   }
 
   status = display_primary_->EventControl(HWC_EVENT_VSYNC, 1);
   if (status) {
-    DLOGE("enabling vsync failed for primary with error = %d",status);
+    DLOGE("enabling vsync failed for primary with error = %d", status);
   }
 
   reset_panel_ = false;
@@ -849,6 +880,7 @@
       display_external_ = NULL;
       return -1;
     }
+    SetFrameBufferResolution(HWC_DISPLAY_EXTERNAL, NULL);
   } else {
     SEQUENCE_WAIT_SCOPE_LOCK(locker_);
     if (!display_external_) {
@@ -868,5 +900,72 @@
   return 0;
 }
 
+void HWCSession::SetFrameBufferResolution(int disp, hwc_display_contents_1_t *content_list) {
+  char property[PROPERTY_VALUE_MAX];
+  uint32_t primary_width = 0;
+  uint32_t primary_height = 0;
+
+  switch (disp) {
+  case HWC_DISPLAY_PRIMARY:
+  {
+    display_primary_->GetPanelResolution(&primary_width, &primary_height);
+    if (property_get("debug.hwc.fbsize", property, NULL) > 0) {
+      char *yptr = strcasestr(property, "x");
+      primary_width = atoi(property);
+      primary_height = atoi(yptr + 1);
+    }
+    display_primary_->SetFrameBufferResolution(primary_width, primary_height);
+    break;
+  }
+
+  case HWC_DISPLAY_EXTERNAL:
+  {
+    uint32_t external_width = 0;
+    uint32_t external_height = 0;
+    display_external_->GetPanelResolution(&external_width, &external_height);
+
+    if (property_get("sys.hwc.mdp_downscale_enabled", property, "false") &&
+        !strcmp(property, "true")) {
+      display_primary_->GetFrameBufferResolution(&primary_width, &primary_height);
+      uint32_t primary_area = primary_width * primary_height;
+      uint32_t external_area = external_width * external_height;
+
+      if (primary_area > external_area) {
+        if (primary_height > primary_width) {
+          Swap(primary_height, primary_width);
+        }
+        AdjustSourceResolution(primary_width, primary_height,
+                               &external_width, &external_height);
+      }
+    }
+    display_external_->SetFrameBufferResolution(external_width, external_height);
+    break;
+  }
+
+  case HWC_DISPLAY_VIRTUAL:
+  {
+    if (ValidateContentList(content_list)) {
+      const private_handle_t *output_handle =
+              static_cast<const private_handle_t *>(content_list->outbuf);
+      int virtual_width = 0;
+      int virtual_height = 0;
+      getBufferSizeAndDimensions(output_handle->width, output_handle->height, output_handle->format,
+                                 virtual_width, virtual_height);
+      display_virtual_->SetFrameBufferResolution(virtual_width, virtual_height);
+    }
+    break;
+  }
+
+  default:
+    break;
+  }
+}
+
+void HWCSession::AdjustSourceResolution(uint32_t dst_width, uint32_t dst_height,
+                                        uint32_t *src_width, uint32_t *src_height) {
+  *src_height = (dst_width * (*src_height)) / (*src_width);
+  *src_width = dst_width;
+}
+
 }  // namespace sde
 
diff --git a/displayengine/libs/hwc/hwc_session.h b/displayengine/libs/hwc/hwc_session.h
index 54ab852..9d5e3d5 100644
--- a/displayengine/libs/hwc/hwc_session.h
+++ b/displayengine/libs/hwc/hwc_session.h
@@ -90,6 +90,10 @@
   void SetFrameDumpConfig(const android::Parcel *input_parcel);
   android::status_t SetMaxMixerStages(const android::Parcel *input_parcel);
   android::status_t SetDisplayMode(const android::Parcel *input_parcel);
+  android::status_t SetSecondaryDisplayStatus(const android::Parcel *input_parcel);
+  void SetFrameBufferResolution(int disp, hwc_display_contents_1_t *content_list);
+  void AdjustSourceResolution(uint32_t dst_width, uint32_t dst_height,
+                              uint32_t *src_width, uint32_t *src_height);
 
   static Locker locker_;
   CoreInterface *core_intf_;