sdm: Enable HDMI dynamic frame rate switch feature

HDMI dynamic frame rate switch feature fetch video layer frame rate,
map to dedicate display frame rate, then invoke display
kernel driver to switch frame rate.

Change-Id: If91fcb6a056294f7ac13256300077b1013c0215e
CRs-fixed: 1001500
diff --git a/sdm/libs/core/display_base.cpp b/sdm/libs/core/display_base.cpp
index 33ee08d..b49e15e 100644
--- a/sdm/libs/core/display_base.cpp
+++ b/sdm/libs/core/display_base.cpp
@@ -52,10 +52,9 @@
   hw_panel_info_ = HWPanelInfo();
   hw_intf_->GetHWPanelInfo(&hw_panel_info_);
 
-  HWDisplayAttributes display_attrib;
   uint32_t active_index = 0;
   hw_intf_->GetActiveConfig(&active_index);
-  hw_intf_->GetDisplayAttributes(active_index, &display_attrib);
+  hw_intf_->GetDisplayAttributes(active_index, &display_attributes_);
 
   HWScaleLutInfo lut_info = {};
   error = comp_manager_->GetScaleLutConfig(&lut_info);
@@ -67,7 +66,7 @@
     goto CleanupOnError;
   }
 
-  error = comp_manager_->RegisterDisplay(display_type_, display_attrib,
+  error = comp_manager_->RegisterDisplay(display_type_, display_attributes_,
                                          hw_panel_info_, &display_comp_ctx_);
   if (error != kErrorNone) {
     goto CleanupOnError;
@@ -92,7 +91,7 @@
   }
 
   color_mgr_ = ColorManagerProxy::CreateColorManagerProxy(display_type_, hw_intf_,
-                               display_attrib, hw_panel_info_);
+                               display_attributes_, hw_panel_info_);
   if (!color_mgr_) {
     DLOGW("Unable to create ColorManagerProxy for display = %d", display_type_);
   }
diff --git a/sdm/libs/core/display_base.h b/sdm/libs/core/display_base.h
index 7242156..a59d6b2 100644
--- a/sdm/libs/core/display_base.h
+++ b/sdm/libs/core/display_base.h
@@ -89,6 +89,7 @@
   HWDeviceType hw_device_type_;
   HWInterface *hw_intf_ = NULL;
   HWPanelInfo hw_panel_info_;
+  HWDisplayAttributes display_attributes_;
   BufferSyncHandler *buffer_sync_handler_ = NULL;
   CompManager *comp_manager_ = NULL;
   RotatorInterface *rotator_intf_ = NULL;
