sdm: hwc2: Add support for HWC Readback feature

Add support for HWC Readback feature in HWC2 and SDM.

CRs-Fixed: 2217822
Change-Id: If2df64dc0bdea7a9ca4c2b0498f1cd3f3dc9de9d
diff --git a/sdm/libs/core/drm/hw_device_drm.cpp b/sdm/libs/core/drm/hw_device_drm.cpp
index 879d199..206197a 100644
--- a/sdm/libs/core/drm/hw_device_drm.cpp
+++ b/sdm/libs/core/drm/hw_device_drm.cpp
@@ -1780,4 +1780,16 @@
   return kErrorNone;
 }
 
+void HWDeviceDRM::DumpConnectorModeInfo() {
+  for (uint32_t i = 0; i < (uint32_t)connector_info_.modes.size(); i++) {
+    DLOGI("Mode[%d] Name:%s vref:%d hdisp:%d hsync_s:%d hsync_e:%d htotal:%d " \
+          "vdisp:%d vsync_s:%d vsync_e:%d vtotal:%d\n", i, connector_info_.modes[i].mode.name,
+          connector_info_.modes[i].mode.vrefresh, connector_info_.modes[i].mode.hdisplay,
+          connector_info_.modes[i].mode.hsync_start, connector_info_.modes[i].mode.hsync_end,
+          connector_info_.modes[i].mode.htotal, connector_info_.modes[i].mode.vdisplay,
+          connector_info_.modes[i].mode.vsync_start, connector_info_.modes[i].mode.vsync_end,
+          connector_info_.modes[i].mode.vtotal);
+  }
+}
+
 }  // namespace sdm
diff --git a/sdm/libs/core/drm/hw_device_drm.h b/sdm/libs/core/drm/hw_device_drm.h
index 4958f75..f684394 100644
--- a/sdm/libs/core/drm/hw_device_drm.h
+++ b/sdm/libs/core/drm/hw_device_drm.h
@@ -150,6 +150,7 @@
   void SetSsppLutFeatures(HWPipeInfo *pipe_info);
   void AddDimLayerIfNeeded();
   DisplayError NullCommit(bool synchronous, bool retain_planes);
+  void DumpConnectorModeInfo();
 
   class Registry {
    public:
diff --git a/sdm/libs/core/drm/hw_info_drm.cpp b/sdm/libs/core/drm/hw_info_drm.cpp
index 2ba7e92..1399edc 100644
--- a/sdm/libs/core/drm/hw_info_drm.cpp
+++ b/sdm/libs/core/drm/hw_info_drm.cpp
@@ -483,6 +483,7 @@
 
   hw_resource->supported_formats_map.erase(sub_blk_type);
   hw_resource->supported_formats_map.insert(make_pair(sub_blk_type, supported_sdm_formats));
+  hw_resource->has_concurrent_writeback = connector_info.concurrent_writeback;
 
   drm_mgr_intf_->UnregisterDisplay(token);
 }
diff --git a/sdm/libs/core/drm/hw_peripheral_drm.cpp b/sdm/libs/core/drm/hw_peripheral_drm.cpp
index 26ea97f..df1149c 100644
--- a/sdm/libs/core/drm/hw_peripheral_drm.cpp
+++ b/sdm/libs/core/drm/hw_peripheral_drm.cpp
@@ -28,6 +28,7 @@
 */
 
 #include <utils/debug.h>
+#include <vector>
 
 #include "hw_peripheral_drm.h"
 
@@ -37,6 +38,8 @@
 using sde_drm::DRMOps;
 using sde_drm::DRMPowerMode;
 using sde_drm::DRMDppsFeatureInfo;
