/* * Copyright (c) 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: * * 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. */ #define ATRACE_TAG (ATRACE_TAG_GRAPHICS | ATRACE_TAG_HAL) #include #include #include "drm_atomic_req.h" #include "drm_connector.h" #include "drm_crtc.h" #include "drm_manager.h" #include "drm_plane.h" #include "string.h" #define __CLASS__ "DRMAtomicReq" namespace sde_drm { DRMAtomicReq::DRMAtomicReq(int fd, DRMManager *drm_mgr) : drm_mgr_(drm_mgr), fd_(fd) {} DRMAtomicReq::~DRMAtomicReq() { if (drm_atomic_req_) { drmModeAtomicFree(drm_atomic_req_); drm_atomic_req_ = nullptr; } } int DRMAtomicReq::Init(const DRMDisplayToken &tok) { token_ = tok; drm_atomic_req_ = drmModeAtomicAlloc(); if (!drm_atomic_req_) { return -ENOMEM; } return 0; } int DRMAtomicReq::Perform(DRMOps opcode, uint32_t obj_id, ...) { va_list args; va_start(args, obj_id); switch (opcode) { case DRMOps::PLANE_SET_SRC_RECT: case DRMOps::PLANE_SET_DST_RECT: case DRMOps::PLANE_SET_ZORDER: case DRMOps::PLANE_SET_ROTATION: case DRMOps::PLANE_SET_ALPHA: case DRMOps::PLANE_SET_BLEND_TYPE: case DRMOps::PLANE_SET_H_DECIMATION: case DRMOps::PLANE_SET_V_DECIMATION: case DRMOps::PLANE_SET_FB_ID: case DRMOps::PLANE_SET_ROT_FB_ID: case DRMOps::PLANE_SET_CRTC: case DRMOps::PLANE_SET_SRC_CONFIG: case DRMOps::PLANE_SET_INPUT_FENCE: case DRMOps::PLANE_SET_SCALER_CONFIG: case DRMOps::PLANE_SET_FB_SECURE_MODE: case DRMOps::PLANE_SET_CSC_CONFIG: case DRMOps::PLANE_SET_MULTIRECT_MODE: case DRMOps::PLANE_SET_EXCL_RECT: case DRMOps::PLANE_SET_INVERSE_PMA: case DRMOps::PLANE_SET_DGM_CSC_CONFIG: case DRMOps::PLANE_SET_POST_PROC: case DRMOps::PLANE_SET_SSPP_LAYOUT: { drm_mgr_->GetPlaneMgr()->Perform(opcode, obj_id, drm_atomic_req_, args); } break; case DRMOps::CRTC_SET_POST_PROC: case DRMOps::CRTC_SET_MODE: case DRMOps::CRTC_SET_ACTIVE: case DRMOps::CRTC_SET_OUTPUT_FENCE_OFFSET: case DRMOps::CRTC_SET_CORE_CLK: case DRMOps::CRTC_SET_CORE_AB: case DRMOps::CRTC_SET_CORE_IB: case DRMOps::CRTC_SET_LLCC_AB: case DRMOps::CRTC_SET_LLCC_IB: case DRMOps::CRTC_SET_DRAM_AB: case DRMOps::CRTC_SET_DRAM_IB: case DRMOps::CRTC_SET_ROT_PREFILL_BW: case DRMOps::CRTC_SET_ROT_CLK: case DRMOps::CRTC_GET_RELEASE_FENCE: case DRMOps::CRTC_SET_ROI: case DRMOps::CRTC_SET_SECURITY_LEVEL: case DRMOps::CRTC_SET_SOLIDFILL_STAGES: case DRMOps::CRTC_SET_IDLE_TIMEOUT: case DRMOps::CRTC_SET_DEST_SCALER_CONFIG: case DRMOps::CRTC_SET_CAPTURE_MODE: case DRMOps::CRTC_SET_IDLE_PC_STATE: { drm_mgr_->GetCrtcMgr()->Perform(opcode, obj_id, drm_atomic_req_, args); } break; case DRMOps::CONNECTOR_SET_CRTC: case DRMOps::CONNECTOR_GET_RETIRE_FENCE: case DRMOps::CONNECTOR_SET_OUTPUT_RECT: case DRMOps::CONNECTOR_SET_OUTPUT_FB_ID: case DRMOps::CONNECTOR_SET_POWER_MODE: case DRMOps::CONNECTOR_SET_ROI: case DRMOps::CONNECTOR_SET_AUTOREFRESH: case DRMOps::CONNECTOR_SET_FB_SECURE_MODE: case DRMOps::CONNECTOR_SET_POST_PROC: case DRMOps::CONNECTOR_SET_HDR_METADATA: case DRMOps::CONNECTOR_SET_QSYNC_MODE: case DRMOps::CONNECTOR_SET_TOPOLOGY_CONTROL: case DRMOps::CONNECTOR_SET_FRAME_TRIGGER: case DRMOps::CONNECTOR_SET_COLORSPACE: { drm_mgr_->GetConnectorMgr()->Perform(opcode, obj_id, drm_atomic_req_, args); } break; case DRMOps::DPPS_CACHE_FEATURE: { drm_mgr_->GetDppsMgrIntf()->CacheDppsFeature(obj_id, args); } break; case DRMOps::DPPS_COMMIT_FEATURE: { drm_mgr_->GetDppsMgrIntf()->CommitDppsFeatures(drm_atomic_req_, token_); } break; default: DRM_LOGE("Invalid opcode %d", opcode); } va_end(args); return 0; } int DRMAtomicReq::CallAtomic(DRMCrtc *crtc, uint32_t flags) { auto plane_mgr = drm_mgr_->GetPlaneMgr(); size_t cnt; cnt = plane_mgr->ApplyDirtyProperties(drm_atomic_req_); ATRACE_INT("dirtyPlaneProps", cnt); cnt = crtc->ApplyDirtyProperties(drm_atomic_req_); ATRACE_INT("dirtyCrtcProps", cnt); int ret = drmModeAtomicCommit(fd_, drm_atomic_req_, flags, nullptr); if (ret) { DRM_LOGE("drmModeAtomicCommit failed with error %d (%s).", errno, strerror(errno)); /* reset all properties so next atomic commit applies all values */ crtc->ClearProperties(); plane_mgr->ClearProperties(); } // reset the drm_atomic_req_ for next call drmModeAtomicSetCursor(drm_atomic_req_, 0); return ret; } int DRMAtomicReq::Validate() { auto crtc = drm_mgr_->GetCrtcMgr()->GetObject(token_.crtc_id); if (crtc == nullptr) { DRM_LOGE("Invalid crtc %d", token_.crtc_id); return -EINVAL; } drm_mgr_->GetPlaneMgr()->UnsetUnusedResources(token_.crtc_id, false /*is_commit*/, drm_atomic_req_); int ret = CallAtomic(crtc, DRM_MODE_ATOMIC_ALLOW_MODESET | DRM_MODE_ATOMIC_TEST_ONLY); if (!ret) crtc->PostValidate(); // reset any dirty properties, all properties should be set again before Commit crtc->DiscardDirtyProperties(); drm_mgr_->GetPlaneMgr()->PostValidate(token_.crtc_id); return ret; } int DRMAtomicReq::Commit(bool synchronous, bool retain_planes) { DTRACE_SCOPED(); auto crtc = drm_mgr_->GetCrtcMgr()->GetObject(token_.crtc_id); if (crtc == nullptr) { DRM_LOGE("Invalid crtc %d", token_.crtc_id); return -EINVAL; } if (retain_planes) { // It is not enough to simply avoid calling UnsetUnusedPlanes, since state transitons have to // be correct when CommitPlaneState is called drm_mgr_->GetPlaneMgr()->RetainPlanes(token_.crtc_id); } drm_mgr_->GetPlaneMgr()->UnsetUnusedResources(token_.crtc_id, true /*is_commit*/, drm_atomic_req_); uint32_t flags = DRM_MODE_ATOMIC_ALLOW_MODESET; if (!synchronous) { flags |= DRM_MODE_ATOMIC_NONBLOCK; } int ret = CallAtomic(crtc, flags); drm_mgr_->GetPlaneMgr()->PostCommit(token_.crtc_id, !ret); crtc->PostCommit(!ret); ATRACE_INT("dirtyPlaneProps", 0); ATRACE_INT("dirtyCrtcProps", 0); return ret; } } // namespace sde_drm