Merge "sde: Add support to read HDMI scan info"
diff --git a/displayengine/include/core/display_interface.h b/displayengine/include/core/display_interface.h
index f8f2701..3042eb2 100644
--- a/displayengine/include/core/display_interface.h
+++ b/displayengine/include/core/display_interface.h
@@ -349,6 +349,12 @@
   */
   virtual DisplayError SetRefreshRate(uint32_t refresh_rate) = 0;
 
+  /*! @brief Method to query whether scanning is support for the HDMI display.
+
+    @return \link DisplayError \endlink
+  */
+  virtual bool IsUnderscanSupported() = 0;
+
  protected:
   virtual ~DisplayInterface() { }
 };
diff --git a/displayengine/include/private/hw_info_types.h b/displayengine/include/private/hw_info_types.h
index 58293a7..03a77ae 100644
--- a/displayengine/include/private/hw_info_types.h
+++ b/displayengine/include/private/hw_info_types.h
@@ -106,6 +106,24 @@
   kPortEDP,
 };
 
+/*! @brief This enumeration holds the all possible scan types. */
+enum HWScanSupport {
+  kScanNotSupported,
+  kScanAlwaysOverscanned,
+  kScanAlwaysUnderscanned,
+  kScanBoth,
+};
+
+/*! @brief This structure holds the scan support for different timing modes. */
+struct HWScanInfo {
+  HWScanSupport pt_scan_support;  //!< Scan support for preferred timing
+  HWScanSupport it_scan_support;  //!< Scan support for digital monitor or industry timings
+  HWScanSupport cea_scan_support; //!< Scan support for CEA resolution timings
+
+  HWScanInfo() : pt_scan_support(kScanNotSupported), it_scan_support(kScanNotSupported),
+                 cea_scan_support(kScanNotSupported) { }
+};
+
 /*! @brief This structure describes the split configuration of a display panel. */
 struct HWSplitInfo {
   uint32_t left_split;
diff --git a/displayengine/libs/core/display_base.cpp b/displayengine/libs/core/display_base.cpp
index dab3e91..b64eae7 100644
--- a/displayengine/libs/core/display_base.cpp
+++ b/displayengine/libs/core/display_base.cpp
@@ -41,7 +41,7 @@
     buffer_sync_handler_(buffer_sync_handler), comp_manager_(comp_manager),
     rotator_ctrl_(rotator_ctrl), state_(kStateOff), hw_device_(0), display_comp_ctx_(0),
     display_attributes_(NULL), num_modes_(0), active_mode_index_(0), pending_commit_(false),
-    vsync_enable_(false) {
+    vsync_enable_(false), underscan_supported_(false) {
 }
 
 DisplayError DisplayBase::Init() {
@@ -290,6 +290,10 @@
   return kErrorNone;
 }
 
+bool DisplayBase::IsUnderscanSupported() {
+  return underscan_supported_;
+}
+
 DisplayError DisplayBase::SetDisplayState(DisplayState state) {
   DisplayError error = kErrorNone;
 
diff --git a/displayengine/libs/core/display_base.h b/displayengine/libs/core/display_base.h
index 848dea1..ef1ea1f 100644
--- a/displayengine/libs/core/display_base.h
+++ b/displayengine/libs/core/display_base.h
@@ -59,6 +59,7 @@
   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 bool IsUnderscanSupported();
 
  protected:
   // DumpImpl method
@@ -87,6 +88,7 @@
   HWLayers hw_layers_;
   bool pending_commit_;
   bool vsync_enable_;
+  bool underscan_supported_;
 };
 
 }  // namespace sde
diff --git a/displayengine/libs/core/display_hdmi.cpp b/displayengine/libs/core/display_hdmi.cpp
index 22c97b5..a36f2f2 100644
--- a/displayengine/libs/core/display_hdmi.cpp
+++ b/displayengine/libs/core/display_hdmi.cpp
@@ -60,6 +60,9 @@
     HWHDMIInterface::Destroy(hw_hdmi_intf_);
   }
 
+  GetScanSupport();
+  underscan_supported_ = (scan_support_ == kScanAlwaysUnderscanned) || (scan_support_ == kScanBoth);
+
   return error;
 }
 
@@ -163,6 +166,11 @@
   return kErrorNotSupported;
 }
 
