sdm: Implement getDisplayIdentificationData

* Implement call flow from client to display interface.

* Use display interface to query EDID blob from Connector information
  and return to client alongside pre-established port information from
  sde-drm during display creation.

* Create EDID blob for Null Display (QMAA) to represent a 1920x1080
  60fps display with panel name "Null Display" and port corresponding to
  either builtin or external, depending on DisplayNull class instantiated.

* Introduce kErrorDriverData as a display interface error to represent
  situations in which expected data from the driver is missing.
  Currently this applied to EDID blob for all physical panels.

* Update UnregisterDisplay API to allow cleanup of token via sde-drm.

Change-Id: I5661f7bc0a5a68cdffa0cad3fe2351baa647012c
CRs-Fixed: 2380873
diff --git a/libdrmutils/drm_interface.h b/libdrmutils/drm_interface.h
index 4a47469..d0f1b81 100644
--- a/libdrmutils/drm_interface.h
+++ b/libdrmutils/drm_interface.h
@@ -612,6 +612,7 @@
   bool is_wb_ubwc_supported;
   uint32_t topology_control;
   bool dyn_bitclk_support;
+  std::vector<uint8_t> edid;
 };
 
 // All DRM Connectors as map<Connector_id , connector_info>
@@ -631,6 +632,7 @@
   uint32_t crtc_id;
   uint32_t crtc_index;
   uint32_t encoder_id;
+  uint8_t hw_port;
 };
 
 enum DRMPPFeatureID {
@@ -922,12 +924,12 @@
    * [output]: DRMDisplayToken - CRTC and Connector id's for the display.
    * [return]: 0 on success, a negative error value otherwise.
    */
-  virtual int RegisterDisplay(int32_t display_id, DRMDisplayToken *tok) = 0;
+  virtual int RegisterDisplay(int32_t display_id, DRMDisplayToken *token) = 0;
 
   /* Client should invoke this interface on display disconnect.
    * [input]: DRMDisplayToken - identifier for the display.
    */
-  virtual void UnregisterDisplay(const DRMDisplayToken &token) = 0;
+  virtual void UnregisterDisplay(DRMDisplayToken *token) = 0;
 
   /*
    * Creates and returns an instance of DRMAtomicReqInterface corresponding to a display token
diff --git a/sdm/include/core/display_interface.h b/sdm/include/core/display_interface.h
index 1b4d063..c744fea 100644
--- a/sdm/include/core/display_interface.h
+++ b/sdm/include/core/display_interface.h
@@ -832,6 +832,17 @@
   */
   virtual DisplayError GetSupportedDSIClock(std::vector<uint64_t> *bitclk_rates) = 0;
 
+  /*! @brief Method to retrieve the EDID information and HW port ID for display
+
+    @param[out] HW port ID
+    @param[out] size of EDID blob data
+    @param[out] EDID blob
+
+    @return \link DisplayError \endlink
+  */
+  virtual DisplayError GetDisplayIdentificationData(uint8_t *out_port, uint32_t *out_data_size,
+                                                    uint8_t *out_data) = 0;
+
  protected:
   virtual ~DisplayInterface() { }
 };
diff --git a/sdm/include/core/sdm_types.h b/sdm/include/core/sdm_types.h
index 0f6973d..5817539 100644
--- a/sdm/include/core/sdm_types.h
+++ b/sdm/include/core/sdm_types.h
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2014 - 2018, The Linux Foundation. All rights reserved.
+* Copyright (c) 2014 - 2019, The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
@@ -59,6 +59,7 @@
   kErrorNotValidated,     //!< Draw cycle has not been validated.
   kErrorCriticalResource,   //!< Critical resource allocation has failed.
   kErrorDeviceRemoved,    //!< A device was removed unexpectedly.