+using sde_drm::DRMSecureMode;
+using sde_drm::DRMCWbCaptureMode;
 
 namespace sdm {
 
@@ -63,6 +66,7 @@
 DisplayError HWPeripheralDRM::Validate(HWLayers *hw_layers) {
   HWLayersInfo &hw_layer_info = hw_layers->info;
   SetDestScalarData(hw_layer_info);
+  SetupConcurrentWriteback(hw_layer_info, true);
 
   return HWDeviceDRM::Validate(hw_layers);
 }
@@ -70,8 +74,15 @@
 DisplayError HWPeripheralDRM::Commit(HWLayers *hw_layers) {
   HWLayersInfo &hw_layer_info = hw_layers->info;
   SetDestScalarData(hw_layer_info);
+  SetupConcurrentWriteback(hw_layer_info, false);
 
-  return HWDeviceDRM::Commit(hw_layers);
+  DisplayError error = HWDeviceDRM::Commit(hw_layers);
+
+  if (cwb_config_.enabled && (error == kErrorNone)) {
+    PostCommitConcurrentWriteback(hw_layer_info.stack->output_buffer);
+  }
+
+  return error;
 }
 
 void HWPeripheralDRM::ResetDisplayParams() {
@@ -187,4 +198,103 @@
   return kErrorNone;
 }
 
+void HWPeripheralDRM::SetupConcurrentWriteback(const HWLayersInfo &hw_layer_info, bool validate) {
+  bool enable = hw_resource_.has_concurrent_writeback && hw_layer_info.stack->output_buffer;
+  if (!(enable || cwb_config_.enabled)) {
+    return;
+  }
+
+  bool setup_modes = enable && !cwb_config_.enabled && validate;
+  if (setup_modes && (SetupConcurrentWritebackModes() == kErrorNone)) {
+    cwb_config_.enabled = true;
+  }
+
+  if (cwb_config_.enabled) {
+    if (enable) {
+      // Set DRM properties for Concurrent Writeback.
+      ConfigureConcurrentWriteback(hw_layer_info.stack);
+    } else {
+      // Tear down the Concurrent Writeback topology.
+      drm_atomic_intf_->Perform(DRMOps::CONNECTOR_SET_CRTC, cwb_config_.token.conn_id, 0);
+    }
+  }
+}
+
+DisplayError HWPeripheralDRM::SetupConcurrentWritebackModes() {
+  // To setup Concurrent Writeback topology, get the Connector ID of Virtual display
+  if (drm_mgr_intf_->RegisterDisplay(DRMDisplayType::VIRTUAL, &cwb_config_.token)) {
+    DLOGE("RegisterDisplay failed for Concurrent Writeback");
+    return kErrorResources;
+  }
+
+  // Set the modes based on Primary display.
+  std::vector<drmModeModeInfo> modes;
+  for (auto &item : connector_info_.modes) {
+    modes.push_back(item.mode);
+  }
+
+  // Inform the mode list to driver.
+  struct sde_drm_wb_cfg cwb_cfg = {};
+  cwb_cfg.connector_id = cwb_config_.token.conn_id;
+  cwb_cfg.flags = SDE_DRM_WB_CFG_FLAGS_CONNECTED;
+  cwb_cfg.count_modes = UINT32(modes.size());
+  cwb_cfg.modes = (uint64_t)modes.data();
+
+  int ret = -EINVAL;
+#ifdef DRM_IOCTL_SDE_WB_CONFIG
+  ret = drmIoctl(dev_fd_, DRM_IOCTL_SDE_WB_CONFIG, &cwb_cfg);
+#endif
+  if (ret) {
+    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;
+  }
+
+  return kErrorNone;
+}
+
+void HWPeripheralDRM::ConfigureConcurrentWriteback(LayerStack *layer_stack) {
+  LayerBuffer *output_buffer = layer_stack->output_buffer;
+  registry_.MapBufferToFbId(output_buffer);
+
+  // Set the topology for Concurrent Writeback: [CRTC_PRIMARY_DISPLAY - CONNECTOR_VIRTUAL_DISPLAY].
+  drm_atomic_intf_->Perform(DRMOps::CONNECTOR_SET_CRTC, cwb_config_.token.conn_id, token_.crtc_id);
+
+  // Set CRTC Capture Mode
+  DRMCWbCaptureMode capture_mode = layer_stack->flags.post_processed_output ?
+                                   DRMCWbCaptureMode::DSPP_OUT : DRMCWbCaptureMode::MIXER_OUT;
+  drm_atomic_intf_->Perform(DRMOps::CRTC_SET_CAPTURE_MODE, token_.crtc_id, capture_mode);
+
+  // Set Connector Output FB
+  uint32_t fb_id = registry_.GetFbId(output_buffer->planes[0].fd);
+  drm_atomic_intf_->Perform(DRMOps::CONNECTOR_SET_OUTPUT_FB_ID, cwb_config_.token.conn_id, fb_id);
+
+  // Set Connector Secure Mode
+  bool secure = output_buffer->flags.secure;
+  DRMSecureMode mode = secure ? DRMSecureMode::SECURE : DRMSecureMode::NON_SECURE;
+  drm_atomic_intf_->Perform(DRMOps::CONNECTOR_SET_FB_SECURE_MODE, cwb_config_.token.conn_id, mode);
+
+  // Set Connector Output Rect
+  sde_drm::DRMRect dst = {};
+  dst.left = 0;
+  dst.top = 0;
+  dst.right = display_attributes_[current_mode_index_].x_pixels;
+  dst.bottom = display_attributes_[current_mode_index_].y_pixels;
+  drm_atomic_intf_->Perform(DRMOps::CONNECTOR_SET_OUTPUT_RECT, cwb_config_.token.conn_id, dst);
+}
+
+void HWPeripheralDRM::PostCommitConcurrentWriteback(LayerBuffer *output_buffer) {
+  bool enabled = hw_resource_.has_concurrent_writeback && output_buffer;
+
+  if (enabled) {
+    // Get Concurrent Writeback fence
+    int *fence = &output_buffer->release_fence_fd;
+    drm_atomic_intf_->Perform(DRMOps::CONNECTOR_GET_RETIRE_FENCE, cwb_config_.token.conn_id, fence);
+  } else {
+    drm_mgr_intf_->UnregisterDisplay(cwb_config_.token);
+    cwb_config_.enabled = false;
+  }
+}
+
 }  // namespace sdm