+bool DisplayHDMI::IsUnderscanSupported() {
+  SCOPE_LOCK(locker_);
+  return DisplayBase::IsUnderscanSupported();
+}
+
 int DisplayHDMI::GetBestConfig() {
   uint32_t best_config_mode = 0;
   HWDisplayAttributes *best = &display_attributes_[0];
@@ -204,6 +212,36 @@
   return best_config_mode;
 }
 
+void DisplayHDMI::GetScanSupport() {
+  DisplayError error = kErrorNone;
+  uint32_t video_format = -1;
+  uint32_t max_cea_format = -1;
+  HWScanInfo scan_info = HWScanInfo();
+  hw_hdmi_intf_->GetHWScanInfo(&scan_info);
+
+  error = hw_hdmi_intf_->GetVideoFormat(active_mode_index_, &video_format);
+  if (error != kErrorNone) {
+    return;
+  }
+
+  error = hw_hdmi_intf_->GetMaxCEAFormat(&max_cea_format);
+  if (error != kErrorNone) {
+    return;
+  }
+
+  // The scan support for a given HDMI TV must be read from scan info corresponding to
+  // Preferred Timing if the preferred timing of the display is currently active, and if it is
+  // valid. In all other cases, we must read the scan support from CEA scan info if
+  // the resolution is a CEA resolution, or from IT scan info for all other resolutions.
+  if (active_mode_index_ == 0 && scan_info.pt_scan_support != kScanNotSupported) {
+    scan_support_ = scan_info.pt_scan_support;
+  } else if (video_format < max_cea_format) {
+    scan_support_ = scan_info.cea_scan_support;
+  } else {
+    scan_support_ = scan_info.it_scan_support;
+  }
+}
+
 void DisplayHDMI::AppendDump(char *buffer, uint32_t length) {
   SCOPE_LOCK(locker_);
   DisplayBase::AppendDump(buffer, length);
diff --git a/displayengine/libs/core/display_hdmi.h b/displayengine/libs/core/display_hdmi.h
index ea60c41..b5a25cc 100644
--- a/displayengine/libs/core/display_hdmi.h
+++ b/displayengine/libs/core/display_hdmi.h
@@ -58,14 +58,17 @@
   virtual DisplayError SetDisplayMode(uint32_t mode);
   virtual DisplayError IsScalingValid(const LayerRect &crop, const LayerRect &dst, bool rotate90);
   virtual DisplayError SetRefreshRate(uint32_t refresh_rate);
+  virtual bool IsUnderscanSupported();
   virtual void AppendDump(char *buffer, uint32_t length);
 
  private:
   virtual int GetBestConfig();
+  virtual void GetScanSupport();
 
   Locker locker_;
   HWHDMIInterface *hw_hdmi_intf_;
   HWInfoInterface *hw_info_intf_;
+  HWScanSupport scan_support_;
 };
 
 }  // namespace sde
diff --git a/displayengine/libs/core/display_primary.cpp b/displayengine/libs/core/display_primary.cpp
index a196026..8c56799 100644
--- a/displayengine/libs/core/display_primary.cpp
+++ b/displayengine/libs/core/display_primary.cpp
@@ -146,6 +146,11 @@
   return DisplayBase::GetVSyncState(enabled);
 }
 
+bool DisplayPrimary::IsUnderscanSupported() {
+  SCOPE_LOCK(locker_);
+  return DisplayBase::IsUnderscanSupported();
+}
+
 DisplayError DisplayPrimary::SetDisplayState(DisplayState state) {
   SCOPE_LOCK(locker_);
   return DisplayBase::SetDisplayState(state);
diff --git a/displayengine/libs/core/display_primary.h b/displayengine/libs/core/display_primary.h
index 2879aa8..11f9687 100644
--- a/displayengine/libs/core/display_primary.h
+++ b/displayengine/libs/core/display_primary.h
@@ -58,6 +58,7 @@
   virtual DisplayError SetDisplayMode(uint32_t mode);
   virtual DisplayError IsScalingValid(const LayerRect &crop, const LayerRect &dst, bool rotate90);
   virtual DisplayError SetRefreshRate(uint32_t refresh_rate);
+  virtual bool IsUnderscanSupported();
   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 a4587c3..30588dc 100644
--- a/displayengine/libs/core/display_virtual.cpp
+++ b/displayengine/libs/core/display_virtual.cpp
@@ -120,6 +120,11 @@
   return DisplayBase::GetVSyncState(enabled);
 }
 