+  kErrorDriverData,       //!< Expected information from the driver is missing
 };
 
 /*! @brief This structure is defined for client and library compatibility check purpose only. This
diff --git a/sdm/libs/core/display_base.cpp b/sdm/libs/core/display_base.cpp
index fb88ae3..87f8fe4 100644
--- a/sdm/libs/core/display_base.cpp
+++ b/sdm/libs/core/display_base.cpp
@@ -1564,6 +1564,15 @@
   return kErrorNone;
 }
 
+DisplayError DisplayBase::GetDisplayIdentificationData(uint8_t *out_port, uint32_t *out_data_size,
+                                                       uint8_t *out_data) {
+  if (!out_port || !out_data_size) {
+    return kErrorParameters;
+  }
+
+  return hw_intf_->GetDisplayIdentificationData(out_port, out_data_size, out_data);
+}
+
 DisplayError DisplayBase::GetClientTargetSupport(uint32_t width, uint32_t height,
                                                  LayerBufferFormat format,
                                                  const ColorMetaData &color_metadata) {
diff --git a/sdm/libs/core/display_base.h b/sdm/libs/core/display_base.h
index 6513b08..ab39263 100644
--- a/sdm/libs/core/display_base.h
+++ b/sdm/libs/core/display_base.h
@@ -145,6 +145,8 @@
     return kErrorNotSupported;
   }
   virtual bool IsSupportSsppTonemap();
+  virtual DisplayError GetDisplayIdentificationData(uint8_t *out_port, uint32_t *out_data_size,
+                                                    uint8_t *out_data);
 
  protected:
   const char *kBt2020Pq = "bt2020_pq";
diff --git a/sdm/libs/core/drm/hw_device_drm.cpp b/sdm/libs/core/drm/hw_device_drm.cpp
index 51a47c8..6086033 100644
--- a/sdm/libs/core/drm/hw_device_drm.cpp
+++ b/sdm/libs/core/drm/hw_device_drm.cpp
@@ -444,7 +444,7 @@
 
   if (token_.conn_id > INT32_MAX) {
     DLOGE("Connector id %u beyond supported range", token_.conn_id);
-    drm_mgr_intf_->UnregisterDisplay(token_);
+    drm_mgr_intf_->UnregisterDisplay(&token_);
     return kErrorNotSupported;
   }
 
@@ -453,7 +453,7 @@
   ret = drm_mgr_intf_->CreateAtomicReq(token_, &drm_atomic_intf_);
   if (ret) {
     DLOGE("Failed creating atomic request for connector id %u. Error: %d.", token_.conn_id, ret);
-    drm_mgr_intf_->UnregisterDisplay(token_);
+    drm_mgr_intf_->UnregisterDisplay(&token_);
     return kErrorResources;
   }
 
@@ -462,7 +462,7 @@
     DLOGE("Failed getting info for connector id %u. Error: %d.", token_.conn_id, ret);
     drm_mgr_intf_->DestroyAtomicReq(drm_atomic_intf_);
     drm_atomic_intf_ = {};
-    drm_mgr_intf_->UnregisterDisplay(token_);
+    drm_mgr_intf_->UnregisterDisplay(&token_);
     return kErrorHardware;
   }
 
@@ -472,7 +472,7 @@
           connector_info_.modes.size());
     drm_mgr_intf_->DestroyAtomicReq(drm_atomic_intf_);
     drm_atomic_intf_ = {};
-    drm_mgr_intf_->UnregisterDisplay(token_);
+    drm_mgr_intf_->UnregisterDisplay(&token_);
     return kErrorDeviceRemoved;
   }
 
@@ -517,7 +517,7 @@
   display_attributes_ = {};
   drm_mgr_intf_->DestroyAtomicReq(drm_atomic_intf_);
   drm_atomic_intf_ = {};
-  drm_mgr_intf_->UnregisterDisplay(token_);
+  drm_mgr_intf_->UnregisterDisplay(&token_);
   return err;
 }
 
@@ -718,6 +718,25 @@
   DLOGI("Dynamic Bit Clk Support = %d", hw_panel_info_.dyn_bitclk_support);
 }
 
+DisplayError HWDeviceDRM::GetDisplayIdentificationData(uint8_t *out_port, uint32_t *out_data_size,
+                                                       uint8_t *out_data) {
+  *out_port = token_.hw_port;
+  std::vector<uint8_t> &edid = connector_info_.edid;
+
+  if (out_data == nullptr) {
+    *out_data_size = (uint32_t)(edid.size());
+    if (*out_data_size == 0) {
+      DLOGE("EDID blob is empty, no data to return");
+      return kErrorDriverData;
+    }
+  } else {
+    *out_data_size = std::min(*out_data_size, (uint32_t)(edid.size()));
+    memcpy(out_data, edid.data(), *out_data_size);
+  }
+
+  return kErrorNone;
+}
+
 void HWDeviceDRM::GetHWDisplayPortAndMode() {
   hw_panel_info_.port = kPortDefault;
   hw_panel_info_.mode =
diff --git a/sdm/libs/core/drm/hw_device_drm.h b/sdm/libs/core/drm/hw_device_drm.h
index 5be5fad..21a64c2 100644
--- a/sdm/libs/core/drm/hw_device_drm.h
+++ b/sdm/libs/core/drm/hw_device_drm.h
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+* Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
@@ -123,6 +123,8 @@
   virtual DisplayError SetDisplayDppsAdROI(void *payload) { return kErrorNotSupported; }
   virtual DisplayError SetDynamicDSIClock(uint64_t bit_clk_rate);
   virtual DisplayError GetDynamicDSIClock(uint64_t *bit_clk_rate);
+  virtual DisplayError GetDisplayIdentificationData(uint8_t *out_port, uint32_t *out_data_size,
+                                                    uint8_t *out_data);
 
   enum {
     kHWEventVSync,
diff --git a/sdm/libs/core/drm/hw_info_drm.cpp b/sdm/libs/core/drm/hw_info_drm.cpp
index 54c8554..faaebea 100644
--- a/sdm/libs/core/drm/hw_info_drm.cpp
+++ b/sdm/libs/core/drm/hw_info_drm.cpp
@@ -501,7 +501,7 @@
   ret = drm_mgr_intf_->GetConnectorInfo(token.conn_id, &connector_info);
   if (ret) {
     DLOGE("Failed getting info for connector id %u. Error: %d.", token.conn_id, ret);
-    drm_mgr_intf_->UnregisterDisplay(token);
+    drm_mgr_intf_->UnregisterDisplay(&token);
     return;
   }
   for (auto &fmts : connector_info.formats_supported) {
@@ -511,7 +511,7 @@
   hw_resource->supported_formats_map.erase(sub_blk_type);
   hw_resource->supported_formats_map.insert(make_pair(sub_blk_type, supported_sdm_formats));
 
-  drm_mgr_intf_->UnregisterDisplay(token);
+  drm_mgr_intf_->UnregisterDisplay(&token);
 }
 
 void HWInfoDRM::GetSDMFormat(uint32_t v4l2_format, LayerBufferFormat *sdm_format) {
diff --git a/sdm/libs/core/drm/hw_peripheral_drm.cpp b/sdm/libs/core/drm/hw_peripheral_drm.cpp
index 0bc95cd..ec865fe 100644
--- a/sdm/libs/core/drm/hw_peripheral_drm.cpp
+++ b/sdm/libs/core/drm/hw_peripheral_drm.cpp
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
@@ -336,7 +336,7 @@
 
 DisplayError HWPeripheralDRM::TeardownConcurrentWriteback(void) {
   if (cwb_config_.enabled) {
-    drm_mgr_intf_->UnregisterDisplay(cwb_config_.token);
+    drm_mgr_intf_->UnregisterDisplay(&(cwb_config_.token));
     cwb_config_.enabled = false;
     registry_.Clear();
   }
@@ -369,7 +369,7 @@
   ret = drmIoctl(dev_fd_, DRM_IOCTL_SDE_WB_CONFIG, &cwb_cfg);
 #endif
   if (ret) {
-    drm_mgr_intf_->UnregisterDisplay(cwb_config_.token);
+    drm_mgr_intf_->UnregisterDisplay(&(cwb_config_.token));
     DLOGE("Dump CWBConfig: mode_count %d flags %x", cwb_cfg.count_modes, cwb_cfg.flags);
     DumpConnectorModeInfo();
     return kErrorHardware;
diff --git a/sdm/libs/core/drm/hw_virtual_drm.cpp b/sdm/libs/core/drm/hw_virtual_drm.cpp
index fbcec91..152e28c 100644
--- a/sdm/libs/core/drm/hw_virtual_drm.cpp
+++ b/sdm/libs/core/drm/hw_virtual_drm.cpp
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
@@ -245,5 +245,13 @@
   return kErrorNone;
 }
 
+DisplayError HWVirtualDRM::GetDisplayIdentificationData(uint8_t *out_port, uint32_t *out_data_size,
+                                                        uint8_t *out_data) {
+  *out_data_size = 0;
+  *out_port = token_.hw_port;
+
+  return kErrorNone;
+}
+
 }  // namespace sdm
 
diff --git a/sdm/libs/core/drm/hw_virtual_drm.h b/sdm/libs/core/drm/hw_virtual_drm.h
index 0584068..7912d88 100644
--- a/sdm/libs/core/drm/hw_virtual_drm.h
+++ b/sdm/libs/core/drm/hw_virtual_drm.h
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
@@ -60,6 +60,8 @@
   virtual DisplayError UnsetScaleLutConfig() {
     return kErrorNotSupported;
   }
+  virtual DisplayError GetDisplayIdentificationData(uint8_t *out_port, uint32_t *out_data_size,
+                                                    uint8_t *out_data);
 
  private:
   void ConfigureWbConnectorFbId(uint32_t fb_id);
diff --git a/sdm/libs/core/fb/hw_device.h b/sdm/libs/core/fb/hw_device.h
index 5f451dc..5e4e1cf 100644
--- a/sdm/libs/core/fb/hw_device.h
+++ b/sdm/libs/core/fb/hw_device.h
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2014 - 2018, The Linux Foundation. All rights reserved.
+* Copyright (c) 2014 - 2019, The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
@@ -113,6 +113,10 @@
   virtual DisplayError SetDynamicDSIClock(uint64_t bit_clk_rate);
   virtual DisplayError GetDynamicDSIClock(uint64_t *bit_clk_rate);
   virtual DisplayError TeardownConcurrentWriteback(void) { return kErrorNotSupported; }
+  virtual DisplayError GetDisplayIdentificationData(uint8_t *out_port, uint32_t *out_data_size,
+                                                    uint8_t *out_data) {
+    return kErrorNotSupported;
+  }
 
   enum {
     kHWEventVSync,
diff --git a/sdm/libs/core/hw_interface.h b/sdm/libs/core/hw_interface.h
index 240517a..5b68536 100644
--- a/sdm/libs/core/hw_interface.h
+++ b/sdm/libs/core/hw_interface.h
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2014 - 2018, The Linux Foundation. All rights reserved.
+* Copyright (c) 2014 - 2019, The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
@@ -124,6 +124,8 @@
   virtual DisplayError SetDynamicDSIClock(uint64_t bit_clk_rate) = 0;
   virtual DisplayError GetDynamicDSIClock(uint64_t *bit_clk_rate) = 0;
   virtual DisplayError TeardownConcurrentWriteback(void) = 0;
+  virtual DisplayError GetDisplayIdentificationData(uint8_t *out_port, uint32_t *out_data_size,
+                                                    uint8_t *out_data) = 0;
 
  protected:
   virtual ~HWInterface() { }
diff --git a/sdm/libs/hwc2/display_null.cpp b/sdm/libs/hwc2/display_null.cpp
index 9076779..48f5a13 100644
--- a/sdm/libs/hwc2/display_null.cpp
+++ b/sdm/libs/hwc2/display_null.cpp
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+* Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
@@ -27,6 +27,7 @@
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
+#include <algorithm>
 #include "display_null.h"
 
 #define __CLASS__ "DisplayNull"
@@ -122,6 +123,19 @@
   return kErrorNone;
 }
 
+DisplayError DisplayNull::GetDisplayIdentificationData(uint8_t *out_port, uint32_t *out_data_size,
+                                                       uint8_t *out_data) {
+  *out_port = 1;  // DSI0 Encoder Index
+  if (out_data == nullptr) {
+    *out_data_size = (uint32_t)(edid_.size());
+  } else {
+    *out_data_size = std::min(*out_data_size, (uint32_t)(edid_.size()));
+    memcpy(out_data, edid_.data(), *out_data_size);
+  }
+
+  return kErrorNone;
+}
+
 DisplayError DisplayNullExternal::Commit(LayerStack *layer_stack) {
   if (!layer_stack) {
     return kErrorParameters;
@@ -167,4 +181,13 @@
   return kErrorNone;
 }
 
+DisplayError DisplayNullExternal::GetDisplayIdentificationData(uint8_t *out_port,
+                                                               uint32_t *out_data_size,
+                                                               uint8_t *out_data) {
+  DisplayNull::GetDisplayIdentificationData(out_port, out_data_size, out_data);
+  *out_port = 4;  // TMDS Encoder Index
+
+  return kErrorNone;
+}
+
 }  // namespace sdm
diff --git a/sdm/libs/hwc2/display_null.h b/sdm/libs/hwc2/display_null.h
index 2dcb9c9..1d54f65 100644
--- a/sdm/libs/hwc2/display_null.h
+++ b/sdm/libs/hwc2/display_null.h
@@ -36,6 +36,9 @@
 
 namespace sdm {
 
+using std::string;
+using std::vector;
+
 #define MAKE_NO_OP(virtual_method_signature) \
       virtual DisplayError virtual_method_signature { return kErrorNone; }
 
@@ -55,7 +58,9 @@
   virtual bool IsPrimaryDisplay() { return true; }
   virtual bool IsUnderscanSupported() { return true; }
   virtual void SetIdleTimeoutMs(uint32_t active_ms) { }
-  virtual std::string Dump() { return ""; }
+  virtual DisplayError GetDisplayIdentificationData(uint8_t *out_port, uint32_t *out_data_size,
+                                                    uint8_t *out_data);
+  virtual string Dump() { return ""; }
   virtual bool IsSupportSsppTonemap() { return false; }
 
   MAKE_NO_OP(TeardownConcurrentWriteback(void))
@@ -77,13 +82,13 @@
   MAKE_NO_OP(ColorSVCRequestRoute(const PPDisplayAPIPayload &, PPDisplayAPIPayload *,
                                   PPPendingParams *))
   MAKE_NO_OP(GetColorModeCount(uint32_t *))
-  MAKE_NO_OP(GetColorModes(uint32_t *, std::vector<std::string> *))
-  MAKE_NO_OP(GetColorModeAttr(const std::string &, AttrVal *))
-  MAKE_NO_OP(SetColorMode(const std::string &))
+  MAKE_NO_OP(GetColorModes(uint32_t *, vector<string> *))
+  MAKE_NO_OP(GetColorModeAttr(const string &, AttrVal *))
+  MAKE_NO_OP(SetColorMode(const string &))
   MAKE_NO_OP(SetColorModeById(int32_t))
-  MAKE_NO_OP(GetColorModeName(int32_t, std::string *))
+  MAKE_NO_OP(GetColorModeName(int32_t, string *))
   MAKE_NO_OP(SetColorTransform(const uint32_t, const double *))
-  MAKE_NO_OP(GetDefaultColorMode(std::string *))
+  MAKE_NO_OP(GetDefaultColorMode(string *))
   MAKE_NO_OP(ApplyDefaultDisplayMode())
   MAKE_NO_OP(SetCursorPosition(int, int))
   MAKE_NO_OP(SetRefreshRate(uint32_t, bool))
@@ -103,10 +108,23 @@
   MAKE_NO_OP(SetDisplayDppsAdROI(void *))
   MAKE_NO_OP(SetDynamicDSIClock(uint64_t bit_clk_rate))
   MAKE_NO_OP(GetDynamicDSIClock(uint64_t *bit_clk_rate))
-  MAKE_NO_OP(GetSupportedDSIClock(std::vector<uint64_t> *bitclk_rates))
+  MAKE_NO_OP(GetSupportedDSIClock(vector<uint64_t> *bitclk_rates))
 
+ protected:
   DisplayConfigVariableInfo default_variable_config_ = {};
   DisplayConfigFixedInfo default_fixed_config_ = {};
+  // 1920x1080 60fps panel of name Null Display with PnPID QCM for Qualcomm
+  // Contains many 'don't-care' fields and valid checksum bytes
+  const vector<uint8_t> edid_{
+    0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x44, 0x6D, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00,
+    0x1B, 0x10, 0x01, 0x03, 0x80, 0x50, 0x2D, 0x78, 0x0A, 0x0D, 0xC9, 0xA0, 0x57, 0x47, 0x98, 0x27,
+    0x12, 0x48, 0x4C, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3A, 0x80, 0x18, 0x71, 0x38, 0x2D, 0x40, 0x58, 0x2C,
+    0x45, 0x00, 0x50, 0x1D, 0x74, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x4E, 0x75, 0x6C,
+    0x6C, 0x20, 0x44, 0x69, 0x73, 0x70, 0x6C, 0x61, 0x79, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD1
+  };
 };
 
 class DisplayNullExternal : public DisplayNull {
@@ -117,6 +135,8 @@
                                        int *release_fence);
   virtual DisplayError SetFrameBufferConfig(const DisplayConfigVariableInfo &variable_info);
   virtual DisplayError GetFrameBufferConfig(DisplayConfigVariableInfo *variable_info);
+  virtual DisplayError GetDisplayIdentificationData(uint8_t *out_port, uint32_t *out_data_size,
+                                                    uint8_t *out_data);
   void SetActive(bool active) { active_ = active; }
   bool IsActive() { return active_; }
 
diff --git a/sdm/libs/hwc2/hwc_display.cpp b/sdm/libs/hwc2/hwc_display.cpp
index d49da4c..859f369 100644
--- a/sdm/libs/hwc2/hwc_display.cpp
+++ b/sdm/libs/hwc2/hwc_display.cpp
@@ -2255,6 +2255,16 @@
   return ((*out_num_types > 0) ? HWC2::Error::HasChanges : HWC2::Error::None);
 }
 
+HWC2::Error HWCDisplay::GetDisplayIdentificationData(uint8_t *out_port, uint32_t *out_data_size,
+                                                     uint8_t *out_data) {
+  DisplayError ret = display_intf_->GetDisplayIdentificationData(out_port, out_data_size, out_data);
+  if (ret != kErrorNone) {
+    DLOGE("GetDisplayIdentificationData failed due to SDM/Driver (err = %d, disp id = %" PRIu64
+          " %d-%d", ret, id_, sdm_id_, type_);
+  }
+  return HWC2::Error::None;
+}
+
 bool HWCDisplay::IsDisplayCommandMode() {
   return is_cmd_mode_;
 }
diff --git a/sdm/libs/hwc2/hwc_display.h b/sdm/libs/hwc2/hwc_display.h
index c38c6bb..35679c1 100644
--- a/sdm/libs/hwc2/hwc_display.h
+++ b/sdm/libs/hwc2/hwc_display.h
@@ -312,6 +312,8 @@
   virtual DisplayError ControlIdlePowerCollapse(bool enable, bool synchronous) {
     return kErrorNone;
   }
+  virtual HWC2::Error GetDisplayIdentificationData(uint8_t *out_port, uint32_t *out_data_size,
+                                                   uint8_t *out_data);
 
  protected:
   static uint32_t throttling_refresh_rate_;
diff --git a/sdm/libs/hwc2/hwc_session.cpp b/sdm/libs/hwc2/hwc_session.cpp
index cf52eec..9640d67 100644
--- a/sdm/libs/hwc2/hwc_session.cpp
+++ b/sdm/libs/hwc2/hwc_session.cpp
@@ -1122,14 +1122,17 @@
     case HWC2::FunctionDescriptor::GetRenderIntents:
       return AsFP<HWC2_PFN_GET_RENDER_INTENTS>(GetRenderIntents);
     case HWC2::FunctionDescriptor::SetColorModeWithRenderIntent:
-      return AsFP<HWC2_PFN_SET_COLOR_MODE_WITH_RENDER_INTENT>(
-          HWCSession::SetColorModeWithRenderIntent);
+      return AsFP<HWC2_PFN_SET_COLOR_MODE_WITH_RENDER_INTENT>
+             (HWCSession::SetColorModeWithRenderIntent);
     case HWC2::FunctionDescriptor::GetDataspaceSaturationMatrix:
       return AsFP<HWC2_PFN_GET_DATASPACE_SATURATION_MATRIX>(GetDataspaceSaturationMatrix);
     case HWC2::FunctionDescriptor::GetPerFrameMetadataKeys:
       return AsFP<HWC2_PFN_GET_PER_FRAME_METADATA_KEYS>(GetPerFrameMetadataKeys);
     case HWC2::FunctionDescriptor::SetLayerPerFrameMetadata:
       return AsFP<HWC2_PFN_SET_LAYER_PER_FRAME_METADATA>(SetLayerPerFrameMetadata);
+    case HWC2::FunctionDescriptor::GetDisplayIdentificationData:
+      return AsFP<HWC2_PFN_GET_DISPLAY_IDENTIFICATION_DATA>
+             (HWCSession::GetDisplayIdentificationData);
     default:
       DLOGD("Unknown/Unimplemented function descriptor: %d (%s)", int_descriptor,
             to_string(descriptor).c_str());
@@ -2932,6 +2935,21 @@
   return CallDisplayFunction(device, display, &HWCDisplay::GetReadbackBufferFence, release_fence);
 }
 
+int32_t HWCSession::GetDisplayIdentificationData(hwc2_device_t *device, hwc2_display_t display,
+                                                 uint8_t *outPort, uint32_t *outDataSize,
+                                                 uint8_t *outData) {
+  if (!outPort || !outDataSize) {
+    return HWC2_ERROR_BAD_PARAMETER;
+  }
+
+  if (display >= kNumDisplays) {
+    return HWC2_ERROR_BAD_DISPLAY;
+  }
+
+  return CallDisplayFunction(device, display, &HWCDisplay::GetDisplayIdentificationData, outPort,
+                             outDataSize, outData);
+}
+
 android::status_t HWCSession::SetQSyncMode(const android::Parcel *input_parcel) {
   auto mode = input_parcel->readInt32();
   auto device = static_cast<hwc2_device_t *>(this);
diff --git a/sdm/libs/hwc2/hwc_session.h b/sdm/libs/hwc2/hwc_session.h
index 676d3a1..3c56470 100644
--- a/sdm/libs/hwc2/hwc_session.h
+++ b/sdm/libs/hwc2/hwc_session.h
@@ -221,6 +221,9 @@
   static int32_t GetReadbackBufferFence(hwc2_device_t *device, hwc2_display_t display,
                                         int32_t *release_fence);
   static uint32_t GetMaxVirtualDisplayCount(hwc2_device_t *device);
+  static int32_t GetDisplayIdentificationData(hwc2_device_t *device, hwc2_display_t display,
+                                              uint8_t *outPort, uint32_t *outDataSize,
+                                              uint8_t *outData);
 
   // HWCDisplayEventHandler
   virtual void DisplayPowerReset();