diff --git a/sdm/libs/core/drm/hw_peripheral_drm.h b/sdm/libs/core/drm/hw_peripheral_drm.h
index fefe9d1..f93d405 100644
--- a/sdm/libs/core/drm/hw_peripheral_drm.h
+++ b/sdm/libs/core/drm/hw_peripheral_drm.h
@@ -35,6 +35,11 @@
 
 namespace sdm {
 
+struct CWBConfig {
+  bool enabled = false;
+  sde_drm::DRMDisplayToken token = {};
+};
+
 class HWPeripheralDRM : public HWDeviceDRM {
  public:
   explicit HWPeripheralDRM(BufferSyncHandler *buffer_sync_handler,
@@ -55,8 +60,14 @@
  private:
   void SetDestScalarData(HWLayersInfo hw_layer_info);
   void ResetDisplayParams();
+  DisplayError SetupConcurrentWritebackModes();
+  void SetupConcurrentWriteback(const HWLayersInfo &hw_layer_info, bool validate);
+  void ConfigureConcurrentWriteback(LayerStack *stack);
+  void PostCommitConcurrentWriteback(LayerBuffer *output_buffer);
+
   sde_drm_dest_scaler_data sde_dest_scalar_data_ = {};
   std::vector<SDEScaler> scalar_data_ = {};
+  CWBConfig cwb_config_ = {};
 };
 
 }  // namespace sdm
diff --git a/sdm/libs/core/drm/hw_virtual_drm.cpp b/sdm/libs/core/drm/hw_virtual_drm.cpp
index bf4dd6e..5013181 100644
--- a/sdm/libs/core/drm/hw_virtual_drm.cpp
+++ b/sdm/libs/core/drm/hw_virtual_drm.cpp
@@ -121,18 +121,6 @@
   return kErrorNone;
 }
 
