sdm: Implement idle fallback support in DRM.

Add CRTC property to set idle timeout. Enhance DRM event
handler for idle fall back custom event registration and
event handling

CRs-Fixed: 2097588

Change-Id: I543286a59a4ae95bc32b25960d1fc43551391951
diff --git a/libdrmutils/drm_interface.h b/libdrmutils/drm_interface.h
index f40f324..53a709e 100644
--- a/libdrmutils/drm_interface.h
+++ b/libdrmutils/drm_interface.h
@@ -242,6 +242,12 @@
    */
   CRTC_SET_SOLIDFILL_STAGES,
   /*
+   * Op: Sets idle timeout.
+   * Arg: uint32_t - CRTC ID
+   *      uint32_t - idle timeout in ms
+   */
+  CRTC_SET_IDLE_TIMEOUT,
+  /*
    * Op: Returns retire fence for this commit. Should be called after Commit() on
    * DRMAtomicReqInterface.
    * Arg: uint32_t - Connector ID
diff --git a/sdm/libs/core/display_primary.cpp b/sdm/libs/core/display_primary.cpp
index 1135475..0c8760e 100644
--- a/sdm/libs/core/display_primary.cpp
+++ b/sdm/libs/core/display_primary.cpp
@@ -166,10 +166,7 @@
 
 void DisplayPrimary::SetIdleTimeoutMs(uint32_t active_ms) {
   lock_guard<recursive_mutex> obj(recursive_mutex_);
-
-  if (comp_manager_->SetIdleTimeoutMs(display_comp_ctx_, active_ms) == kErrorNone) {
-    hw_intf_->SetIdleTimeoutMs(active_ms);
-  }
+  comp_manager_->SetIdleTimeoutMs(display_comp_ctx_, active_ms);
 }
 
 DisplayError DisplayPrimary::SetDisplayMode(uint32_t mode) {
@@ -208,7 +205,7 @@
       ControlPartialUpdate(false /* enable */, &pending);
     } else if (mode == kModeCommand) {
       // Flush idle timeout value currently set.
-      hw_intf_->SetIdleTimeoutMs(0);
+      comp_manager_->SetIdleTimeoutMs(display_comp_ctx_, 0);
       switch_to_cmd_ = true;
     }
   }
diff --git a/sdm/libs/core/drm/hw_device_drm.cpp b/sdm/libs/core/drm/hw_device_drm.cpp
index ecc0f63..da33a25 100644
--- a/sdm/libs/core/drm/hw_device_drm.cpp
+++ b/sdm/libs/core/drm/hw_device_drm.cpp
@@ -905,6 +905,13 @@
   }
   drm_atomic_intf_->Perform(DRMOps::CRTC_SET_MODE, token_.crtc_id, &current_mode);
   drm_atomic_intf_->Perform(DRMOps::CRTC_SET_ACTIVE, token_.crtc_id, 1);
+
+  if (!validate && (hw_layer_info.set_idle_time_ms >= 0)) {
+    DLOGI_IF(kTagDriverConfig, "Setting idle timeout to = %d ms",
+             hw_layer_info.set_idle_time_ms);
+    drm_atomic_intf_->Perform(DRMOps::CRTC_SET_IDLE_TIMEOUT, token_.crtc_id,
+                              hw_layer_info.set_idle_time_ms);
+  }
 }
 
 void HWDeviceDRM::AddSolidfillStage(const HWSolidfillStage &sf, uint32_t plane_alpha) {
@@ -1230,7 +1237,9 @@
   return kErrorNotSupported;
 }
 
-void HWDeviceDRM::SetIdleTimeoutMs(uint32_t timeout_ms) {}
+void HWDeviceDRM::SetIdleTimeoutMs(uint32_t timeout_ms) {
+  // TODO(user): This function can be removed after fb is deprecated
+}
 
 DisplayError HWDeviceDRM::SetDisplayMode(const HWDisplayMode hw_display_mode) {
   return kErrorNotSupported;
diff --git a/sdm/libs/core/drm/hw_events_drm.cpp b/sdm/libs/core/drm/hw_events_drm.cpp
index 2745ee7..a90d294 100644
--- a/sdm/libs/core/drm/hw_events_drm.cpp
+++ b/sdm/libs/core/drm/hw_events_drm.cpp
@@ -40,6 +40,7 @@
 #include <utils/debug.h>
 #include <utils/sys.h>
 #include <xf86drm.h>
+#include <drm/msm_drm.h>
 
 #include <algorithm>
 #include <map>
@@ -86,7 +87,15 @@
         // Clear any existing data
         Sys::pread_(poll_fds_[i].fd, data, kMaxStringLength, 0);
       } break;
-      case HWEvent::IDLE_NOTIFY:
+      case HWEvent::IDLE_NOTIFY: {
+        poll_fds_[i].fd = drmOpen("msm_drm", nullptr);
+        if (poll_fds_[i].fd < 0) {
+          DLOGE("drmOpen failed with error %d", poll_fds_[i].fd);
+          return kErrorResources;
+        }
+        poll_fds_[i].events = POLLIN | POLLPRI | POLLERR;
+        idle_notify_index_ = i;
+      } break;
       case HWEvent::CEC_READ_MESSAGE:
       case HWEvent::SHOW_BLANK_EVENT:
       case HWEvent::THERMAL_LEVEL:
@@ -182,6 +191,7 @@
   }
 
   RegisterPanelDead(true);
+  RegisterIdleNotify(true);
 
   return kErrorNone;
 }
@@ -189,6 +199,7 @@
 DisplayError HWEventsDRM::Deinit() {
   exit_threads_ = true;
   RegisterPanelDead(false);
+  RegisterIdleNotify(false);
   Sys::pthread_cancel_(event_thread_);
   WakeUpEventThread();
   pthread_join(event_thread_, NULL);
@@ -294,6 +305,7 @@
       switch (event_data_list_[i].event_type) {
         case HWEvent::VSYNC:
         case HWEvent::PANEL_DEAD:
+        case HWEvent::IDLE_NOTIFY:
           if (poll_fd.revents & (POLLIN | POLLPRI | POLLERR)) {
             (this->*(event_data_list_[i]).event_parser)(nullptr);
           }
@@ -304,7 +316,6 @@
             (this->*(event_data_list_[i]).event_parser)(data);
           }
           break;
-        case HWEvent::IDLE_NOTIFY:
         case HWEvent::CEC_READ_MESSAGE:
         case HWEvent::SHOW_BLANK_EVENT:
         case HWEvent::THERMAL_LEVEL:
@@ -374,6 +385,39 @@
   return kErrorNone;
 }
 
+DisplayError HWEventsDRM::RegisterIdleNotify(bool enable) {
+  uint32_t i = 0;
+  for (; i < event_data_list_.size(); i++) {
+    if (event_data_list_[i].event_type == HWEvent::IDLE_NOTIFY) {
+      break;
+    }
+  }
+
+  if (i == event_data_list_.size()) {
+    DLOGI("idle notify is not supported event");
+    return kErrorNone;
+  }
+
+  struct drm_msm_event_req req = {};
+  int ret = 0;
+
+  req.object_id = token_.crtc_id;
+  req.object_type = DRM_MODE_OBJECT_CRTC;
+  req.event = DRM_EVENT_IDLE_NOTIFY;
+  if (enable) {
+    ret = drmIoctl(poll_fds_[idle_notify_index_].fd, DRM_IOCTL_MSM_REGISTER_EVENT, &req);
+  } else {
+    ret = drmIoctl(poll_fds_[idle_notify_index_].fd, DRM_IOCTL_MSM_DEREGISTER_EVENT, &req);
+  }
+
+  if (ret) {
+    DLOGE("register idle notify enable:%d failed", enable);
+    return kErrorResources;
+  }
+
+  return kErrorNone;
+}
+
 void HWEventsDRM::HandleVSync(char *data) {
   drmEventContext event = {};
   event.version = DRM_EVENT_CONTEXT_VERSION;
@@ -432,7 +476,45 @@
 }
 
 void HWEventsDRM::HandleIdleTimeout(char *data) {
-  event_handler_->IdleTimeout();
+  char event_data[kMaxStringLength];
+  int32_t size;
+  struct drm_msm_event_resp *event_resp = NULL;
+
+  size = (int32_t)Sys::pread_(poll_fds_[idle_notify_index_].fd, event_data, kMaxStringLength, 0);
+  if (size < 0) {
+    return;
+  }
+
+  if (size > kMaxStringLength) {
+    DLOGE("event size %d is greater than event buffer size %zd\n", size, kMaxStringLength);
+    return;
+  }
+
+  if (size < (int32_t)sizeof(*event_resp)) {
+    DLOGE("size %d exp %zd\n", size, sizeof(*event_resp));
+    return;
+  }
+
+  int32_t i = 0;
+
+  while (i < size) {
+    event_resp = (struct drm_msm_event_resp *)&event_data[i];
+    switch (event_resp->base.type) {
+      case DRM_EVENT_IDLE_NOTIFY:
+      {
+        DLOGV("Received Idle time event");
+        event_handler_->IdleTimeout();
+        break;
+      }
+      default: {
+        DLOGE("invalid event %d", event_resp->base.type);
+        break;
+      }
+    }
+    i += event_resp->base.length;
+  }
+
+  return;
 }
 
 void HWEventsDRM::HandleCECMessage(char *data) {
diff --git a/sdm/libs/core/drm/hw_events_drm.h b/sdm/libs/core/drm/hw_events_drm.h
index 4db0346..a7fb463 100644
--- a/sdm/libs/core/drm/hw_events_drm.h
+++ b/sdm/libs/core/drm/hw_events_drm.h
@@ -83,6 +83,7 @@
   DisplayError CloseFds();
   DisplayError RegisterVSync();
   DisplayError RegisterPanelDead(bool enable);
+  DisplayError RegisterIdleNotify(bool enable);
 
   HWEventHandler *event_handler_{};
   vector<HWEventData> event_data_list_{};
@@ -92,6 +93,7 @@
   bool exit_threads_ = false;
   uint32_t vsync_index_ = 0;
   bool vsync_enabled_ = false;
+  uint32_t idle_notify_index_ = 0;
   sde_drm::DRMDisplayToken token_ = {};
   bool is_primary_ = false;
   uint32_t panel_dead_index_ = 0;