+bool DisplayVirtual::IsUnderscanSupported() {
+  SCOPE_LOCK(locker_);
+  return DisplayBase::IsUnderscanSupported();
+}
+
 DisplayError DisplayVirtual::SetDisplayState(DisplayState state) {
   SCOPE_LOCK(locker_);
   return DisplayBase::SetDisplayState(state);
diff --git a/displayengine/libs/core/display_virtual.h b/displayengine/libs/core/display_virtual.h
index d6c0fbf..6350e26 100644
--- a/displayengine/libs/core/display_virtual.h
+++ b/displayengine/libs/core/display_virtual.h
@@ -58,6 +58,7 @@
   virtual DisplayError SetDisplayMode(uint32_t mode);
   virtual DisplayError IsScalingValid(const LayerRect &crop, const LayerRect &dst, bool rotate90);
   virtual DisplayError SetRefreshRate(uint32_t refresh_rate);
+  virtual bool IsUnderscanSupported();
   virtual void AppendDump(char *buffer, uint32_t length);
 
  private:
diff --git a/displayengine/libs/core/fb/hw_device.h b/displayengine/libs/core/fb/hw_device.h
index 18c6652..e25709e 100644
--- a/displayengine/libs/core/fb/hw_device.h
+++ b/displayengine/libs/core/fb/hw_device.h
@@ -29,7 +29,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <linux/msm_mdp_ext.h>
-#include <video/msm_hdmi_modes.h>
 #include <linux/mdss_rotator.h>
 #include <poll.h>
 #include <pthread.h>
diff --git a/displayengine/libs/core/fb/hw_hdmi.cpp b/displayengine/libs/core/fb/hw_hdmi.cpp
index 0a908bf..fec8661 100644
--- a/displayengine/libs/core/fb/hw_hdmi.cpp
+++ b/displayengine/libs/core/fb/hw_hdmi.cpp
@@ -105,7 +105,7 @@
 }
 
 HWHDMI::HWHDMI(BufferSyncHandler *buffer_sync_handler,  HWInfoInterface *hw_info_intf)
-  : HWDevice(buffer_sync_handler) {
+  : HWDevice(buffer_sync_handler), hw_scan_info_() {
   HWDevice::device_type_ = kDeviceHDMI;
   HWDevice::device_name_ = "HDMI Display Device";
   HWDevice::hw_info_intf_ = hw_info_intf;
@@ -129,6 +129,7 @@
   MSM_HDMI_MODES_INIT_TIMINGS(supported_video_modes_);
   MSM_HDMI_MODES_SET_SUPP_TIMINGS(supported_video_modes_, MSM_HDMI_MODES_ALL);
 
+  ReadScanInfo();
   return kErrorNone;
 
 CleanupOnError:
@@ -340,5 +341,80 @@
   return HWDevice::GetHWPanelInfo(panel_info);
 }
 
+DisplayError HWHDMI::GetHWScanInfo(HWScanInfo *scan_info) {
+  if (!scan_info) {
+    return kErrorParameters;
+  }
+  *scan_info = hw_scan_info_;
+  return kErrorNone;
+}
+
+DisplayError HWHDMI::GetVideoFormat(uint32_t config_index, uint32_t *video_format) {
+  *video_format = hdmi_modes_[config_index];
+
+  return kErrorNone;
+}
+
+DisplayError HWHDMI::GetMaxCEAFormat(uint32_t *max_cea_format) {
+  *max_cea_format = HDMI_VFRMT_END;
+
+  return kErrorNone;
+}
+
+HWScanSupport HWHDMI::MapHWScanSupport(uint32_t value) {
+  switch (value) {
+  // TODO(user): Read the scan type from driver defined values instead of hardcoding
+  case 0:
+    return kScanNotSupported;
+  case 1:
+    return kScanAlwaysOverscanned;
+  case 2:
+    return kScanAlwaysUnderscanned;
+  case 3:
+    return kScanBoth;
+  default:
+    return kScanNotSupported;
+    break;
+  }
+}
+
+void HWHDMI::ReadScanInfo() {
+  int scan_info_file = -1;
+  ssize_t len = -1;
+  char data[PAGE_SIZE] = {'\0'};
+
+  snprintf(data, sizeof(data), "%s%d/scan_info", fb_path_, fb_node_index_);
+  scan_info_file = open_(data, O_RDONLY);
+  if (scan_info_file < 0) {
+    DLOGW("File '%s' not found.", data);
+    return;
+  }
+
+  memset(&data[0], 0, sizeof(data));
+  len = read(scan_info_file, data, sizeof(data) - 1);
+  if (len <= 0) {
+    close_(scan_info_file);
+    DLOGW("File %s%d/scan_info is empty.", fb_path_, fb_node_index_);
+    return;
+  }
+  data[len] = '\0';
+  close_(scan_info_file);
+
+  const uint32_t scan_info_max_count = 3;
+  uint32_t scan_info_count = 0;
+  char *tokens[scan_info_max_count] = { NULL };
+  ParseLine(data, tokens, scan_info_max_count, &scan_info_count);
+  if (scan_info_count != scan_info_max_count) {
+    DLOGW("Failed to parse scan info string %s", data);
+    return;
+  }
+
+  hw_scan_info_.pt_scan_support = MapHWScanSupport(atoi(tokens[0]));
+  hw_scan_info_.it_scan_support = MapHWScanSupport(atoi(tokens[1]));
+  hw_scan_info_.cea_scan_support = MapHWScanSupport(atoi(tokens[2]));
+  DLOGI("PT %d IT %d CEA %d", hw_scan_info_.pt_scan_support, hw_scan_info_.it_scan_support,
+        hw_scan_info_.cea_scan_support);
+}
+
 }  // namespace sde
 