-void HWVirtualDRM::DumpConnectorModeInfo() {
-  for (uint32_t i = 0; i < (uint32_t)connector_info_.modes.size(); i++) {
-    DLOGI("Mode[%d] Name:%s vref:%d hdisp:%d hsync_s:%d hsync_e:%d htotal:%d " \
-          "vdisp:%d vsync_s:%d vsync_e:%d vtotal:%d\n", i, connector_info_.modes[i].mode.name,
-          connector_info_.modes[i].mode.vrefresh, connector_info_.modes[i].mode.hdisplay,
-          connector_info_.modes[i].mode.hsync_start, connector_info_.modes[i].mode.hsync_end,
-          connector_info_.modes[i].mode.htotal, connector_info_.modes[i].mode.vdisplay,
-          connector_info_.modes[i].mode.vsync_start, connector_info_.modes[i].mode.vsync_end,
-          connector_info_.modes[i].mode.vtotal);
-  }
-}
-
 DisplayError HWVirtualDRM::Commit(HWLayers *hw_layers) {
   LayerBuffer *output_buffer = hw_layers->info.stack->output_buffer;
   DisplayError err = kErrorNone;
diff --git a/sdm/libs/core/drm/hw_virtual_drm.h b/sdm/libs/core/drm/hw_virtual_drm.h
index 257e08a..d89737e 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, The Linux Foundation. All rights reserved.
+Copyright (c) 2017-2018, 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
@@ -58,7 +58,6 @@
   void ConfigureWbConnectorDestRect();
   void ConfigureWbConnectorSecureMode(bool secure);
   void InitializeConfigs();
-  void DumpConnectorModeInfo();
   DisplayError SetWbConfigs(const HWDisplayAttributes &display_attributes);
   void GetModeIndex(const HWDisplayAttributes &display_attributes, int *mode_index);
 };
diff --git a/sdm/libs/hwc2/hwc_color_manager.cpp b/sdm/libs/hwc2/hwc_color_manager.cpp
index 8023b6a..1b4e15a 100644
--- a/sdm/libs/hwc2/hwc_color_manager.cpp
+++ b/sdm/libs/hwc2/hwc_color_manager.cpp
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2015 - 2017, The Linux Foundation. All rights reserved.
+* Copyright (c) 2015 - 2018, 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
@@ -30,6 +30,7 @@
 #include <dlfcn.h>
 #include <cutils/sockets.h>
 #include <cutils/native_handle.h>
+#include <sync/sync.h>
 #include <utils/String16.h>
 #include <binder/Parcel.h>
 #include <gralloc_priv.h>
@@ -259,7 +260,13 @@
       }
     }
   } else {
-    ret = hwc_display->GetFrameCaptureStatus();
+    ret = -EAGAIN;
+    int fence_fd = -1;
+    if (hwc_display->GetFrameCaptureFence(&fence_fd) && (fence_fd >= 0)) {
+      ret = sync_wait(fence_fd, 1000);
+      ::close(fence_fd);
+    }
+
     if (!ret) {
       if (frame_capture_data->buffer != NULL) {
         if (munmap(frame_capture_data->buffer, buffer_info.alloc_buffer_info.size) != 0) {
@@ -274,7 +281,7 @@
         }
       }
     } else {
-      DLOGE("GetFrameCaptureStatus failed. ret = %d", ret);
+      DLOGE("GetFrameCaptureFence failed. ret = %d", ret);
     }
   }
   return ret;
diff --git a/sdm/libs/hwc2/hwc_display.h b/sdm/libs/hwc2/hwc_display.h
index c973d19..0bb221c 100644
--- a/sdm/libs/hwc2/hwc_display.h
+++ b/sdm/libs/hwc2/hwc_display.h
@@ -148,15 +148,21 @@
   virtual int FrameCaptureAsync(const BufferInfo &output_buffer_info, bool post_processed) {
     return -1;
   }
-  // Returns the status of frame capture operation requested with FrameCaptureAsync().
-  // -EAGAIN : No status obtain yet, call API again after another frame.
-  // < 0 : Operation happened but failed.
-  // 0 : Success.
-  virtual int GetFrameCaptureStatus() { return -EAGAIN; }
+  // Client gets release fence of the frame capture, requested with FrameCaptureAsync().
+  // True : Frame capture configured and client gets release fence.
+  // False: Frame capture is not valid and has not been configured.
+  virtual bool GetFrameCaptureFence(int32_t *release_fence) { return false; }
 
   virtual DisplayError SetDetailEnhancerConfig(const DisplayDetailEnhancerData &de_data) {
     return kErrorNotSupported;
   }