diff --git a/sdm/libs/core/display_hdmi.cpp b/sdm/libs/core/display_hdmi.cpp
index 6522377..9a9628a 100644
--- a/sdm/libs/core/display_hdmi.cpp
+++ b/sdm/libs/core/display_hdmi.cpp
@@ -190,12 +190,31 @@
 DisplayError DisplayHDMI::GetRefreshRateRange(uint32_t *min_refresh_rate,
                                               uint32_t *max_refresh_rate) {
   SCOPE_LOCK(locker_);
-  return DisplayBase::GetRefreshRateRange(min_refresh_rate, max_refresh_rate);
+  DisplayError error = kErrorNone;
+
+  if (hw_panel_info_.min_fps && hw_panel_info_.max_fps) {
+    *min_refresh_rate = hw_panel_info_.min_fps;
+    *max_refresh_rate = hw_panel_info_.max_fps;
+  } else {
+    error = DisplayBase::GetRefreshRateRange(min_refresh_rate, max_refresh_rate);
+  }
+
+  return error;
 }
 
 DisplayError DisplayHDMI::SetRefreshRate(uint32_t refresh_rate) {
   SCOPE_LOCK(locker_);
-  return kErrorNotSupported;
+
+  if (!active_) {
+    return kErrorPermission;
+  }
+
+  DisplayError error = hw_intf_->SetRefreshRate(refresh_rate);
+  if (error != kErrorNone) {
+    return error;
+  }
+
+  return kErrorNone;
 }
 
 bool DisplayHDMI::IsUnderscanSupported() {
@@ -358,9 +377,10 @@
   hw_intf_->GetActiveConfig(&active_index);
   hw_intf_->GetDisplayAttributes(active_index, &display_attributes);
 
-  if (panel_info != hw_panel_info_) {
+  if (panel_info != hw_panel_info_ || display_attributes != display_attributes_) {
     comp_manager_->ReconfigureDisplay(display_comp_ctx_, display_attributes, panel_info);
     hw_panel_info_ = panel_info;
+    display_attributes_ = display_attributes;
   }
 }
 
diff --git a/sdm/libs/core/fb/hw_hdmi.cpp b/sdm/libs/core/fb/hw_hdmi.cpp
index 73a6a3c..6d4bf9b 100644
--- a/sdm/libs/core/fb/hw_hdmi.cpp
+++ b/sdm/libs/core/fb/hw_hdmi.cpp
@@ -317,6 +317,8 @@
 
   active_config_index_ = index;
 
+  frame_rate_ = timing_mode->refresh_rate;
+
   // Get the supported s3d modes for current active config index
   HWDisplayAttributes attrib;
   GetDisplayS3DSupport(index, &attrib);
@@ -696,5 +698,61 @@
   return kErrorNone;
 }
 
+DisplayError HWHDMI::SetRefreshRate(uint32_t refresh_rate) {
+  char mode_path[kMaxStringLength] = {0};
+  char node_path[kMaxStringLength] = {0};
+  uint32_t mode = kModeHFP;
+
+  if (refresh_rate == frame_rate_) {
+    return kErrorNone;
+  }
+
+  snprintf(mode_path, sizeof(mode_path), "%s%d/msm_fb_dfps_mode", fb_path_, fb_node_index_);
+  snprintf(node_path, sizeof(node_path), "%s%d/dynamic_fps", fb_path_, fb_node_index_);
+
+  int fd_mode = Sys::open_(mode_path, O_WRONLY);
+  if (fd_mode < 0) {
+    DLOGE("Failed to open %s with error %s", node_path, strerror(errno));
+    return kErrorFileDescriptor;
+  }
+
+  char dfps_mode[kMaxStringLength];
+  snprintf(dfps_mode, sizeof(dfps_mode), "%d", mode);
+  DLOGI_IF(kTagDriverConfig, "Setting dfps_mode  = %d", mode);
+  ssize_t len = Sys::pwrite_(fd_mode, dfps_mode, strlen(dfps_mode), 0);
+  if (len < 0) {
+    DLOGE("Failed to enable dfps mode %d with error %s", mode, strerror(errno));
+    Sys::close_(fd_mode);
+    return kErrorUndefined;
+  }
+  Sys::close_(fd_mode);
+
+  int fd_node = Sys::open_(node_path, O_WRONLY);
+  if (fd_node < 0) {
+    DLOGE("Failed to open %s with error %s", node_path, strerror(errno));
+    return kErrorFileDescriptor;
+  }
+
+  char refresh_rate_string[kMaxStringLength];
+  snprintf(refresh_rate_string, sizeof(refresh_rate_string), "%d", refresh_rate);
+  DLOGI_IF(kTagDriverConfig, "Setting refresh rate = %d", refresh_rate);
+  len = Sys::pwrite_(fd_node, refresh_rate_string, strlen(refresh_rate_string), 0);
+  if (len < 0) {
+    DLOGE("Failed to write %d with error %s", refresh_rate, strerror(errno));
+    Sys::close_(fd_node);
+    return kErrorUndefined;
+  }
+  Sys::close_(fd_node);
+
+  DisplayError error = ReadTimingInfo();
+  if (error != kErrorNone) {
+    return error;
+  }
+
+  frame_rate_ = refresh_rate;
+
+  return kErrorNone;
+}
+
 }  // namespace sdm
 
