hwc2: Add VRR related Composer 2.4 APIs

Define SetActiveConfigWithConstraints and GetDisplayVsyncPeriod
APIs for VRR on Composer 2.4 .

Change-Id: Id64e71a9756e183b1d47a285fb6be0cae9ca046e
diff --git a/sdm/libs/hwc2/hwc_callbacks.cpp b/sdm/libs/hwc2/hwc_callbacks.cpp
index d1345ee..8085dcb 100644
--- a/sdm/libs/hwc2/hwc_callbacks.cpp
+++ b/sdm/libs/hwc2/hwc_callbacks.cpp
@@ -60,6 +60,27 @@
   return HWC2::Error::None;
 }
 
+HWC2::Error HWCCallbacks::Vsync_2_4(hwc2_display_t display, int64_t timestamp, uint32_t period) {
+  DTRACE_SCOPED();
+  if (!vsync_2_4_) {
+    return HWC2::Error::NoResources;
+  }
+
+  vsync_2_4_(vsync_2_4_data_, display, timestamp, period);
+  return HWC2::Error::None;
+}
+
+HWC2::Error HWCCallbacks::VsyncPeriodTimingChanged(
+    hwc2_display_t display, hwc_vsync_period_change_timeline_t *updated_timeline) {
+  DTRACE_SCOPED();
+  if (!vsync_period_timing_changed_) {
+    return HWC2::Error::NoResources;
+  }
+
+  vsync_period_timing_changed_(vsync_period_timing_changed_data_, display, updated_timeline);
+  return HWC2::Error::None;
+}
+
 HWC2::Error HWCCallbacks::Register(HWC2::Callback descriptor, hwc2_callback_data_t callback_data,
                                    hwc2_function_pointer_t pointer) {
   switch (descriptor) {
@@ -75,6 +96,15 @@
       vsync_data_ = callback_data;
       vsync_ = reinterpret_cast<HWC2_PFN_VSYNC>(pointer);
       break;
+    case HWC2::Callback::Vsync_2_4:
+      vsync_2_4_data_ = callback_data;
+      vsync_2_4_ = reinterpret_cast<HWC2_PFN_VSYNC_2_4>(pointer);
+      break;
+    case HWC2::Callback::VsyncPeriodTimingChanged:
+      vsync_period_timing_changed_data_ = callback_data;
+      vsync_period_timing_changed_ =
+          reinterpret_cast<HWC2_PFN_VSYNC_PERIOD_TIMING_CHANGED>(pointer);
+      break;
     default:
       return HWC2::Error::BadParameter;
   }
diff --git a/sdm/libs/hwc2/hwc_callbacks.h b/sdm/libs/hwc2/hwc_callbacks.h
index 2e4bdba..baec40d 100644
--- a/sdm/libs/hwc2/hwc_callbacks.h
+++ b/sdm/libs/hwc2/hwc_callbacks.h
@@ -53,6 +53,9 @@
   HWC2::Error Hotplug(hwc2_display_t display, HWC2::Connection state);
   HWC2::Error Refresh(hwc2_display_t display);
   HWC2::Error Vsync(hwc2_display_t display, int64_t timestamp);
+  HWC2::Error Vsync_2_4(hwc2_display_t display, int64_t timestamp, uint32_t period);
+  HWC2::Error VsyncPeriodTimingChanged(hwc2_display_t display,
+                                       hwc_vsync_period_change_timeline_t *updated_timeline);
   HWC2::Error Register(HWC2::Callback, hwc2_callback_data_t callback_data,
                        hwc2_function_pointer_t pointer);
   void UpdateVsyncSource(hwc2_display_t from) {
@@ -61,6 +64,7 @@
   hwc2_display_t GetVsyncSource() { return vsync_source_; }
 
   bool VsyncCallbackRegistered() { return (vsync_ != nullptr && vsync_data_ != nullptr); }
+  bool Vsync_2_4CallbackRegistered() { return (vsync_2_4_ != nullptr); }
   bool NeedsRefresh(hwc2_display_t display) { return pending_refresh_.test(UINT32(display)); }
   void ResetRefresh(hwc2_display_t display) { pending_refresh_.reset(UINT32(display)); }
 
@@ -68,10 +72,14 @@
   hwc2_callback_data_t hotplug_data_ = nullptr;
   hwc2_callback_data_t refresh_data_ = nullptr;
   hwc2_callback_data_t vsync_data_ = nullptr;
+  hwc2_callback_data_t vsync_2_4_data_ = nullptr;
+  hwc2_callback_data_t vsync_period_timing_changed_data_ = nullptr;
 
   HWC2_PFN_HOTPLUG hotplug_ = nullptr;
   HWC2_PFN_REFRESH refresh_ = nullptr;
   HWC2_PFN_VSYNC vsync_ = nullptr;
+  HWC2_PFN_VSYNC_2_4 vsync_2_4_ = nullptr;
+  HWC2_PFN_VSYNC_PERIOD_TIMING_CHANGED vsync_period_timing_changed_ = nullptr;
   hwc2_display_t vsync_source_ = HWC_DISPLAY_PRIMARY;   // hw vsync is active on this display
   std::bitset<kNumDisplays> pending_refresh_;         // Displays waiting to get refreshed
 };
diff --git a/sdm/libs/hwc2/hwc_display.cpp b/sdm/libs/hwc2/hwc_display.cpp
index 238b1d0..467cc1b 100644
--- a/sdm/libs/hwc2/hwc_display.cpp
+++ b/sdm/libs/hwc2/hwc_display.cpp
@@ -52,6 +52,7 @@
 namespace sdm {
 
 uint32_t HWCDisplay::throttling_refresh_rate_ = 60;
+constexpr uint32_t kVsyncTimeDriftNs = 1000000;
 
 bool NeedsToneMap(const LayerStack &layer_stack) {
   for (Layer *layer : layer_stack.layers) {
@@ -62,6 +63,10 @@
   return false;
 }
 
+bool IsTimeAfterOrEqualVsyncTime(int64_t time, int64_t vsync_time) {
+  return ((vsync_time != INT64_MAX) && ((time - (vsync_time - kVsyncTimeDriftNs)) >= 0));
+}
+
 HWCColorMode::HWCColorMode(DisplayInterface *display_intf) : display_intf_(display_intf) {}
 
 HWC2::Error HWCColorMode::Init() {
@@ -856,7 +861,8 @@
   ATRACE_INT("SetVsyncState ", enabled == HWC2::Vsync::Enable ? 1 : 0);
   DisplayError error = kErrorNone;
 
-  if (shutdown_pending_ || !callbacks_->VsyncCallbackRegistered()) {
+  if (shutdown_pending_ ||
+      (!callbacks_->VsyncCallbackRegistered() && !callbacks_->Vsync_2_4CallbackRegistered())) {
     return HWC2::Error::None;
   }
 
@@ -1222,7 +1228,17 @@
 }
 
 DisplayError HWCDisplay::VSync(const DisplayEventVSync &vsync) {
-  callbacks_->Vsync(id_, vsync.timestamp);
+  if (callbacks_->Vsync_2_4CallbackRegistered()) {
+    hwc2_vsync_period_t vsync_period;
+    if (GetDisplayVsyncPeriod(&vsync_period) != HWC2::Error::None) {
+      vsync_period = 0;
+    }
+    ATRACE_INT("VsyncPeriod", INT32(vsync_period));
+    callbacks_->Vsync_2_4(id_, vsync.timestamp, vsync_period);
+  } else {
+    callbacks_->Vsync(id_, vsync.timestamp);
+  }
+
   return kErrorNone;
 }
 
@@ -2438,4 +2454,176 @@
   return -1;
 }
 
+HWC2::Error HWCDisplay::GetDisplayVsyncPeriod(hwc2_vsync_period_t *vsync_period) {
+  if (GetTransientVsyncPeriod(vsync_period)) {
+    return HWC2::Error::None;
+  }
+
+  return GetVsyncPeriodByActiveConfig(vsync_period);
+}
+
+HWC2::Error HWCDisplay::SetActiveConfigWithConstraints(hwc2_config_t config,
+    hwc_vsync_period_change_constraints_t *vsync_period_change_constraints,
+    hwc_vsync_period_change_timeline_t *out_timeline) {
+  if (variable_config_map_.find(config) == variable_config_map_.end()) {
+    DLOGE("Invalid config: %d", config);
+    return HWC2::Error::BadConfig;
+  }
+
+  if (vsync_period_change_constraints->seamlessRequired && !AllowSeamless(config)) {
+    DLOGE("Seamless switch to the config: %d, is not allowed!", config);
+    return HWC2::Error::SeamlessNotAllowed;
+  }
+
+  hwc2_vsync_period_t vsync_period;
+  if (GetDisplayVsyncPeriod(&vsync_period) != HWC2::Error::None) {
+    return HWC2::Error::BadConfig;
+  }
+
+  std::tie(out_timeline->refreshTimeNanos, out_timeline->newVsyncAppliedTimeNanos) =
+      RequestActiveConfigChange(config, vsync_period,
+                                vsync_period_change_constraints->desiredTimeNanos);
+
+  out_timeline->refreshRequired = true;
+  return HWC2::Error::None;
+}
+
+void HWCDisplay::ProcessActiveConfigChange() {
+  if (!IsActiveConfigReadyToSubmit(systemTime(SYSTEM_TIME_MONOTONIC))) {
+    return;
+  }
+
+  DTRACE_SCOPED();
+  hwc2_vsync_period_t vsync_period;
+  if (GetVsyncPeriodByActiveConfig(&vsync_period) == HWC2::Error::None) {
+    SubmitActiveConfigChange(vsync_period);
+  }
+}
+
+HWC2::Error HWCDisplay::GetVsyncPeriodByActiveConfig(hwc2_vsync_period_t *vsync_period) {
+  hwc2_config_t active_config;
+
+  auto error = GetActiveConfig(&active_config);
+  if (error != HWC2::Error::None) {
+    DLOGE("Failed to get active config!");
+    return error;
+  }
+
+  int32_t active_vsync_period;
+  error = GetDisplayAttribute(active_config, HWC2::Attribute::VsyncPeriod, &active_vsync_period);
+  if (error != HWC2::Error::None) {
+    DLOGE("Failed to get VsyncPeriod of config: %d", active_config);
+    return error;
+  }
+
+  *vsync_period = static_cast<hwc2_vsync_period_t>(active_vsync_period);
+  return HWC2::Error::None;
+}
+
+bool HWCDisplay::GetTransientVsyncPeriod(hwc2_vsync_period_t *vsync_period) {
+  std::lock_guard<std::mutex> lock(transient_refresh_rate_lock_);
+  auto now = systemTime(SYSTEM_TIME_MONOTONIC);
+
+  while (!transient_refresh_rate_info_.empty()) {
+    if (IsActiveConfigApplied(now, transient_refresh_rate_info_.front().vsync_applied_time)) {
+      transient_refresh_rate_info_.pop_front();
+    } else {
+      *vsync_period = transient_refresh_rate_info_.front().transient_vsync_period;
+      return true;
+    }
+  }
+
+  return false;
+}
+
+std::tuple<int64_t, int64_t> HWCDisplay::RequestActiveConfigChange(
+    hwc2_config_t config, hwc2_vsync_period_t current_vsync_period, int64_t desired_time) {
+  int64_t refresh_time, applied_time;
+  std::tie(refresh_time, applied_time) =
+      EstimateVsyncPeriodChangeTimeline(current_vsync_period, desired_time);
+
+  pending_refresh_rate_config_ = config;
+  pending_refresh_rate_refresh_time_ = refresh_time;
+  pending_refresh_rate_applied_time_ = applied_time;
+
+  return std::make_tuple(refresh_time, applied_time);
+}
+
+std::tuple<int64_t, int64_t> HWCDisplay::EstimateVsyncPeriodChangeTimeline(
+    hwc2_vsync_period_t current_vsync_period, int64_t desired_time) {
+  const auto now = systemTime(SYSTEM_TIME_MONOTONIC);
+  const auto delta = desired_time - now;
+  const auto refresh_rate_activate_period = current_vsync_period * vsyncs_to_apply_rate_change_;
+  nsecs_t refresh_time;
+
+  if (delta < 0) {
+    refresh_time = now + (delta % current_vsync_period);
+  } else if (delta < refresh_rate_activate_period) {
+    refresh_time = now + (delta % current_vsync_period) - current_vsync_period;
+  } else {
+    refresh_time = desired_time - refresh_rate_activate_period;
+  }
+
+  const auto applied_time = refresh_time + refresh_rate_activate_period;
+  return std::make_tuple(refresh_time, applied_time);
+}
+
+void HWCDisplay::SubmitActiveConfigChange(hwc2_vsync_period_t current_vsync_period) {
+  HWC2::Error error = SetActiveConfig(pending_refresh_rate_config_);
+  if (error != HWC2::Error::None) {
+    return;
+  }
+
+  std::lock_guard<std::mutex> lock(transient_refresh_rate_lock_);
+  hwc_vsync_period_change_timeline_t timeline;
+  std::tie(timeline.refreshTimeNanos, timeline.newVsyncAppliedTimeNanos) =
+      EstimateVsyncPeriodChangeTimeline(current_vsync_period, pending_refresh_rate_refresh_time_);
+
+  transient_refresh_rate_info_.push_back({current_vsync_period, timeline.newVsyncAppliedTimeNanos});
+  if (timeline.newVsyncAppliedTimeNanos != pending_refresh_rate_applied_time_) {
+    timeline.refreshRequired = false;
+    callbacks_->VsyncPeriodTimingChanged(id_, &timeline);
+  }
+
+  pending_refresh_rate_config_ = UINT_MAX;
+  pending_refresh_rate_refresh_time_ = INT64_MAX;
+  pending_refresh_rate_applied_time_ = INT64_MAX;
+}
+
+bool HWCDisplay::IsActiveConfigReadyToSubmit(int64_t time) {
+  return ((pending_refresh_rate_config_ != UINT_MAX) &&
+          IsTimeAfterOrEqualVsyncTime(time, pending_refresh_rate_refresh_time_));
+}
+
+bool HWCDisplay::IsActiveConfigApplied(int64_t time, int64_t vsync_applied_time) {
+  return IsTimeAfterOrEqualVsyncTime(time, vsync_applied_time);
+}
+
+bool HWCDisplay::IsSameGroup(hwc2_config_t config_id1, hwc2_config_t config_id2) {
+  const auto &variable_config1 = variable_config_map_.find(config_id1);
+  const auto &variable_config2 = variable_config_map_.find(config_id2);
+
+  if ((variable_config1 == variable_config_map_.end()) ||
+      (variable_config2 == variable_config_map_.end())) {
+    DLOGE("Invalid config: %u, %u", config_id1, config_id2);
+    return false;
+  }
+
+  const DisplayConfigGroupInfo &config_group1 = variable_config1->second;
+  const DisplayConfigGroupInfo &config_group2 = variable_config2->second;
+
+  return (config_group1 == config_group2);
+}
+
+bool HWCDisplay::AllowSeamless(hwc2_config_t config) {
+  hwc2_config_t active_config;
+  auto error = GetActiveConfig(&active_config);
+  if (error != HWC2::Error::None) {
+    DLOGE("Failed to get active config!");
+    return false;
+  }
+
+  return IsSameGroup(active_config, config);
+}
+
 }  // namespace sdm
diff --git a/sdm/libs/hwc2/hwc_display.h b/sdm/libs/hwc2/hwc_display.h
index ee060e9..96b3f65 100644
--- a/sdm/libs/hwc2/hwc_display.h
+++ b/sdm/libs/hwc2/hwc_display.h
@@ -65,6 +65,11 @@
   kSecureMax,
 };
 
+struct TransientRefreshRateInfo {
+  uint32_t transient_vsync_period;
+  int64_t vsync_applied_time;
+};
+
 class HWCColorMode {
  public:
   explicit HWCColorMode(DisplayInterface *display_intf);
@@ -244,6 +249,7 @@
     return HWC2::Error::Unsupported;
   }
   bool IsFirstCommitDone() { return !first_cycle_; }
+  virtual void ProcessActiveConfigChange();
 
   // HWC2 APIs
   virtual HWC2::Error AcceptDisplayChanges(void);
@@ -345,6 +351,10 @@
   virtual void SetLayerStack(HWCLayerStack *stack);
   virtual void PostPowerMode();
   virtual void NotifyClientStatus(bool connected) { client_connected_ = connected; }
+  virtual HWC2::Error GetDisplayVsyncPeriod(hwc2_vsync_period_t *vsync_period);
+  virtual HWC2::Error SetActiveConfigWithConstraints(hwc2_config_t config,
+      hwc_vsync_period_change_constraints_t *vsync_period_change_constraints,
+      hwc_vsync_period_change_timeline_t *out_timeline);
 
  protected:
   static uint32_t throttling_refresh_rate_;
@@ -377,6 +387,19 @@
   virtual void GetUnderScanConfig() { }
   int32_t SetClientTargetDataSpace(int32_t dataspace);
   int32_t GetDisplayConfigGroupId(const DisplayConfigGroupInfo &variable_config);
+  HWC2::Error GetVsyncPeriodByActiveConfig(hwc2_vsync_period_t *vsync_period);
+  bool GetTransientVsyncPeriod(hwc2_vsync_period_t *vsync_period);
+  std::tuple<int64_t, int64_t> RequestActiveConfigChange(hwc2_config_t config,
+                                                         hwc2_vsync_period_t current_vsync_period,
+                                                         int64_t desired_time);
+  std::tuple<int64_t, int64_t> EstimateVsyncPeriodChangeTimeline(
+      hwc2_vsync_period_t current_vsync_period, int64_t desired_time);
+  void SubmitActiveConfigChange(hwc2_vsync_period_t current_vsync_period);
+  bool IsActiveConfigReadyToSubmit(int64_t time);
+  bool IsActiveConfigApplied(int64_t time, int64_t vsync_applied_time);
+  bool IsSameGroup(hwc2_config_t config_id1, hwc2_config_t config_id2);
+  bool AllowSeamless(hwc2_config_t request_config);
+  void SetVsyncsApplyRateChange(uint32_t vsyncs) { vsyncs_to_apply_rate_change_ = vsyncs; }
 
   enum {
     INPUT_LAYER_DUMP,
@@ -437,6 +460,12 @@
   bool fast_path_composition_ = false;
   bool client_connected_ = true;
   bool pending_config_ = false;
+  uint32_t vsyncs_to_apply_rate_change_ = 1;
+  hwc2_config_t pending_refresh_rate_config_ = UINT_MAX;
+  int64_t pending_refresh_rate_refresh_time_ = INT64_MAX;
+  int64_t pending_refresh_rate_applied_time_ = INT64_MAX;
+  std::deque<TransientRefreshRateInfo> transient_refresh_rate_info_;
+  std::mutex transient_refresh_rate_lock_;
 
  private:
   void DumpInputBuffers(void);
diff --git a/sdm/libs/hwc2/hwc_display_builtin.cpp b/sdm/libs/hwc2/hwc_display_builtin.cpp
index c85b772..aa65ba7 100644
--- a/sdm/libs/hwc2/hwc_display_builtin.cpp
+++ b/sdm/libs/hwc2/hwc_display_builtin.cpp
@@ -172,6 +172,13 @@
   if (enable_optimize_refresh_) {
     DLOGI("Drop redundant drawcycles %d", id_);
   }
+
+  int vsyncs = 0;
+  HWCDebugHandler::Get()->GetProperty(DEFER_FPS_FRAME_COUNT, &vsyncs);
+  if (vsyncs > 0) {
+    SetVsyncsApplyRateChange(UINT32(vsyncs));
+  }
+
   pmic_intf_ = new PMICInterface();
   pmic_intf_->Init();
 
diff --git a/sdm/libs/hwc2/hwc_session.cpp b/sdm/libs/hwc2/hwc_session.cpp
index 9e2d8d6..108ee32 100644
--- a/sdm/libs/hwc2/hwc_session.cpp
+++ b/sdm/libs/hwc2/hwc_session.cpp
@@ -776,6 +776,7 @@
     if (power_on_pending_[display]) {
       status = HWC2::Error::None;
     } else {
+      hwc_session->hwc_display_[display]->ProcessActiveConfigChange();
       status = hwc_session->PresentDisplayInternal(display, out_retire_fence);
       if (status == HWC2::Error::None) {
         // Check if hwc's refresh trigger is getting exercised.
@@ -1313,6 +1314,11 @@
       return AsFP<HWC2_PFN_SET_DISPLAY_BRIGHTNESS>(HWCSession::SetDisplayBrightness);
     case HWC2::FunctionDescriptor::GetDisplayConnectionType:
       return AsFP<HWC2_PFN_GET_DISPLAY_CONNECTION_TYPE>(HWCSession::GetDisplayConnectionType);
+    case HWC2::FunctionDescriptor::GetDisplayVsyncPeriod:
+      return AsFP<HWC2_PFN_GET_DISPLAY_VSYNC_PERIOD>(HWCSession::GetDisplayVsyncPeriod);
+    case HWC2::FunctionDescriptor::SetActiveConfigWithConstraints:
+      return AsFP<HWC2_PFN_SET_ACTIVE_CONFIG_WITH_CONSTRAINTS>
+                  (HWCSession::SetActiveConfigWithConstraints);
     default:
       DLOGD("Unknown/Unimplemented function descriptor: %d (%s)", int_descriptor,
             to_string(descriptor).c_str());
@@ -3332,6 +3338,26 @@
   return HWC2_ERROR_NONE;
 }
 
+int32_t HWCSession::GetDisplayVsyncPeriod(hwc2_device_t *device, hwc2_display_t display,
+                                          hwc2_vsync_period_t *out_vsync_period) {
+  if (!out_vsync_period || !device) {
+    return HWC2_ERROR_BAD_PARAMETER;
+  }
+
+  return CallDisplayFunction(device, display, &HWCDisplay::GetDisplayVsyncPeriod, out_vsync_period);
+}
+
+int32_t HWCSession::SetActiveConfigWithConstraints(hwc2_device_t *device, hwc2_display_t display,
+                    hwc2_config_t config,
+                    hwc_vsync_period_change_constraints_t *vsync_period_change_constraints,
+                    hwc_vsync_period_change_timeline_t *out_timeline) {
+  if (!vsync_period_change_constraints || !out_timeline || !device) {
+    return HWC2_ERROR_BAD_PARAMETER;
+  }
+
+  return CallDisplayFunction(device, display, &HWCDisplay::SetActiveConfigWithConstraints, config,
+                             vsync_period_change_constraints, out_timeline);
+}
 
 int32_t HWCSession::GetDisplayBrightnessSupport(hwc2_device_t *device, hwc2_display_t display,
                                                 bool *outSupport) {
diff --git a/sdm/libs/hwc2/hwc_session.h b/sdm/libs/hwc2/hwc_session.h
index e0d9821..f056290 100644
--- a/sdm/libs/hwc2/hwc_session.h
+++ b/sdm/libs/hwc2/hwc_session.h
@@ -212,6 +212,12 @@
                                       float brightness);
   static int32_t GetDisplayConnectionType(hwc2_device_t *device, hwc2_display_t display,
                                           uint32_t *outType);
+  static int32_t GetDisplayVsyncPeriod(hwc2_device_t *device, hwc2_display_t display,
+                                       hwc2_vsync_period_t *out_vsync_period);
+  static int32_t SetActiveConfigWithConstraints(hwc2_device_t *device, hwc2_display_t display,
+                    hwc2_config_t config,
+                    hwc_vsync_period_change_constraints_t *vsync_period_change_constraints,
+                    hwc_vsync_period_change_timeline_t *out_timeline);
 
   // HWCDisplayEventHandler
   virtual void DisplayPowerReset();