| /* |
| * 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 |
| * 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 <core/buffer_allocator.h> |
| #include <utils/debug.h> |
| #include <sync/sync.h> |
| |
| #include "hwc_buffer_sync_handler.h" |
| #include "hwc_session.h" |
| |
| #define __CLASS__ "HWCSession" |
| |
| namespace sdm { |
| |
| using ::android::hardware::Void; |
| |
| void HWCSession::StartServices() { |
| android::status_t status = IDisplayConfig::registerAsService(); |
| if (status != android::OK) { |
| DLOGW("Could not register IDisplayConfig as service (%d).", status); |
| } else { |
| DLOGI("IDisplayConfig service registration completed."); |
| } |
| } |
| |
| int MapDisplayType(IDisplayConfig::DisplayType dpy) { |
| switch (dpy) { |
| case IDisplayConfig::DisplayType::DISPLAY_PRIMARY: |
| return HWC_DISPLAY_PRIMARY; |
| |
| case IDisplayConfig::DisplayType::DISPLAY_EXTERNAL: |
| return HWC_DISPLAY_EXTERNAL; |
| |
| case IDisplayConfig::DisplayType::DISPLAY_VIRTUAL: |
| return HWC_DISPLAY_VIRTUAL; |
| |
| default: |
| break; |
| } |
| |
| return -EINVAL; |
| } |
| |
| HWCDisplay::DisplayStatus MapExternalStatus(IDisplayConfig::DisplayExternalStatus status) { |
| switch (status) { |
| case IDisplayConfig::DisplayExternalStatus::EXTERNAL_OFFLINE: |
| return HWCDisplay::kDisplayStatusOffline; |
| |
| case IDisplayConfig::DisplayExternalStatus::EXTERNAL_ONLINE: |
| return HWCDisplay::kDisplayStatusOnline; |
| |
| case IDisplayConfig::DisplayExternalStatus::EXTERNAL_PAUSE: |
| return HWCDisplay::kDisplayStatusPause; |
| |
| case IDisplayConfig::DisplayExternalStatus::EXTERNAL_RESUME: |
| return HWCDisplay::kDisplayStatusResume; |
| |
| default: |
| break; |
| } |
| |
| return HWCDisplay::kDisplayStatusInvalid; |
| } |
| |
| // Methods from ::vendor::hardware::display::config::V1_0::IDisplayConfig follow. |
| Return<void> HWCSession::isDisplayConnected(IDisplayConfig::DisplayType dpy, |
| isDisplayConnected_cb _hidl_cb) { |
| int32_t error = -EINVAL; |
| bool connected = false; |
| int disp_id = MapDisplayType(dpy); |
| |
| if (disp_id < HWC_DISPLAY_PRIMARY || disp_id >= HWC_NUM_DISPLAY_TYPES) { |
| _hidl_cb(error, connected); |
| return Void(); |
| } |
| |
| SEQUENCE_WAIT_SCOPE_LOCK(locker_[disp_id]); |
| |
| connected = hwc_display_[disp_id]; |
| error = 0; |
| |
| _hidl_cb(error, connected); |
| return Void(); |
| } |
| |
| int32_t HWCSession::SetSecondaryDisplayStatus(int disp_id, HWCDisplay::DisplayStatus status) { |
| if (disp_id < HWC_DISPLAY_PRIMARY || disp_id >= HWC_NUM_DISPLAY_TYPES) { |
| DLOGE("Invalid display = %d", disp_id); |
| return -EINVAL; |
| } |
| |
| SEQUENCE_WAIT_SCOPE_LOCK(locker_[disp_id]); |
| DLOGI("Display = %d, Status = %d", disp_id, status); |
| |
| if (disp_id == HWC_DISPLAY_PRIMARY) { |
| DLOGE("Not supported for this display"); |
| } else if (!hwc_display_[disp_id]) { |
| DLOGW("Display is not connected"); |
| } else { |
| return hwc_display_[disp_id]->SetDisplayStatus(status); |
| } |
| |
| return -EINVAL; |
| } |
| |
| Return<int32_t> HWCSession::setSecondayDisplayStatus(IDisplayConfig::DisplayType dpy, |
| IDisplayConfig::DisplayExternalStatus status) { |
| return SetSecondaryDisplayStatus(MapDisplayType(dpy), MapExternalStatus(status)); |
| } |
| |
| Return<int32_t> HWCSession::configureDynRefeshRate(IDisplayConfig::DisplayDynRefreshRateOp op, |
| uint32_t refreshRate) { |
| SEQUENCE_WAIT_SCOPE_LOCK(locker_[HWC_DISPLAY_PRIMARY]); |
| HWCDisplay *hwc_display = hwc_display_[HWC_DISPLAY_PRIMARY]; |
| |
| switch (op) { |
| case IDisplayConfig::DisplayDynRefreshRateOp::DISABLE_METADATA_DYN_REFRESH_RATE: |
| return hwc_display->Perform(HWCDisplayPrimary::SET_METADATA_DYN_REFRESH_RATE, false); |
| |
| case IDisplayConfig::DisplayDynRefreshRateOp::ENABLE_METADATA_DYN_REFRESH_RATE: |
| return hwc_display->Perform(HWCDisplayPrimary::SET_METADATA_DYN_REFRESH_RATE, true); |
| |
| case IDisplayConfig::DisplayDynRefreshRateOp::SET_BINDER_DYN_REFRESH_RATE: |
| return hwc_display->Perform(HWCDisplayPrimary::SET_BINDER_DYN_REFRESH_RATE, refreshRate); |
| |
| default: |
| DLOGW("Invalid operation %d", op); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| int32_t HWCSession::GetConfigCount(int disp_id, uint32_t *count) { |
| if (disp_id < 0) { |
| return -EINVAL; |
| } |
| |
| SEQUENCE_WAIT_SCOPE_LOCK(locker_[disp_id]); |
| |
| if (hwc_display_[disp_id]) { |
| return hwc_display_[disp_id]->GetDisplayConfigCount(count); |
| } |
| |
| return -EINVAL; |
| } |
| |
| Return<void> HWCSession::getConfigCount(IDisplayConfig::DisplayType dpy, |
| getConfigCount_cb _hidl_cb) { |
| uint32_t count = 0; |
| int32_t error = GetConfigCount(MapDisplayType(dpy), &count); |
| |
| _hidl_cb(error, count); |
| |
| return Void(); |
| } |
| |
| int32_t HWCSession::GetActiveConfigIndex(int disp_id, uint32_t *config) { |
| if (disp_id < 0) { |
| return -EINVAL; |
| } |
| |
| SEQUENCE_WAIT_SCOPE_LOCK(locker_[disp_id]); |
| |
| if (hwc_display_[disp_id]) { |
| return hwc_display_[disp_id]->GetActiveDisplayConfig(config); |
| } |
| |
| return -EINVAL; |
| } |
| |
| Return<void> HWCSession::getActiveConfig(IDisplayConfig::DisplayType dpy, |
| getActiveConfig_cb _hidl_cb) { |
| uint32_t config = 0; |
| int32_t error = GetActiveConfigIndex(MapDisplayType(dpy), &config); |
| |
| _hidl_cb(error, config); |
| |
| return Void(); |
| } |
| |
| int32_t HWCSession::SetActiveConfigIndex(int disp_id, uint32_t config) { |
| if (disp_id < 0) { |
| return -EINVAL; |
| } |
| |
| SEQUENCE_WAIT_SCOPE_LOCK(locker_[disp_id]); |
| int32_t error = -EINVAL; |
| if (hwc_display_[disp_id]) { |
| error = hwc_display_[disp_id]->SetActiveDisplayConfig(config); |
| if (!error) { |
| Refresh(0); |
| } |
| } |
| |
| return error; |
| } |
| |
| Return<int32_t> HWCSession::setActiveConfig(IDisplayConfig::DisplayType dpy, uint32_t config) { |
| return SetActiveConfigIndex(MapDisplayType(dpy), config); |
| } |
| |
| Return<void> HWCSession::getDisplayAttributes(uint32_t configIndex, |
| IDisplayConfig::DisplayType dpy, |
| getDisplayAttributes_cb _hidl_cb) { |
| int32_t error = -EINVAL; |
| IDisplayConfig::DisplayAttributes display_attributes = {}; |
| int disp_id = MapDisplayType(dpy); |
| |
| if (disp_id >= HWC_DISPLAY_PRIMARY && disp_id < HWC_NUM_DISPLAY_TYPES) { |
| SEQUENCE_WAIT_SCOPE_LOCK(locker_[disp_id]); |
| if (hwc_display_[disp_id]) { |
| DisplayConfigVariableInfo hwc_display_attributes; |
| error = hwc_display_[disp_id]->GetDisplayAttributesForConfig(static_cast<int>(configIndex), |
| &hwc_display_attributes); |
| if (!error) { |
| display_attributes.vsyncPeriod = hwc_display_attributes.vsync_period_ns; |
| display_attributes.xRes = hwc_display_attributes.x_pixels; |
| display_attributes.yRes = hwc_display_attributes.y_pixels; |
| display_attributes.xDpi = hwc_display_attributes.x_dpi; |
| display_attributes.yDpi = hwc_display_attributes.y_dpi; |
| display_attributes.panelType = IDisplayConfig::DisplayPortType::DISPLAY_PORT_DEFAULT; |
| display_attributes.isYuv = hwc_display_attributes.is_yuv; |
| } |
| } |
| } |
| _hidl_cb(error, display_attributes); |
| |
| return Void(); |
| } |
| |
| Return<int32_t> HWCSession::setPanelBrightness(uint32_t level) { |
| SEQUENCE_WAIT_SCOPE_LOCK(locker_[HWC_DISPLAY_PRIMARY]); |
| int32_t error = -EINVAL; |
| |
| if (hwc_display_[HWC_DISPLAY_PRIMARY]) { |
| error = hwc_display_[HWC_DISPLAY_PRIMARY]->SetPanelBrightness(static_cast<int>(level)); |
| if (error) { |
| DLOGE("Failed to set the panel brightness = %d. Error = %d", level, error); |
| } |
| } |
| |
| return error; |
| } |
| |
| int32_t HWCSession::GetPanelBrightness(int *level) { |
| SEQUENCE_WAIT_SCOPE_LOCK(locker_[HWC_DISPLAY_PRIMARY]); |
| int32_t error = -EINVAL; |
| |
| if (hwc_display_[HWC_DISPLAY_PRIMARY]) { |
| error = hwc_display_[HWC_DISPLAY_PRIMARY]->GetPanelBrightness(level); |
| if (error) { |
| DLOGE("Failed to get the panel brightness. Error = %d", error); |
| } |
| } |
| |
| return error; |
| } |
| |
| Return<void> HWCSession::getPanelBrightness(getPanelBrightness_cb _hidl_cb) { |
| int level = 0; |
| int32_t error = GetPanelBrightness(&level); |
| |
| _hidl_cb(error, static_cast<uint32_t>(level)); |
| |
| return Void(); |
| } |
| |
| int32_t HWCSession::MinHdcpEncryptionLevelChanged(int disp_id, uint32_t min_enc_level) { |
| DLOGI("Display %d", disp_id); |
| |
| if (disp_id < 0) { |
| return -EINVAL; |
| } |
| |
| SEQUENCE_WAIT_SCOPE_LOCK(locker_[disp_id]); |
| if (disp_id != HWC_DISPLAY_EXTERNAL) { |
| DLOGE("Not supported for display"); |
| } else if (!hwc_display_[disp_id]) { |
| DLOGW("Display is not connected"); |
| } else { |
| return hwc_display_[disp_id]->OnMinHdcpEncryptionLevelChange(min_enc_level); |
| } |
| |
| return -EINVAL; |
| } |
| |
| Return<int32_t> HWCSession::minHdcpEncryptionLevelChanged(IDisplayConfig::DisplayType dpy, |
| uint32_t min_enc_level) { |
| return MinHdcpEncryptionLevelChanged(MapDisplayType(dpy), min_enc_level); |
| } |
| |
| Return<int32_t> HWCSession::refreshScreen() { |
| SEQUENCE_WAIT_SCOPE_LOCK(locker_[HWC_DISPLAY_PRIMARY]); |
| Refresh(HWC_DISPLAY_PRIMARY); |
| |
| return 0; |
| } |
| |
| int32_t HWCSession::ControlPartialUpdate(int disp_id, bool enable) { |
| if (disp_id < 0) { |
| return -EINVAL; |
| } |
| |
| if (disp_id != HWC_DISPLAY_PRIMARY) { |
| DLOGW("CONTROL_PARTIAL_UPDATE is not applicable for display = %d", disp_id); |
| return -EINVAL; |
| } |
| |
| SEQUENCE_WAIT_SCOPE_LOCK(locker_[disp_id]); |
| HWCDisplay *hwc_display = hwc_display_[HWC_DISPLAY_PRIMARY]; |
| if (!hwc_display) { |
| DLOGE("primary display object is not instantiated"); |
| return -EINVAL; |
| } |
| |
| uint32_t pending = 0; |
| DisplayError hwc_error = hwc_display->ControlPartialUpdate(enable, &pending); |
| |
| if (hwc_error == kErrorNone) { |
| if (!pending) { |
| return 0; |
| } |
| } else if (hwc_error == kErrorNotSupported) { |
| return 0; |
| } else { |
| return -EINVAL; |
| } |
| |
| // Todo(user): Unlock it before sending events to client. It may cause deadlocks in future. |
| Refresh(HWC_DISPLAY_PRIMARY); |
| |
| // Wait until partial update control is complete |
| int32_t error = locker_[disp_id].WaitFinite(kCommitDoneTimeoutMs); |
| |
| return error; |
| } |
| |
| Return<int32_t> HWCSession::controlPartialUpdate(IDisplayConfig::DisplayType dpy, bool enable) { |
| return ControlPartialUpdate(MapDisplayType(dpy), enable); |
| } |
| |
| Return<int32_t> HWCSession::toggleScreenUpdate(bool on) { |
| SEQUENCE_WAIT_SCOPE_LOCK(locker_[HWC_DISPLAY_PRIMARY]); |
| |
| int32_t error = -EINVAL; |
| if (hwc_display_[HWC_DISPLAY_PRIMARY]) { |
| error = hwc_display_[HWC_DISPLAY_PRIMARY]->ToggleScreenUpdates(on); |
| if (error) { |
| DLOGE("Failed to toggle screen updates = %d. Error = %d", on, error); |
| } |
| } |
| |
| return error; |
| } |
| |
| Return<int32_t> HWCSession::setIdleTimeout(uint32_t value) { |
| SEQUENCE_WAIT_SCOPE_LOCK(locker_[HWC_DISPLAY_PRIMARY]); |
| |
| if (hwc_display_[HWC_DISPLAY_PRIMARY]) { |
| hwc_display_[HWC_DISPLAY_PRIMARY]->SetIdleTimeoutMs(value); |
| return 0; |
| } |
| |
| DLOGW("Display = %d is not connected.", HWC_DISPLAY_PRIMARY); |
| return -ENODEV; |
| } |
| |
| Return<void> HWCSession::getHDRCapabilities(IDisplayConfig::DisplayType dpy, |
| getHDRCapabilities_cb _hidl_cb) { |
| int32_t error = -EINVAL; |
| IDisplayConfig::DisplayHDRCapabilities hdr_caps = {}; |
| |
| if (!_hidl_cb) { |
| DLOGE("_hidl_cb callback not provided."); |
| return Void(); |
| } |
| |
| do { |
| int disp_id = MapDisplayType(dpy); |
| if ((disp_id < 0) || (disp_id >= HWC_NUM_DISPLAY_TYPES)) { |
| DLOGE("Invalid display id = %d", disp_id); |
| break; |
| } |
| |
| SEQUENCE_WAIT_SCOPE_LOCK(locker_[disp_id]); |
| HWCDisplay *hwc_display = hwc_display_[disp_id]; |
| if (!hwc_display) { |
| DLOGW("Display = %d is not connected.", disp_id); |
| error = -ENODEV; |
| break; |
| } |
| |
| // query number of hdr types |
| uint32_t out_num_types = 0; |
| if (hwc_display->GetHdrCapabilities(&out_num_types, nullptr, nullptr, nullptr, nullptr) |
| != HWC2::Error::None) { |
| break; |
| } |
| |
| if (!out_num_types) { |
| error = 0; |
| break; |
| } |
| |
| // query hdr caps |
| hdr_caps.supportedHdrTypes.resize(out_num_types); |
| |
| float out_max_luminance = 0.0f; |
| float out_max_average_luminance = 0.0f; |
| float out_min_luminance = 0.0f; |
| if (hwc_display->GetHdrCapabilities(&out_num_types, hdr_caps.supportedHdrTypes.data(), |
| &out_max_luminance, &out_max_average_luminance, |
| &out_min_luminance) |
| == HWC2::Error::None) { |
| error = 0; |
| } |
| } while (false); |
| |
| _hidl_cb(error, hdr_caps); |
| |
| return Void(); |
| } |
| |
| Return<int32_t> HWCSession::setCameraLaunchStatus(uint32_t on) { |
| SEQUENCE_WAIT_SCOPE_LOCK(locker_[HWC_DISPLAY_PRIMARY]); |
| |
| if (!core_intf_) { |
| DLOGW("core_intf_ not initialized."); |
| return -ENOENT; |
| } |
| |
| if (!hwc_display_[HWC_DISPLAY_PRIMARY]) { |
| DLOGW("Display = %d is not connected.", HWC_DISPLAY_PRIMARY); |
| return -ENODEV; |
| } |
| |
| HWBwModes mode = on > 0 ? kBwCamera : kBwDefault; |
| |
| // trigger invalidate to apply new bw caps. |
| Refresh(HWC_DISPLAY_PRIMARY); |
| |
| if (core_intf_->SetMaxBandwidthMode(mode) != kErrorNone) { |
| return -EINVAL; |
| } |
| |
| new_bw_mode_ = true; |
| need_invalidate_ = true; |
| hwc_display_[HWC_DISPLAY_PRIMARY]->ResetValidation(); |
| |
| return 0; |
| } |
| |
| int32_t HWCSession::DisplayBWTransactionPending(bool *status) { |
| SEQUENCE_WAIT_SCOPE_LOCK(locker_[HWC_DISPLAY_PRIMARY]); |
| |
| if (hwc_display_[HWC_DISPLAY_PRIMARY]) { |
| if (sync_wait(bw_mode_release_fd_, 0) < 0) { |
| DLOGI("bw_transaction_release_fd is not yet signaled: err= %s", strerror(errno)); |
| *status = false; |
| } |
| |
| return 0; |
| } |
| |
| DLOGW("Display = %d is not connected.", HWC_DISPLAY_PRIMARY); |
| return -ENODEV; |
| } |
| |
| Return<void> HWCSession::displayBWTransactionPending(displayBWTransactionPending_cb _hidl_cb) { |
| bool status = true; |
| |
| if (!_hidl_cb) { |
| DLOGE("_hidl_cb callback not provided."); |
| return Void(); |
| } |
| |
| int32_t error = DisplayBWTransactionPending(&status); |
| |
| _hidl_cb(error, status); |
| |
| return Void(); |
| } |
| #ifdef DISPLAY_CONFIG_1_1 |
| // Methods from ::vendor::hardware::display::config::V1_1::IDisplayConfig follow. |
| Return<int32_t> HWCSession::setDisplayAnimating(uint64_t display_id, bool animating ) { |
| return CallDisplayFunction(static_cast<hwc2_device_t *>(this), display_id, |
| &HWCDisplay::SetDisplayAnimating, animating); |
| } |
| #endif |
| |
| #ifdef DISPLAY_CONFIG_1_3 |
| Return<int32_t> HWCSession::controlIdlePowerCollapse(bool enable, bool synchronous) { |
| SEQUENCE_WAIT_SCOPE_LOCK(locker_[HWC_DISPLAY_PRIMARY]); |
| |
| if (hwc_display_[HWC_DISPLAY_PRIMARY]) { |
| if (!enable) { |
| if (!idle_pc_ref_cnt_) { |
| HWC2::Error err = |
| hwc_display_[HWC_DISPLAY_PRIMARY]->ControlIdlePowerCollapse(enable, synchronous); |
| if (err == HWC2::Error::Unsupported) { |
| return 0; |
| } |
| Refresh(HWC_DISPLAY_PRIMARY); |
| int32_t error = locker_[HWC_DISPLAY_PRIMARY].WaitFinite(kCommitDoneTimeoutMs); |
| if (error == ETIMEDOUT) { |
| DLOGE("Timed out!! Next frame commit done event not received!!"); |
| return error; |
| } |
| DLOGI("Idle PC disabled!!"); |
| } |
| idle_pc_ref_cnt_++; |
| } else if (idle_pc_ref_cnt_ > 0) { |
| if (!(idle_pc_ref_cnt_ - 1)) { |
| HWC2::Error err = |
| hwc_display_[HWC_DISPLAY_PRIMARY]->ControlIdlePowerCollapse(enable, synchronous); |
| if (err == HWC2::Error::Unsupported) { |
| return 0; |
| } |
| DLOGI("Idle PC enabled!!"); |
| } |
| idle_pc_ref_cnt_--; |
| } |
| return 0; |
| } |
| |
| DLOGW("Display = %d is not connected.", HWC_DISPLAY_PRIMARY); |
| return -ENODEV; |
| } |
| #endif // DISPLAY_CONFIG_1_3 |
| |
| } // namespace sdm |