diff --git a/sdm/libs/core/fb/hw_hdmi.h b/sdm/libs/core/fb/hw_hdmi.h
index 50bbbaa..aa5e2a0 100644
--- a/sdm/libs/core/fb/hw_hdmi.h
+++ b/sdm/libs/core/fb/hw_hdmi.h
@@ -40,6 +40,18 @@
   static DisplayError Destroy(HWInterface *intf);
 
  protected:
+  enum HWFramerateUpdate {
+    // Switch framerate by switch to other standard modes though panel blank/unblank
+    kModeSuspendResume,
+    // Switch framerate by tuning pixel clock
+    kModeClock,
+    // Switch framerate by tuning vertical front porch
+    kModeVFP,
+    // Switch framerate by tuning horizontal front porch
+    kModeHFP,
+    kModeMAX
+  };
+
   HWHDMI(BufferSyncHandler *buffer_sync_handler, HWInfoInterface *hw_info_intf);
   virtual DisplayError Init();
   virtual DisplayError Deinit();
@@ -56,6 +68,7 @@
   virtual DisplayError GetConfigIndex(uint32_t mode, uint32_t *index);
   virtual DisplayError Validate(HWLayers *hw_layers);
   virtual DisplayError SetS3DMode(HWS3DMode s3d_mode);
+  virtual DisplayError SetRefreshRate(uint32_t refresh_rate);
 
  private:
   DisplayError ReadEDIDInfo();
@@ -80,6 +93,7 @@
   std::map<HWS3DMode, msm_hdmi_s3d_mode> s3d_mode_sdm_to_mdp_;
   std::vector<HWS3DMode> supported_s3d_modes_;
   int active_mdp_s3d_mode_ = HDMI_S3D_NONE;
+  uint32_t frame_rate_ = 0;
 };
 
 }  // namespace sdm
diff --git a/sdm/libs/hwc/hwc_display.cpp b/sdm/libs/hwc/hwc_display.cpp
index ab9b4a7..29e0c1a 100644
--- a/sdm/libs/hwc/hwc_display.cpp
+++ b/sdm/libs/hwc/hwc_display.cpp
@@ -537,11 +537,7 @@
       layer_stack_.flags.cursor_present = true;
     }
 
-    if (layer->frame_rate > metadata_refresh_rate_) {
-      metadata_refresh_rate_ = SanitizeRefreshRate(layer->frame_rate);
-    } else {
-      layer->frame_rate = current_refresh_rate_;
-    }
+    PrepareDynamicRefreshRate(layer);
 
     layer->input_buffer->buffer_id = reinterpret_cast<uint64_t>(hwc_layer.handle);
   }
@@ -1314,21 +1310,6 @@
   }
 }
 
-uint32_t HWCDisplay::RoundToStandardFPS(uint32_t fps) {
-  static const uint32_t standard_fps[4] = {30, 24, 48, 60};
-
-  int count = INT(sizeof(standard_fps) / sizeof(standard_fps[0]));
-  for (int i = 0; i < count; i++) {
-    if ((standard_fps[i] - fps) < 2) {
-      // Most likely used for video, the fps can fluctuate
-      // Ex: b/w 29 and 30 for 30 fps clip
-      return standard_fps[i];
-    }
-  }
-
-  return fps;
-}
-
 void HWCDisplay::ApplyScanAdjustment(hwc_rect_t *display_frame) {
 }
 
@@ -1510,6 +1491,35 @@
   return (updating_count == 1);
 }
 
