drm_hwcomposer: Plumb link status BAD to OnHotplugEvent composer callback

This updates the Uevent handler to handle the hotplug events
sent by the kernel for link training failure.
Adds support in hotplug handler in resourcemanager to read connector
properties and read the value of link-status property.
If this is set to DRM_MODE_LINK_STATUS_BAD, then invoke
the OnHotplugEvent() composer callback with the BAD CABLE
DisplayHotplugEvent to notify the Surfaceflinger about link training
failure.

Currently this uses the ERROR_INCOMPATIBLE_CABLE field of the
DisplayHotplugEvent callback. The longer term would be to define
a new field in this enum to indicate MODESET_RETRY and use this
INCOMPATIBLE_CABLE for the terminal link train failure state.

Change-Id: I823d7570dd08e476d0c887e350bce3f8f0069afb
Signed-off-by: Manasi Navare <navaremanasi@chromium.org>
diff --git a/drm/DrmConnector.cpp b/drm/DrmConnector.cpp
index 8b92407..5371513 100644
--- a/drm/DrmConnector.cpp
+++ b/drm/DrmConnector.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <drm/drm_mode.h>
 #define LOG_TAG "drmhwc"
 
 #include "DrmConnector.h"
@@ -230,4 +231,14 @@
   return 0;
 }
 
+bool DrmConnector::IsLinkStatusGood() {
+  if (GetConnectorProperty("link-status", &link_status_property_, false)) {
+    auto link_status_property_value = link_status_property_.GetValue();
+    if (link_status_property_value &&
+        (link_status_property_value == DRM_MODE_LINK_STATUS_BAD))
+      return false;
+  }
+
+  return true;
+}
 }  // namespace android
diff --git a/drm/DrmConnector.h b/drm/DrmConnector.h
index 8063fd8..9186f07 100644
--- a/drm/DrmConnector.h
+++ b/drm/DrmConnector.h
@@ -80,6 +80,8 @@
 
   int UpdateModes();
 
+  bool IsLinkStatusGood();
+
   auto &GetModes() const {
     return modes_;
   }
@@ -151,6 +153,7 @@
   DrmProperty colorspace_property_;
   DrmProperty content_type_property_;
 
+  DrmProperty link_status_property_;
   DrmProperty writeback_pixel_formats_;
   DrmProperty writeback_fb_id_;
   DrmProperty writeback_out_fence_;
diff --git a/drm/DrmHwc.cpp b/drm/DrmHwc.cpp
index df3eb56..57293c2 100644
--- a/drm/DrmHwc.cpp
+++ b/drm/DrmHwc.cpp
@@ -124,6 +124,16 @@
   return true;
 }
 
+void DrmHwc::NotifyDisplayLinkStatus(
+    std::shared_ptr<DrmDisplayPipeline> pipeline) {
+  if (display_handles_.count(pipeline) == 0) {
+    ALOGE("%s, can't find the display, pipeline: %p", __func__, pipeline.get());
+    return;
+  }
+  ScheduleHotplugEvent(display_handles_[pipeline],
+                       DisplayStatus::kLinkTrainingFailed);
+}
+
 HWC2::Error DrmHwc::CreateVirtualDisplay(
     uint32_t width, uint32_t height,
     int32_t *format,  // NOLINT(readability-non-const-parameter)
diff --git a/drm/DrmHwc.h b/drm/DrmHwc.h
index 44dc276..ac97717 100644
--- a/drm/DrmHwc.h
+++ b/drm/DrmHwc.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include "drm/DrmDisplayPipeline.h"
 #include "drm/ResourceManager.h"
 #include "hwc2_device/HwcDisplay.h"
 
@@ -26,6 +27,13 @@
   DrmHwc();
   ~DrmHwc() override = default;
 
+  // Enum for Display status: Connected, Disconnected, Link Training Failed
+  enum DisplayStatus {
+    kDisconnected,
+    kConnected,
+    kLinkTrainingFailed,
+  };
+
   // Client Callback functions.:
   virtual void SendVsyncEventToClient(hwc2_display_t displayid,
                                       int64_t timestamp,
@@ -34,7 +42,7 @@
       hwc2_display_t displayid, int64_t timestamp) const = 0;
   virtual void SendRefreshEventToClient(uint64_t displayid) = 0;
   virtual void SendHotplugEventToClient(hwc2_display_t displayid,
-                                        bool connected) = 0;
+                                        enum DisplayStatus display_status) = 0;
 
   // Device functions
   HWC2::Error CreateVirtualDisplay(uint32_t width, uint32_t height,
@@ -53,8 +61,9 @@
     return resource_manager_;
   }
 