+  virtual HWC2::Error SetReadbackBuffer(const native_handle_t *buffer, int32_t acquire_fence,
+                                        bool post_processed_output) {
+    return HWC2::Error::Unsupported;
+  }
+  virtual HWC2::Error GetReadbackBufferFence(int32_t *release_fence) {
+    return HWC2::Error::Unsupported;
+  }
 
   // Display Configurations
   virtual int SetActiveDisplayConfig(uint32_t config);
diff --git a/sdm/libs/hwc2/hwc_display_primary.cpp b/sdm/libs/hwc2/hwc_display_primary.cpp
index c68081a..4751c98 100644
--- a/sdm/libs/hwc2/hwc_display_primary.cpp
+++ b/sdm/libs/hwc2/hwc_display_primary.cpp
@@ -31,6 +31,7 @@
 #include <sync/sync.h>
 #include <utils/constants.h>
 #include <utils/debug.h>
+#include <utils/utils.h>
 #include <stdarg.h>
 #include <sys/mman.h>
 
@@ -185,11 +186,15 @@
 
   bool pending_output_dump = dump_frame_count_ && dump_output_to_file_;
 
-  if (frame_capture_buffer_queued_ || pending_output_dump) {
+  if (readback_buffer_queued_ || pending_output_dump) {
+    CloseFd(&output_buffer_.release_fence_fd);
     // RHS values were set in FrameCaptureAsync() called from a binder thread. They are picked up
-    // here in a subsequent draw round.
-    layer_stack_.output_buffer = &output_buffer_;
-    layer_stack_.flags.post_processed_output = post_processed_output_;
+    // here in a subsequent draw round. Readback is not allowed for any secure use case.
+    readback_configured_ = !layer_stack_.flags.secure_present;
+    if (readback_configured_) {
+      layer_stack_.output_buffer = &output_buffer_;
+      layer_stack_.flags.post_processed_output = post_processed_output_;
+    }
   }
 
   uint32_t num_updating_layers = GetUpdatingLayersCount();
@@ -234,6 +239,7 @@
     }
   }
 
+  CloseFd(&output_buffer_.acquire_fence_fd);
   return status;
 }
 
@@ -306,6 +312,52 @@
   return status;
 }
 
+HWC2::Error HWCDisplayPrimary::SetReadbackBuffer(const native_handle_t *buffer,
+                                                 int32_t acquire_fence,
+                                                 bool post_processed_output) {
+  const private_handle_t *handle = reinterpret_cast<const private_handle_t *>(buffer);
+  if (!handle || (handle->fd < 0)) {
+    return HWC2::Error::BadParameter;
+  }
+
+  // Configure the output buffer as Readback buffer
+  output_buffer_.width = UINT32(handle->width);
+  output_buffer_.height = UINT32(handle->height);
+  output_buffer_.unaligned_width = UINT32(handle->unaligned_width);
+  output_buffer_.unaligned_height = UINT32(handle->unaligned_height);
+  output_buffer_.format = HWCLayer::GetSDMFormat(handle->format, handle->flags);
+  output_buffer_.planes[0].fd = handle->fd;
+  output_buffer_.planes[0].stride = UINT32(handle->width);
+  output_buffer_.acquire_fence_fd = dup(acquire_fence);
+  output_buffer_.release_fence_fd = -1;
+
+  post_processed_output_ = post_processed_output;
+  readback_buffer_queued_ = true;
+  readback_configured_ = false;
+  validated_ = false;
+
+  DisablePartialUpdateOneFrame();
+  return HWC2::Error::None;
+}
+
+HWC2::Error HWCDisplayPrimary::GetReadbackBufferFence(int32_t *release_fence) {
+  auto status = HWC2::Error::None;
+
+  if (readback_configured_ && (output_buffer_.release_fence_fd >= 0)) {
+    *release_fence = output_buffer_.release_fence_fd;
+  } else {
+    status = HWC2::Error::Unsupported;
+    *release_fence = -1;
+  }
+
+  post_processed_output_ = false;
+  readback_buffer_queued_ = false;
+  readback_configured_ = false;
+  output_buffer_ = {};
+
+  return status;
+}
+
 int HWCDisplayPrimary::Perform(uint32_t operation, ...) {
   va_list args;
   va_start(args, operation);
@@ -447,52 +499,30 @@
   validated_ = false;
 }
 