+bool HWCDisplay::SingleVideoLayerUpdating(uint32_t app_layer_count) {
+  uint32_t updating_count = 0;
+
+  for (uint i = 0; i < app_layer_count; i++) {
+    Layer *layer = layer_stack_.layers[i];
+    if (layer->flags.updating && (layer->input_buffer->flags.video == true)) {
+      updating_count++;
+    }
+  }
+
+  return (updating_count == 1);
+}
+
+uint32_t HWCDisplay::RoundToStandardFPS(float fps) {
+  static const uint32_t standard_fps[4] = {30, 24, 48, 60};
+  uint32_t frame_rate = (uint32_t)(fps);
+
+  int count = INT(sizeof(standard_fps) / sizeof(standard_fps[0]));
+  for (int i = 0; i < count; i++) {
+    if ((standard_fps[i] - frame_rate) < 2) {
+      // Most likely used for video, the fps can fluctuate
+      // Ex: b/w 29 and 30 for 30 fps clip
+      return standard_fps[i];
+    }
+  }
+
+  return frame_rate;
+}
+
 uint32_t HWCDisplay::SanitizeRefreshRate(uint32_t req_refresh_rate) {
   uint32_t refresh_rate = req_refresh_rate;
 
@@ -1530,4 +1540,12 @@
   return display_class_;
 }
 
+void HWCDisplay::PrepareDynamicRefreshRate(Layer *layer) {
+  if (layer->frame_rate > metadata_refresh_rate_) {
+    metadata_refresh_rate_ = SanitizeRefreshRate(layer->frame_rate);
+  } else {
+    layer->frame_rate = current_refresh_rate_;
+  }
+}
+
 }  // namespace sdm
diff --git a/sdm/libs/hwc/hwc_display.h b/sdm/libs/hwc/hwc_display.h
index 7b14f10..17db165 100644
--- a/sdm/libs/hwc/hwc_display.h
+++ b/sdm/libs/hwc/hwc_display.h
@@ -149,6 +149,9 @@
   virtual int CommitLayerStack(hwc_display_contents_1_t *content_list);
   virtual int PostCommitLayerStack(hwc_display_contents_1_t *content_list);
   virtual void DumpOutputBuffer(const BufferInfo& buffer_info, void *base, int fence);
+  virtual uint32_t RoundToStandardFPS(float fps);
+  virtual uint32_t SanitizeRefreshRate(uint32_t req_refresh_rate);
+  virtual void PrepareDynamicRefreshRate(Layer *layer);
   inline void SetRect(const hwc_rect_t &source, LayerRect *target);
   inline void SetRect(const hwc_frect_t &source, LayerRect *target);
   inline void SetComposition(const int32_t &source, LayerComposition *target);
@@ -160,7 +163,6 @@
   const char *GetDisplayString();
   void ScaleDisplayFrame(hwc_rect_t *display_frame);
   void MarkLayersForGPUBypass(hwc_display_contents_1_t *content_list);
-  uint32_t RoundToStandardFPS(uint32_t fps);
   virtual void ApplyScanAdjustment(hwc_rect_t *display_frame);
   DisplayError SetCSC(ColorSpace_t source, LayerCSC *target);
   DisplayError SetIGC(IGC_t source, LayerIGC *target);
@@ -169,7 +171,7 @@
   void CacheLayerStackInfo(hwc_display_contents_1_t *content_list);
   bool IsLayerUpdating(hwc_display_contents_1_t *content_list, int layer_index);
   bool SingleLayerUpdating(uint32_t app_layer_count);
-  uint32_t SanitizeRefreshRate(uint32_t req_refresh_rate);
+  bool SingleVideoLayerUpdating(uint32_t app_layer_count);
 
   enum {
     INPUT_LAYER_DUMP,
diff --git a/sdm/libs/hwc/hwc_display_external.cpp b/sdm/libs/hwc/hwc_display_external.cpp
index 813b123..a647517 100644
--- a/sdm/libs/hwc/hwc_display_external.cpp
+++ b/sdm/libs/hwc/hwc_display_external.cpp
@@ -49,6 +49,7 @@
                                HWCDisplay **hwc_display) {
   uint32_t external_width = 0;
   uint32_t external_height = 0;
+  int drc_enabled = 0;
 
   HWCDisplay *hwc_display_external = new HWCDisplayExternal(core_intf, hwc_procs, qservice);
   int status = hwc_display_external->Init();
@@ -80,6 +81,9 @@
     return status;
   }
 
