hwc2: Add support for multiple displays
- Add support for an additional display which can be either a builtin
display or pluggable display.
- Restructure HWCDisplayPrimary as HWCBuiltIn and HWCDisplayExternal
as HWCDisplayPluggable.
- Refactor hotplug handling. Iterate through all pluggable displays,
and check present connection status. Connect/disconnect a given
display if connection status has changed.
CRs-Fixed: 2200594
Change-Id: Ibd2f93ddfbe72af58220691ad481469c8fdcbf86
diff --git a/sdm/libs/hwc2/hwc_display_builtin.cpp b/sdm/libs/hwc2/hwc_display_builtin.cpp
new file mode 100644
index 0000000..f841dbc
--- /dev/null
+++ b/sdm/libs/hwc2/hwc_display_builtin.cpp
@@ -0,0 +1,700 @@
+/*
+* Copyright (c) 2014-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
+* met:
+* * Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* * Redistributions in binary form must reproduce the above
+* copyright notice, this list of conditions and the following
+* disclaimer in the documentation and/or other materials provided
+* with the distribution.
+* * Neither the name of The Linux Foundation nor the names of its
+* contributors may be used to endorse or promote products derived
+* from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <cutils/properties.h>
+#include <sync/sync.h>
+#include <utils/constants.h>
+#include <utils/debug.h>
+#include <utils/utils.h>
+#include <stdarg.h>
+#include <sys/mman.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "hwc_display_builtin.h"
+#include "hwc_debugger.h"
+
+#define __CLASS__ "HWCDisplayBuiltIn"
+
+namespace sdm {
+
+int HWCDisplayBuiltIn::Create(CoreInterface *core_intf, BufferAllocator *buffer_allocator,
+ HWCCallbacks *callbacks, HWCDisplayEventHandler *event_handler,
+ qService::QService *qservice, hwc2_display_t id, int32_t sdm_id,
+ HWCDisplay **hwc_display) {
+ int status = 0;
+ uint32_t builtin_width = 0;
+ uint32_t builtin_height = 0;
+
+ HWCDisplay *hwc_display_builtin =
+ new HWCDisplayBuiltIn(core_intf, buffer_allocator, callbacks, event_handler, qservice, id,
+ sdm_id);
+ status = hwc_display_builtin->Init();
+ if (status) {
+ delete hwc_display_builtin;
+ return status;
+ }
+
+ hwc_display_builtin->GetMixerResolution(&builtin_width, &builtin_height);
+ int width = 0, height = 0;
+ HWCDebugHandler::Get()->GetProperty(FB_WIDTH_PROP, &width);
+ HWCDebugHandler::Get()->GetProperty(FB_HEIGHT_PROP, &height);
+ if (width > 0 && height > 0) {
+ builtin_width = UINT32(width);
+ builtin_height = UINT32(height);
+ }
+
+ status = hwc_display_builtin->SetFrameBufferResolution(builtin_width, builtin_height);
+ if (status) {
+ Destroy(hwc_display_builtin);
+ return status;
+ }
+
+ *hwc_display = hwc_display_builtin;
+
+ return status;
+}
+
+void HWCDisplayBuiltIn::Destroy(HWCDisplay *hwc_display) {
+ hwc_display->Deinit();
+ delete hwc_display;
+}
+
+HWCDisplayBuiltIn::HWCDisplayBuiltIn(CoreInterface *core_intf, BufferAllocator *buffer_allocator,
+ HWCCallbacks *callbacks, HWCDisplayEventHandler *event_handler,
+ qService::QService *qservice, hwc2_display_t id,
+ int32_t sdm_id)
+ : HWCDisplay(core_intf, buffer_allocator, callbacks, event_handler, qservice, kBuiltIn, id,
+ sdm_id, true, DISPLAY_CLASS_BUILTIN),
+ buffer_allocator_(buffer_allocator),
+ cpu_hint_(NULL) {
+}
+
+int HWCDisplayBuiltIn::Init() {
+ cpu_hint_ = new CPUHint();
+ if (cpu_hint_->Init(static_cast<HWCDebugHandler *>(HWCDebugHandler::Get())) != kErrorNone) {
+ delete cpu_hint_;
+ cpu_hint_ = NULL;
+ }
+
+ use_metadata_refresh_rate_ = true;
+ int disable_metadata_dynfps = 0;
+ HWCDebugHandler::Get()->GetProperty(DISABLE_METADATA_DYNAMIC_FPS_PROP, &disable_metadata_dynfps);
+ if (disable_metadata_dynfps) {
+ use_metadata_refresh_rate_ = false;
+ }
+
+ int status = HWCDisplay::Init();
+ if (status) {
+ return status;
+ }
+ color_mode_ = new HWCColorMode(display_intf_);
+ color_mode_->Init();
+ HWCDebugHandler::Get()->GetProperty(ENABLE_DEFAULT_COLOR_MODE,
+ &default_mode_status_);
+
+ return status;
+}
+
+void HWCDisplayBuiltIn::ProcessBootAnimCompleted() {
+ uint32_t numBootUpLayers = 0;
+ // TODO(user): Remove this hack
+
+ numBootUpLayers = static_cast<uint32_t>(Debug::GetBootAnimLayerCount());
+
+ if (numBootUpLayers == 0) {
+ numBootUpLayers = 2;
+ }
+ /* All other checks namely "init.svc.bootanim" or
+ * HWC_GEOMETRY_CHANGED fail in correctly identifying the
+ * exact bootup transition to homescreen
+ */
+ char property[PROPERTY_VALUE_MAX];
+ bool isEncrypted = false;
+ bool main_class_services_started = false;
+ property_get("ro.crypto.state", property, "unencrypted");
+ if (!strcmp(property, "encrypted")) {
+ property_get("ro.crypto.type", property, "block");
+ if (!strcmp(property, "block")) {
+ isEncrypted = true;
+ property_get("vold.decrypt", property, "");
+ if (!strcmp(property, "trigger_restart_framework")) {
+ main_class_services_started = true;
+ }
+ }
+ }
+
+ if ((!isEncrypted || (isEncrypted && main_class_services_started)) &&
+ (layer_set_.size() > numBootUpLayers)) {
+ DLOGI("Applying default mode");
+ boot_animation_completed_ = true;
+ // Applying default mode after bootanimation is finished And
+ // If Data is Encrypted, it is ready for access.
+ if (display_intf_) {
+ display_intf_->ApplyDefaultDisplayMode();
+ RestoreColorTransform();
+ }
+ }
+}
+
+HWC2::Error HWCDisplayBuiltIn::Validate(uint32_t *out_num_types, uint32_t *out_num_requests) {
+ auto status = HWC2::Error::None;
+ DisplayError error = kErrorNone;
+
+ if (default_mode_status_ && !boot_animation_completed_) {
+ ProcessBootAnimCompleted();
+ }
+
+ if (display_paused_) {
+ MarkLayersForGPUBypass();
+ return status;
+ }
+
+ if (color_tranform_failed_) {
+ // Must fall back to client composition
+ MarkLayersForClientComposition();
+ }
+
+ if (config_pending_) {
+ if (display_intf_->SetActiveConfig(display_config_) != kErrorNone) {
+ DLOGW("Invalid display config %d", display_config_);
+ // Reset the display config with active config
+ display_intf_->GetActiveConfig(&display_config_);
+ }
+ }
+ // Fill in the remaining blanks in the layers and add them to the SDM layerstack
+ BuildLayerStack();
+ // Checks and replaces layer stack for solid fill
+ SolidFillPrepare();
+
+ bool pending_output_dump = dump_frame_count_ && dump_output_to_file_;
+
+ 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. 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();
+ bool one_updating_layer = (num_updating_layers == 1);
+ if (num_updating_layers != 0) {
+ ToggleCPUHint(one_updating_layer);
+ }
+
+ uint32_t refresh_rate = GetOptimalRefreshRate(one_updating_layer);
+ bool final_rate = force_refresh_rate_ ? true : false;
+ error = display_intf_->SetRefreshRate(refresh_rate, final_rate);
+ if (error == kErrorNone) {
+ // On success, set current refresh rate to new refresh rate
+ current_refresh_rate_ = refresh_rate;
+ }
+
+ if (layer_set_.empty()) {
+ // Avoid flush for Command mode panel.
+ flush_ = !IsDisplayCommandMode();
+ validated_ = true;
+ return status;
+ }
+
+ status = PrepareLayerStack(out_num_types, out_num_requests);
+ return status;
+}
+
+HWC2::Error HWCDisplayBuiltIn::Present(int32_t *out_retire_fence) {
+ auto status = HWC2::Error::None;
+ if (display_paused_) {
+ DisplayError error = display_intf_->Flush();
+ validated_ = false;
+ if (error != kErrorNone) {
+ DLOGE("Flush failed. Error = %d", error);
+ }
+ } else {
+ status = HWCDisplay::CommitLayerStack();
+ if (status == HWC2::Error::None) {
+ HandleFrameOutput();
+ SolidFillCommit();
+ status = HWCDisplay::PostCommitLayerStack(out_retire_fence);
+ }
+ }
+
+ CloseFd(&output_buffer_.acquire_fence_fd);
+ return status;
+}
+
+HWC2::Error HWCDisplayBuiltIn::GetColorModes(uint32_t *out_num_modes, ColorMode *out_modes) {
+ if (out_modes == nullptr) {
+ *out_num_modes = color_mode_->GetColorModeCount();
+ } else {
+ color_mode_->GetColorModes(out_num_modes, out_modes);
+ }
+
+ return HWC2::Error::None;
+}
+
+HWC2::Error HWCDisplayBuiltIn::GetRenderIntents(ColorMode mode, uint32_t *out_num_intents,
+ RenderIntent *out_intents) {
+ if (out_intents == nullptr) {
+ *out_num_intents = color_mode_->GetRenderIntentCount(mode);
+ } else {
+ color_mode_->GetRenderIntents(mode, out_num_intents, out_intents);
+ }
+ return HWC2::Error::None;
+}
+
+HWC2::Error HWCDisplayBuiltIn::SetColorMode(ColorMode mode) {
+ return SetColorModeWithRenderIntent(mode, RenderIntent::COLORIMETRIC);
+}
+
+HWC2::Error HWCDisplayBuiltIn::SetColorModeWithRenderIntent(ColorMode mode, RenderIntent intent) {
+ auto status = color_mode_->SetColorModeWithRenderIntent(mode, intent);
+ if (status != HWC2::Error::None) {
+ DLOGE("failed for mode = %d intent = %d", mode, intent);
+ return status;
+ }
+ callbacks_->Refresh(HWC_DISPLAY_PRIMARY);
+ validated_ = false;
+ return status;
+}
+
+HWC2::Error HWCDisplayBuiltIn::SetColorModeById(int32_t color_mode_id) {
+ auto status = color_mode_->SetColorModeById(color_mode_id);
+ if (status != HWC2::Error::None) {
+ DLOGE("failed for mode = %d", color_mode_id);
+ return status;
+ }
+
+ callbacks_->Refresh(HWC_DISPLAY_PRIMARY);
+ validated_ = false;
+
+ return status;
+}
+
+HWC2::Error HWCDisplayBuiltIn::RestoreColorTransform() {
+ auto status = color_mode_->RestoreColorTransform();
+ if (status != HWC2::Error::None) {
+ DLOGE("failed to RestoreColorTransform");
+ return status;
+ }
+
+ callbacks_->Refresh(HWC_DISPLAY_PRIMARY);
+
+ return status;
+}
+
+HWC2::Error HWCDisplayBuiltIn::SetColorTransform(const float *matrix,
+ android_color_transform_t hint) {
+ if (!matrix) {
+ return HWC2::Error::BadParameter;
+ }
+
+ auto status = color_mode_->SetColorTransform(matrix, hint);
+ if (status != HWC2::Error::None) {
+ DLOGE("failed for hint = %d", hint);
+ color_tranform_failed_ = true;
+ return status;
+ }
+
+ callbacks_->Refresh(HWC_DISPLAY_PRIMARY);
+ color_tranform_failed_ = false;
+ validated_ = false;
+
+ return status;
+}
+
+HWC2::Error HWCDisplayBuiltIn::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 HWCDisplayBuiltIn::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 HWCDisplayBuiltIn::Perform(uint32_t operation, ...) {
+ va_list args;
+ va_start(args, operation);
+ int val = 0;
+ LayerSolidFill *solid_fill_color;
+ LayerRect *rect = NULL;
+
+ switch (operation) {
+ case SET_METADATA_DYN_REFRESH_RATE:
+ val = va_arg(args, int32_t);
+ SetMetaDataRefreshRateFlag(val);
+ break;
+ case SET_BINDER_DYN_REFRESH_RATE:
+ val = va_arg(args, int32_t);
+ ForceRefreshRate(UINT32(val));
+ break;
+ case SET_DISPLAY_MODE:
+ val = va_arg(args, int32_t);
+ SetDisplayMode(UINT32(val));
+ break;
+ case SET_QDCM_SOLID_FILL_INFO:
+ solid_fill_color = va_arg(args, LayerSolidFill*);
+ SetQDCMSolidFillInfo(true, *solid_fill_color);
+ break;
+ case UNSET_QDCM_SOLID_FILL_INFO:
+ solid_fill_color = va_arg(args, LayerSolidFill*);
+ SetQDCMSolidFillInfo(false, *solid_fill_color);
+ break;
+ case SET_QDCM_SOLID_FILL_RECT:
+ rect = va_arg(args, LayerRect*);
+ solid_fill_rect_ = *rect;
+ break;
+ default:
+ DLOGW("Invalid operation %d", operation);
+ va_end(args);
+ return -EINVAL;
+ }
+ va_end(args);
+ validated_ = false;
+
+ return 0;
+}
+
+DisplayError HWCDisplayBuiltIn::SetDisplayMode(uint32_t mode) {
+ DisplayError error = kErrorNone;
+
+ if (display_intf_) {
+ error = display_intf_->SetDisplayMode(mode);
+ }
+
+ return error;
+}
+
+void HWCDisplayBuiltIn::SetMetaDataRefreshRateFlag(bool enable) {
+ int disable_metadata_dynfps = 0;
+
+ HWCDebugHandler::Get()->GetProperty(DISABLE_METADATA_DYNAMIC_FPS_PROP, &disable_metadata_dynfps);
+ if (disable_metadata_dynfps) {
+ return;
+ }
+ use_metadata_refresh_rate_ = enable;
+}
+
+void HWCDisplayBuiltIn::SetQDCMSolidFillInfo(bool enable, const LayerSolidFill &color) {
+ solid_fill_enable_ = enable;
+ solid_fill_color_ = color;
+}
+
+void HWCDisplayBuiltIn::ToggleCPUHint(bool set) {
+ if (!cpu_hint_) {
+ return;
+ }
+
+ if (set) {
+ cpu_hint_->Set();
+ } else {
+ cpu_hint_->Reset();
+ }
+}
+
+int HWCDisplayBuiltIn::HandleSecureSession(const std::bitset<kSecureMax> &secure_sessions,
+ bool *power_on_pending) {
+ if (!power_on_pending) {
+ return -EINVAL;
+ }
+
+ if (active_secure_sessions_[kSecureDisplay] != secure_sessions[kSecureDisplay]) {
+ SecureEvent secure_event =
+ secure_sessions.test(kSecureDisplay) ? kSecureDisplayStart : kSecureDisplayEnd;
+ DisplayError err = display_intf_->HandleSecureEvent(secure_event);
+ if (err != kErrorNone) {
+ DLOGE("Set secure event failed");
+ return err;
+ }
+
+ DLOGI("SecureDisplay state changed from %d to %d for display %d",
+ active_secure_sessions_.test(kSecureDisplay), secure_sessions.test(kSecureDisplay),
+ type_);
+ }
+ active_secure_sessions_ = secure_sessions;
+ *power_on_pending = false;
+ return 0;
+}
+
+void HWCDisplayBuiltIn::ForceRefreshRate(uint32_t refresh_rate) {
+ if ((refresh_rate && (refresh_rate < min_refresh_rate_ || refresh_rate > max_refresh_rate_)) ||
+ force_refresh_rate_ == refresh_rate) {
+ // Cannot honor force refresh rate, as its beyond the range or new request is same
+ return;
+ }
+
+ force_refresh_rate_ = refresh_rate;
+
+ callbacks_->Refresh(HWC_DISPLAY_PRIMARY);
+
+ return;
+}
+
+uint32_t HWCDisplayBuiltIn::GetOptimalRefreshRate(bool one_updating_layer) {
+ if (force_refresh_rate_) {
+ return force_refresh_rate_;
+ } else if (use_metadata_refresh_rate_ && one_updating_layer && metadata_refresh_rate_) {
+ return metadata_refresh_rate_;
+ }
+
+ return max_refresh_rate_;
+}
+
+DisplayError HWCDisplayBuiltIn::Refresh() {
+ DisplayError error = kErrorNone;
+
+ callbacks_->Refresh(HWC_DISPLAY_PRIMARY);
+
+ return error;
+}
+
+void HWCDisplayBuiltIn::SetIdleTimeoutMs(uint32_t timeout_ms) {
+ display_intf_->SetIdleTimeoutMs(timeout_ms);
+ validated_ = false;
+}
+
+void HWCDisplayBuiltIn::HandleFrameOutput() {
+ if (readback_buffer_queued_) {
+ validated_ = false;
+ }
+
+ if (dump_output_to_file_) {
+ HandleFrameDump();
+ }
+}
+
+void HWCDisplayBuiltIn::HandleFrameDump() {
+ if (!readback_configured_) {
+ dump_frame_count_ = 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;
+ validated_ = false;
+ }
+ }
+
+ if (0 == dump_frame_count_) {
+ dump_output_to_file_ = false;
+ // Unmap and Free buffer
+ if (munmap(output_buffer_base_, output_buffer_info_.alloc_buffer_info.size) != 0) {
+ DLOGE("unmap failed with err %d", errno);
+ }
+ if (buffer_allocator_->FreeBuffer(&output_buffer_info_) != 0) {
+ DLOGE("FreeBuffer failed");
+ }
+
+ readback_buffer_queued_ = false;
+ post_processed_output_ = false;
+ readback_configured_ = false;
+
+ output_buffer_ = {};
+ output_buffer_info_ = {};
+ output_buffer_base_ = nullptr;
+ }
+}
+
+HWC2::Error HWCDisplayBuiltIn::SetFrameDumpConfig(uint32_t count, uint32_t bit_mask_layer_type) {
+ HWCDisplay::SetFrameDumpConfig(count, bit_mask_layer_type);
+ dump_output_to_file_ = bit_mask_layer_type & (1 << OUTPUT_LAYER_DUMP);
+ DLOGI("output_layer_dump_enable %d", dump_output_to_file_);
+
+ if (!count || !dump_output_to_file_) {
+ return HWC2::Error::None;
+ }
+
+ // Allocate and map output buffer
+ output_buffer_info_ = {};
+ // Since we dump DSPP output use Panel resolution.
+ GetPanelResolution(&output_buffer_info_.buffer_config.width,
+ &output_buffer_info_.buffer_config.height);
+ output_buffer_info_.buffer_config.format = kFormatRGB888;
+ output_buffer_info_.buffer_config.buffer_count = 1;
+ if (buffer_allocator_->AllocateBuffer(&output_buffer_info_) != 0) {
+ DLOGE("Buffer allocation failed");
+ output_buffer_info_ = {};
+ return HWC2::Error::NoResources;
+ }
+
+ void *buffer = mmap(NULL, output_buffer_info_.alloc_buffer_info.size, PROT_READ | PROT_WRITE,
+ MAP_SHARED, output_buffer_info_.alloc_buffer_info.fd, 0);
+
+ if (buffer == MAP_FAILED) {
+ DLOGE("mmap failed with err %d", errno);
+ buffer_allocator_->FreeBuffer(&output_buffer_info_);
+ output_buffer_info_ = {};
+ return HWC2::Error::NoResources;
+ }
+
+ output_buffer_base_ = buffer;
+ const native_handle_t *handle = static_cast<native_handle_t *>(output_buffer_info_.private_data);
+ SetReadbackBuffer(handle, -1, true);
+
+ return HWC2::Error::None;
+}
+
+int HWCDisplayBuiltIn::FrameCaptureAsync(const BufferInfo &output_buffer_info,
+ bool post_processed_output) {
+ // Note: This function is called in context of a binder thread and a lock is already held
+ if (output_buffer_info.alloc_buffer_info.fd < 0) {
+ DLOGE("Invalid fd %d", output_buffer_info.alloc_buffer_info.fd);
+ return -1;
+ }
+
+ auto panel_width = 0u;
+ auto panel_height = 0u;
+ auto fb_width = 0u;
+ auto fb_height = 0u;
+
+ GetPanelResolution(&panel_width, &panel_height);
+ GetFrameBufferResolution(&fb_width, &fb_height);
+
+ if (post_processed_output && (output_buffer_info.buffer_config.width < panel_width ||
+ output_buffer_info.buffer_config.height < panel_height)) {
+ DLOGE("Buffer dimensions should not be less than panel resolution");
+ return -1;
+ } else if (!post_processed_output && (output_buffer_info.buffer_config.width < fb_width ||
+ output_buffer_info.buffer_config.height < fb_height)) {
+ DLOGE("Buffer dimensions should not be less than FB resolution");
+ return -1;
+ }
+
+ const native_handle_t *buffer = static_cast<native_handle_t *>(output_buffer_info.private_data);
+ SetReadbackBuffer(buffer, -1, post_processed_output);
+
+ return 0;
+}
+
+bool HWCDisplayBuiltIn::GetFrameCaptureFence(int32_t *release_fence) {
+ return (GetReadbackBufferFence(release_fence) == HWC2::Error::None);
+}
+
+DisplayError HWCDisplayBuiltIn::SetDetailEnhancerConfig
+ (const DisplayDetailEnhancerData &de_data) {
+ DisplayError error = kErrorNotSupported;
+
+ if (display_intf_) {
+ error = display_intf_->SetDetailEnhancerData(de_data);
+ validated_ = false;
+ }
+ return error;
+}
+
+DisplayError HWCDisplayBuiltIn::ControlPartialUpdate(bool enable, uint32_t *pending) {
+ DisplayError error = kErrorNone;
+
+ if (display_intf_) {
+ error = display_intf_->ControlPartialUpdate(enable, pending);
+ validated_ = false;
+ }
+
+ return error;
+}
+
+DisplayError HWCDisplayBuiltIn::DisablePartialUpdateOneFrame() {
+ DisplayError error = kErrorNone;
+
+ if (display_intf_) {
+ error = display_intf_->DisablePartialUpdateOneFrame();
+ validated_ = false;
+ }
+
+ return error;
+}
+
+
+DisplayError HWCDisplayBuiltIn::SetMixerResolution(uint32_t width, uint32_t height) {
+ DisplayError error = display_intf_->SetMixerResolution(width, height);
+ validated_ = false;
+ return error;
+}
+
+DisplayError HWCDisplayBuiltIn::GetMixerResolution(uint32_t *width, uint32_t *height) {
+ return display_intf_->GetMixerResolution(width, height);
+}
+
+HWC2::Error HWCDisplayBuiltIn::SetQSyncMode(QSyncMode qsync_mode) {
+ auto err = display_intf_->SetQSyncMode(qsync_mode);
+ if (err != kErrorNone) {
+ return HWC2::Error::Unsupported;
+ }
+
+ return HWC2::Error::None;
+}
+
+} // namespace sdm