-static void SetLayerBuffer(const BufferInfo &output_buffer_info, LayerBuffer *output_buffer) {
-  const BufferConfig& buffer_config = output_buffer_info.buffer_config;
-  const AllocatedBufferInfo &alloc_buffer_info = output_buffer_info.alloc_buffer_info;
-
-  output_buffer->width = alloc_buffer_info.aligned_width;
-  output_buffer->height = alloc_buffer_info.aligned_height;
-  output_buffer->unaligned_width = buffer_config.width;
-  output_buffer->unaligned_height = buffer_config.height;
-  output_buffer->format = buffer_config.format;
-  output_buffer->planes[0].fd = alloc_buffer_info.fd;
-  output_buffer->planes[0].stride = alloc_buffer_info.stride;
-}
-
 void HWCDisplayPrimary::HandleFrameOutput() {
-  if (frame_capture_buffer_queued_) {
-    HandleFrameCapture();
-  } else if (dump_output_to_file_) {
+  if (readback_buffer_queued_) {
+    validated_ = false;
+  }
+
+  if (dump_output_to_file_) {
     HandleFrameDump();
   }
 }
 
-void HWCDisplayPrimary::HandleFrameCapture() {
-  if (output_buffer_.release_fence_fd >= 0) {
-    frame_capture_status_ = sync_wait(output_buffer_.release_fence_fd, 1000);
-    ::close(output_buffer_.release_fence_fd);
-    output_buffer_.release_fence_fd = -1;
+void HWCDisplayPrimary::HandleFrameDump() {
+  if (!readback_configured_) {
+    dump_frame_count_ = 0;
   }
 
-  frame_capture_buffer_queued_ = false;
-  post_processed_output_ = false;
-  output_buffer_ = {};
-}
-
-void HWCDisplayPrimary::HandleFrameDump() {
-  if (dump_frame_count_) {
-    int ret = 0;
-    if (output_buffer_.release_fence_fd >= 0) {
-      ret = sync_wait(output_buffer_.release_fence_fd, 1000);
-      ::close(output_buffer_.release_fence_fd);
-      output_buffer_.release_fence_fd = -1;
-      if (ret < 0) {
-        DLOGE("sync_wait error errno = %d, desc = %s", errno, strerror(errno));
-      }
-    }
-    if (ret >= 0) {
+  if (dump_frame_count_ && output_buffer_.release_fence_fd >= 0) {
+    int ret = sync_wait(output_buffer_.release_fence_fd, 1000);
+    ::close(output_buffer_.release_fence_fd);
+    output_buffer_.release_fence_fd = -1;
+    if (ret < 0) {
+      DLOGE("sync_wait error errno = %d, desc = %s", errno, strerror(errno));
+    } else {
       DumpOutputBuffer(output_buffer_info_, output_buffer_base_, layer_stack_.retire_fence_fd);
+      readback_buffer_queued_ = false;
     }
   }
 
@@ -506,7 +536,10 @@
       DLOGE("FreeBuffer failed");
     }
 
+    readback_buffer_queued_ = false;
     post_processed_output_ = false;
+    readback_configured_ = false;
+
     output_buffer_ = {};
     output_buffer_info_ = {};
     output_buffer_base_ = nullptr;
@@ -546,9 +579,9 @@
   }
 
   output_buffer_base_ = buffer;
-  post_processed_output_ = true;
-  DisablePartialUpdateOneFrame();
-  validated_ = false;
+  const native_handle_t *handle = static_cast<native_handle_t *>(output_buffer_info_.private_data);
+  SetReadbackBuffer(handle, -1, true);
+
   return HWC2::Error::None;
 }
 
@@ -578,16 +611,16 @@
     return -1;
   }
 
-  SetLayerBuffer(output_buffer_info, &output_buffer_);
-  post_processed_output_ = post_processed_output;
-  frame_capture_buffer_queued_ = true;
-  // Status is only cleared on a new call to dump and remains valid otherwise
-  frame_capture_status_ = -EAGAIN;
-  DisablePartialUpdateOneFrame();
+  const native_handle_t *buffer = static_cast<native_handle_t *>(output_buffer_info.private_data);
+  SetReadbackBuffer(buffer, -1, post_processed_output);
 
   return 0;
 }
 
+bool HWCDisplayPrimary::GetFrameCaptureFence(int32_t *release_fence) {
+  return (GetReadbackBufferFence(release_fence) == HWC2::Error::None);
+}
+
 DisplayError HWCDisplayPrimary::SetDetailEnhancerConfig
                                    (const DisplayDetailEnhancerData &de_data) {
   DisplayError error = kErrorNotSupported;
diff --git a/sdm/libs/hwc2/hwc_display_primary.h b/sdm/libs/hwc2/hwc_display_primary.h
index 903a7c0..cedf859 100644
--- a/sdm/libs/hwc2/hwc_display_primary.h
+++ b/sdm/libs/hwc2/hwc_display_primary.h
@@ -67,9 +67,12 @@
   virtual void SetIdleTimeoutMs(uint32_t timeout_ms);
   virtual HWC2::Error SetFrameDumpConfig(uint32_t count, uint32_t bit_mask_layer_type);
   virtual int FrameCaptureAsync(const BufferInfo &output_buffer_info, bool post_processed);
-  virtual int GetFrameCaptureStatus() { return frame_capture_status_; }
+  virtual bool GetFrameCaptureFence(int32_t *release_fence);
   virtual DisplayError SetDetailEnhancerConfig(const DisplayDetailEnhancerData &de_data);
   virtual DisplayError ControlPartialUpdate(bool enable, uint32_t *pending);
+  virtual HWC2::Error SetReadbackBuffer(const native_handle_t *buffer, int32_t acquire_fence,
+                                        bool post_processed_output);
+  virtual HWC2::Error GetReadbackBufferFence(int32_t *release_fence);
 
  private:
   HWCDisplayPrimary(CoreInterface *core_intf, BufferAllocator *buffer_allocator,
@@ -84,7 +87,6 @@
   void ForceRefreshRate(uint32_t refresh_rate);
   uint32_t GetOptimalRefreshRate(bool one_updating_layer);
   void HandleFrameOutput();
-  void HandleFrameCapture();
   void HandleFrameDump();
   DisplayError SetMixerResolution(uint32_t width, uint32_t height);
   DisplayError GetMixerResolution(uint32_t *width, uint32_t *height);
@@ -92,13 +94,11 @@
   BufferAllocator *buffer_allocator_ = nullptr;
   CPUHint *cpu_hint_ = nullptr;
 
-  // Primary output buffer configuration
+  // Primary readback buffer configuration
   LayerBuffer output_buffer_ = {};
   bool post_processed_output_ = false;
-
-  // Members for 1 frame capture in a client provided buffer
-  bool frame_capture_buffer_queued_ = false;
-  int frame_capture_status_ = -EAGAIN;
+  bool readback_buffer_queued_ = false;
+  bool readback_configured_ = false;
 
   // Members for N frame output dump to file
   bool dump_output_to_file_ = false;
diff --git a/sdm/libs/hwc2/hwc_session.cpp b/sdm/libs/hwc2/hwc_session.cpp
index eab73d4..6b04669 100644
--- a/sdm/libs/hwc2/hwc_session.cpp
+++ b/sdm/libs/hwc2/hwc_session.cpp
@@ -886,6 +886,12 @@
       return AsFP<HWC2_PFN_SET_VSYNC_ENABLED>(SetVsyncEnabled);
     case HWC2::FunctionDescriptor::ValidateDisplay:
       return AsFP<HWC2_PFN_VALIDATE_DISPLAY>(HWCSession::ValidateDisplay);
+    case HWC2::FunctionDescriptor::SetReadbackBuffer:
+      return AsFP<HWC2_PFN_SET_READBACK_BUFFER>(HWCSession::SetReadbackBuffer);
+    case HWC2::FunctionDescriptor::GetReadbackBufferAttributes:
+      return AsFP<HWC2_PFN_GET_READBACK_BUFFER_ATTRIBUTES>(HWCSession::GetReadbackBufferAttributes);
+    case HWC2::FunctionDescriptor::GetReadbackBufferFence:
+      return AsFP<HWC2_PFN_GET_READBACK_BUFFER_FENCE>(HWCSession::GetReadbackBufferFence);
     default:
       DLOGD("Unknown/Unimplemented function descriptor: %d (%s)", int_descriptor,
             to_string(descriptor).c_str());
@@ -2090,4 +2096,47 @@
   }
 }
 
+int32_t HWCSession::GetReadbackBufferAttributes(hwc2_device_t *device, hwc2_display_t display,
+                                                int32_t *format, int32_t *dataspace) {
+  if (!device || !format || !dataspace) {
+    return HWC2_ERROR_BAD_PARAMETER;
+  }
+
+  if (display != HWC_DISPLAY_PRIMARY) {
+    return HWC2_ERROR_BAD_DISPLAY;
+  }
+
+  *format = HAL_PIXEL_FORMAT_RGB_888;
+  *dataspace = HAL_DATASPACE_V0_SRGB;  // ((STANDARD_BT709 | TRANSFER_SRGB) | RANGE_FULL)
+
+  return HWC2_ERROR_NONE;
+}
+
+int32_t HWCSession::SetReadbackBuffer(hwc2_device_t *device, hwc2_display_t display,
+                                      const native_handle_t *buffer, int32_t acquire_fence) {
+  if (!buffer) {
+    return HWC2_ERROR_BAD_PARAMETER;
+  }
+
+  if (display != HWC_DISPLAY_PRIMARY) {
+    return HWC2_ERROR_BAD_DISPLAY;
+  }
+
+  return CallDisplayFunction(device, display, &HWCDisplay::SetReadbackBuffer,
+                             buffer, acquire_fence, false);
+}
+
+int32_t HWCSession::GetReadbackBufferFence(hwc2_device_t *device, hwc2_display_t display,
+                                           int32_t *release_fence) {
+  if (!release_fence) {
+    return HWC2_ERROR_BAD_PARAMETER;
+  }
+
+  if (display != HWC_DISPLAY_PRIMARY) {
+    return HWC2_ERROR_BAD_DISPLAY;
+  }
+
+  return CallDisplayFunction(device, display, &HWCDisplay::GetReadbackBufferFence, release_fence);
+}
+
 }  // namespace sdm
diff --git a/sdm/libs/hwc2/hwc_session.h b/sdm/libs/hwc2/hwc_session.h
index 06ce5a2..cd962f4 100644
--- a/sdm/libs/hwc2/hwc_session.h
+++ b/sdm/libs/hwc2/hwc_session.h
@@ -164,6 +164,12 @@
                               int32_t /*android_color_mode_t*/ int_mode);
   static int32_t SetColorTransform(hwc2_device_t *device, hwc2_display_t display,
                                    const float *matrix, int32_t /*android_color_transform_t*/ hint);
+  static int32_t GetReadbackBufferAttributes(hwc2_device_t *device, hwc2_display_t display,
+                                             int32_t *format, int32_t *dataspace);
+  static int32_t SetReadbackBuffer(hwc2_device_t *device, hwc2_display_t display,
+                                   const native_handle_t *buffer, int32_t acquire_fence);
+  static int32_t GetReadbackBufferFence(hwc2_device_t *device, hwc2_display_t display,
+                                        int32_t *release_fence);
 
   // HWCDisplayEventHandler
   virtual void DisplayPowerReset();