diff --git a/displayengine/libs/core/fb/hw_hdmi.h b/displayengine/libs/core/fb/hw_hdmi.h
index 322e664..adfa1e2 100644
--- a/displayengine/libs/core/fb/hw_hdmi.h
+++ b/displayengine/libs/core/fb/hw_hdmi.h
@@ -25,6 +25,7 @@
 #ifndef __HW_HDMI_H__
 #define __HW_HDMI_H__
 
+#include <video/msm_hdmi_modes.h>
 #include "hw_device.h"
 #include "hw_hdmi_interface.h"
 
@@ -41,6 +42,9 @@
   virtual DisplayError GetDisplayAttributes(HWDisplayAttributes *display_attributes,
                                             uint32_t index);
   virtual DisplayError GetHWPanelInfo(HWPanelInfo *panel_info);
+  virtual DisplayError GetHWScanInfo(HWScanInfo *scan_info);
+  virtual DisplayError GetVideoFormat(uint32_t config_index, uint32_t *video_format);
+  virtual DisplayError GetMaxCEAFormat(uint32_t *max_cea_format);
   virtual DisplayError SetDisplayAttributes(uint32_t index);
   virtual DisplayError GetConfigIndex(uint32_t mode, uint32_t *index);
   virtual DisplayError PowerOn();
@@ -54,11 +58,14 @@
 
  private:
   int GetHDMIModeCount();
+  void ReadScanInfo();
+  HWScanSupport MapHWScanSupport(uint32_t value);
 
   uint32_t hdmi_mode_count_;
   uint32_t hdmi_modes_[256];
   // Holds the hdmi timing information. Ex: resolution, fps etc.,
   msm_hdmi_mode_timing_info *supported_video_modes_;
+  HWScanInfo hw_scan_info_;
 };
 
 }  // namespace sde
diff --git a/displayengine/libs/core/hw_hdmi_interface.h b/displayengine/libs/core/hw_hdmi_interface.h
index 36ca991..276fefd 100644
--- a/displayengine/libs/core/hw_hdmi_interface.h
+++ b/displayengine/libs/core/hw_hdmi_interface.h
@@ -36,6 +36,9 @@
   static DisplayError Create(HWHDMIInterface **intf, HWInfoInterface *hw_info_intf,
                              BufferSyncHandler *buffer_sync_handler);
   static DisplayError Destroy(HWHDMIInterface *intf);
+  virtual DisplayError GetHWScanInfo(HWScanInfo *scan_info) = 0;
+  virtual DisplayError GetVideoFormat(uint32_t config_index, uint32_t *video_format) = 0;
+  virtual DisplayError GetMaxCEAFormat(uint32_t *max_cea_format) = 0;
 
  protected:
   virtual ~HWHDMIInterface() { }
diff --git a/displayengine/libs/hwc/hwc_display.cpp b/displayengine/libs/hwc/hwc_display.cpp
index 690bf30..7794731 100644
--- a/displayengine/libs/hwc/hwc_display.cpp
+++ b/displayengine/libs/hwc/hwc_display.cpp
@@ -390,6 +390,7 @@
 
     hwc_rect_t scaled_display_frame = hwc_layer.displayFrame;
     ScaleDisplayFrame(&scaled_display_frame);