-  void ScheduleHotplugEvent(hwc2_display_t displayid, bool connected) {
-    deferred_hotplug_events_[displayid] = connected;
+  void ScheduleHotplugEvent(hwc2_display_t displayid,
+                            enum DisplayStatus display_status) {
+    deferred_hotplug_events_[displayid] = display_status;
   }
 
   void DeinitDisplays();
@@ -64,8 +73,14 @@
   bool UnbindDisplay(std::shared_ptr<DrmDisplayPipeline> pipeline) override;
   void FinalizeDisplayBinding() override;
 
+  // Notify Display Link Status
+  void NotifyDisplayLinkStatus(
+      std::shared_ptr<DrmDisplayPipeline> pipeline) override;
+
  protected:
-  auto& Displays() { return displays_; }
+  auto &Displays() {
+    return displays_;
+  }
 
  private:
   ResourceManager resource_manager_;
@@ -75,7 +90,7 @@
 
   std::string dump_string_;
 
-  std::map<hwc2_display_t, bool> deferred_hotplug_events_;
+  std::map<hwc2_display_t, enum DisplayStatus> deferred_hotplug_events_;
   std::vector<hwc2_display_t> displays_for_removal_list_;
 
   uint32_t last_display_handle_ = kPrimaryDisplay;
diff --git a/drm/ResourceManager.cpp b/drm/ResourceManager.cpp
index 5ac80c4..0c23734 100644
--- a/drm/ResourceManager.cpp
+++ b/drm/ResourceManager.cpp
@@ -154,6 +154,10 @@
         attached_pipelines_.erase(conn);
       }
     }
+    if (connected) {
+      if (!conn->IsLinkStatusGood())
+        frontend_interface_->NotifyDisplayLinkStatus(attached_pipelines_[conn]);
+    }
   }
   frontend_interface_->FinalizeDisplayBinding();
 }
diff --git a/drm/ResourceManager.h b/drm/ResourceManager.h
index 9a2652c..3c4d7ae 100644
--- a/drm/ResourceManager.h
+++ b/drm/ResourceManager.h
@@ -22,6 +22,7 @@
 #include "DrmDevice.h"
 #include "DrmDisplayPipeline.h"
 #include "DrmFbImporter.h"
+#include "DrmProperty.h"
 #include "UEventListener.h"
 
 namespace android {
@@ -37,6 +38,8 @@
   virtual bool BindDisplay(std::shared_ptr<DrmDisplayPipeline>) = 0;
   virtual bool UnbindDisplay(std::shared_ptr<DrmDisplayPipeline>) = 0;
   virtual void FinalizeDisplayBinding() = 0;
+  virtual void NotifyDisplayLinkStatus(
+      std::shared_ptr<DrmDisplayPipeline> pipeline) = 0;
 };
 
 class ResourceManager {
diff --git a/hwc2_device/DrmHwcTwo.cpp b/hwc2_device/DrmHwcTwo.cpp
index c120b63..b243199 100644
--- a/hwc2_device/DrmHwcTwo.cpp
+++ b/hwc2_device/DrmHwcTwo.cpp
@@ -69,15 +69,16 @@
 }
 
 void DrmHwcTwo::SendHotplugEventToClient(hwc2_display_t displayid,
-                                         bool connected) {
+                                         DisplayStatus display_status) {
   auto hc = hotplug_callback_;
+
   if (hc.first != nullptr && hc.second != nullptr) {
     /* For some reason HWC Service will call HWC2 API in hotplug callback
      * handler. This is the reason we're using recursive mutex.
      */
     hc.first(hc.second, displayid,
-             connected ? HWC2_CONNECTION_CONNECTED
-                       : HWC2_CONNECTION_DISCONNECTED);
+             display_status ? HWC2_CONNECTION_CONNECTED
+                            : HWC2_CONNECTION_DISCONNECTED);
   }
 }
 