+  HWCDebugHandler::Get()->GetProperty("sdm.hdmi.drc_enabled", &(drc_enabled));
+  reinterpret_cast<HWCDisplayExternal *>(hwc_display_external)->drc_enabled_ = drc_enabled;
+
   *hwc_display = hwc_display_external;
 
   return status;
@@ -98,6 +102,7 @@
 
 int HWCDisplayExternal::Prepare(hwc_display_contents_1_t *content_list) {
   int status = 0;
+  DisplayError error = kErrorNone;
 
   if (secure_display_active_) {
     MarkLayersForGPUBypass(content_list);
@@ -119,6 +124,18 @@
     return 0;
   }
 
+  bool one_video_updating_layer = SingleVideoLayerUpdating(UINT32(content_list->numHwLayers - 1));
+
+  if (current_refresh_rate_ != metadata_refresh_rate_ && one_video_updating_layer && drc_enabled_) {
+    error = display_intf_->SetRefreshRate(metadata_refresh_rate_);
+  }
+
+  if (error == kErrorNone) {
+    // On success, set current refresh rate to new refresh rate
+    current_refresh_rate_ = metadata_refresh_rate_;
+  }
+
+
   status = PrepareLayerStack(content_list);
   if (status) {
     return status;
@@ -221,5 +238,36 @@
   }
 }
 
+uint32_t HWCDisplayExternal::RoundToStandardFPS(float fps) {
+  static const uint32_t standard_fps[] = {23976, 24000, 25000, 29970, 30000, 50000, 59940, 60000};
+  static const uint32_t mapping_fps[] = {59940, 60000, 60000, 59940, 60000, 50000, 59940, 60000};
+  uint32_t frame_rate = (uint32_t)(fps * 1000);
+
+  int count = INT(sizeof(standard_fps) / sizeof(standard_fps[0]));
+  for (int i = 0; i < count; i++) {
+    // Most likely used for video, the fps for frames should be stable from video side.
+    if (standard_fps[i] > frame_rate) {
+      if (i > 0) {
+        if ((standard_fps[i] - frame_rate) > (frame_rate - standard_fps[i-1])) {
+          return mapping_fps[i-1];
+        } else {
+          return mapping_fps[i];
+        }
+      } else {
+        return mapping_fps[i];
+      }
+    }
+  }
+
+  return standard_fps[count - 1];
+}
+
+void HWCDisplayExternal::PrepareDynamicRefreshRate(Layer *layer) {
+  if (layer->input_buffer->flags.video) {
+    metadata_refresh_rate_ = SanitizeRefreshRate(layer->frame_rate);
+    layer->frame_rate = current_refresh_rate_;
+  }
+}
+
 }  // namespace sdm
 
diff --git a/sdm/libs/hwc/hwc_display_external.h b/sdm/libs/hwc/hwc_display_external.h
index 436190d..c54f738 100644
--- a/sdm/libs/hwc/hwc_display_external.h
+++ b/sdm/libs/hwc/hwc_display_external.h
@@ -41,6 +41,11 @@
   virtual int Commit(hwc_display_contents_1_t *content_list);
   virtual void SetSecureDisplay(bool secure_display_active);
 
+ protected:
+  virtual uint32_t RoundToStandardFPS(float fps);
+  virtual void PrepareDynamicRefreshRate(Layer *layer);
+  int drc_enabled_ = 0;
+
  private:
   HWCDisplayExternal(CoreInterface *core_intf, hwc_procs_t const **hwc_procs,
                      qService::QService *qservice);