+    ApplyScanAdjustment(&scaled_display_frame);
 
     SetRect(scaled_display_frame, &layer.dst_rect);
     SetRect(hwc_layer.sourceCropf, &layer.src_rect);
@@ -1000,5 +1001,8 @@
   return fps;
 }
 
+void HWCDisplay::ApplyScanAdjustment(hwc_rect_t *display_frame) {
+}
+
 }  // namespace sde
 
diff --git a/displayengine/libs/hwc/hwc_display.h b/displayengine/libs/hwc/hwc_display.h
index 15aefd4..8a68619 100644
--- a/displayengine/libs/hwc/hwc_display.h
+++ b/displayengine/libs/hwc/hwc_display.h
@@ -115,6 +115,7 @@
   void MarkLayersForGPUBypass(hwc_display_contents_1_t *content_list);
   void CloseAcquireFences(hwc_display_contents_1_t *content_list);
   uint32_t RoundToStandardFPS(uint32_t fps);
+  virtual void ApplyScanAdjustment(hwc_rect_t *display_frame);
 
   enum {
     INPUT_LAYER_DUMP,
diff --git a/displayengine/libs/hwc/hwc_display_external.cpp b/displayengine/libs/hwc/hwc_display_external.cpp
index 2758e3a..9e57114 100644
--- a/displayengine/libs/hwc/hwc_display_external.cpp
+++ b/displayengine/libs/hwc/hwc_display_external.cpp
@@ -27,6 +27,7 @@
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
+#include <cutils/properties.h>
 #include <utils/constants.h>
 
 #include "hwc_display_external.h"
@@ -40,6 +41,17 @@
   : HWCDisplay(core_intf, hwc_procs, kHDMI, HWC_DISPLAY_EXTERNAL) {
 }
 
+int HWCDisplayExternal::Init() {
+  int status = 0;
+
+  status = HWCDisplay::Init();
+  if (status != 0) {
+    return status;
+  }
+
+  return status;
+}
+
 int HWCDisplayExternal::Prepare(hwc_display_contents_1_t *content_list) {
   int status = 0;
 
@@ -91,5 +103,39 @@
   return 0;
 }
 
+void HWCDisplayExternal::ApplyScanAdjustment(hwc_rect_t *display_frame) {
+  if (display_intf_->IsUnderscanSupported()) {
+    return;
+  }
+
+  // Read user defined width and height ratio
+  char property[PROPERTY_VALUE_MAX];
+  property_get("persist.sys.actionsafe.width", property, "0");
+  float width_ratio = FLOAT(atoi(property)) / 100.0f;
+  property_get("persist.sys.actionsafe.height", property, "0");
+  float height_ratio = FLOAT(atoi(property)) / 100.0f;
+
+  if (width_ratio == 0.0f ||  height_ratio == 0.0f) {
+    return;
+  }
+
+  uint32_t panel_width = 0;
+  uint32_t panel_height = 0;
+  GetPanelResolution(&panel_width, &panel_height);
+
+  if (panel_width == 0 || panel_height == 0) {
+    DLOGV("Invalid panel dimensions (%d, %d)", panel_width, panel_height);
+    return;
+  }
+
+  int x_offset = INT((FLOAT(panel_width) * width_ratio) / 2.0f);
+  int y_offset = INT((FLOAT(panel_height) * height_ratio) / 2.0f);
+
+  display_frame->left = display_frame->left + x_offset;
+  display_frame->top = display_frame->top + y_offset;
+  display_frame->right = display_frame->right - x_offset;
+  display_frame->bottom = display_frame->bottom - y_offset;
+}
+
 }  // namespace sde
 
diff --git a/displayengine/libs/hwc/hwc_display_external.h b/displayengine/libs/hwc/hwc_display_external.h
index e268621..6795761 100644
--- a/displayengine/libs/hwc/hwc_display_external.h
+++ b/displayengine/libs/hwc/hwc_display_external.h
@@ -32,9 +32,12 @@
 class HWCDisplayExternal : public HWCDisplay {
  public:
   explicit HWCDisplayExternal(CoreInterface *core_intf, hwc_procs_t const **hwc_procs);
+  virtual int Init();
   virtual int Prepare(hwc_display_contents_1_t *content_list);
   virtual int Commit(hwc_display_contents_1_t *content_list);
   virtual int GetDisplayConfigs(uint32_t *configs, size_t *num_configs);
+ private:
+  virtual void ApplyScanAdjustment(hwc_rect_t *display_frame);
 };
 
 }  // namespace sde