diff --git a/hwc2_device/DrmHwcTwo.h b/hwc2_device/DrmHwcTwo.h
index b3ca0f8..ac87153 100644
--- a/hwc2_device/DrmHwcTwo.h
+++ b/hwc2_device/DrmHwcTwo.h
@@ -37,7 +37,7 @@
       hwc2_display_t displayid, int64_t timestamp) const override;
   void SendRefreshEventToClient(uint64_t displayid) override;
   void SendHotplugEventToClient(hwc2_display_t displayid,
-                                bool connected) override;
+                                DisplayStatus display_status) override;
 
  private:
   std::pair<HWC2_PFN_HOTPLUG, hwc2_callback_data_t> hotplug_callback_{};
diff --git a/hwc2_device/HwcDisplay.cpp b/hwc2_device/HwcDisplay.cpp
index 7772175..7f65e65 100644
--- a/hwc2_device/HwcDisplay.cpp
+++ b/hwc2_device/HwcDisplay.cpp
@@ -19,6 +19,8 @@
 
 #include "HwcDisplay.h"
 
+#include <cinttypes>
+
 #include "backend/Backend.h"
 #include "backend/BackendManager.h"
 #include "bufferinfo/BufferInfoGetter.h"
@@ -96,9 +98,9 @@
 
   if (pipeline_ != nullptr || handle_ == kPrimaryDisplay) {
     Init();
-    hwc_->ScheduleHotplugEvent(handle_, /*connected = */ true);
+    hwc_->ScheduleHotplugEvent(handle_, DrmHwc::kConnected);
   } else {
-    hwc_->ScheduleHotplugEvent(handle_, /*connected = */ false);
+    hwc_->ScheduleHotplugEvent(handle_, DrmHwc::kDisconnected);
   }
 }
 
diff --git a/hwc3/DrmHwcThree.cpp b/hwc3/DrmHwcThree.cpp
index d758865..2ac307c 100644
--- a/hwc3/DrmHwcThree.cpp
+++ b/hwc3/DrmHwcThree.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "drmhwc"
+
 #include "DrmHwcThree.h"
 
 #include <cinttypes>
@@ -59,10 +61,22 @@
                               static_cast<int32_t>(vsync_period));
 }
 
-void DrmHwcThree::SendHotplugEventToClient(hwc2_display_t display_id,
-                                           bool connected) {
-  HandleDisplayHotplugEvent(static_cast<uint64_t>(display_id), connected);
-  common::DisplayHotplugEvent event = connected ? common::DisplayHotplugEvent::CONNECTED : common::DisplayHotplugEvent::DISCONNECTED;
+void DrmHwcThree::SendHotplugEventToClient(
+    hwc2_display_t display_id, DrmHwc::DisplayStatus display_status) {
+  common::DisplayHotplugEvent event = common::DisplayHotplugEvent::DISCONNECTED;
+  switch (display_status) {
+    case DrmHwc::kDisconnected:
+      event = common::DisplayHotplugEvent::DISCONNECTED;
+      HandleDisplayHotplugEvent(static_cast<uint64_t>(display_id), false);
+      break;
+    case DrmHwc::kConnected:
+      event = common::DisplayHotplugEvent::CONNECTED;
+      HandleDisplayHotplugEvent(static_cast<uint64_t>(display_id), true);
+      break;
+    case DrmHwc::kLinkTrainingFailed:
+      event = common::DisplayHotplugEvent::ERROR_INCOMPATIBLE_CABLE;
+      break;
+  }
   composer_callback_->onHotplugEvent(static_cast<int64_t>(display_id), event);
 }
 
diff --git a/hwc3/DrmHwcThree.h b/hwc3/DrmHwcThree.h
index 8ba96c6..f020634 100644
--- a/hwc3/DrmHwcThree.h
+++ b/hwc3/DrmHwcThree.h
@@ -39,7 +39,7 @@
       hwc2_display_t display_id, int64_t timestamp) const override;
   void SendRefreshEventToClient(uint64_t display_id) override;
   void SendHotplugEventToClient(hwc2_display_t display_id,
-                                bool connected) override;
+                                DrmHwc::DisplayStatus display_status) override;
 
  private:
   void CleanDisplayResources(uint64_t display_id);