| /* |
| * 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. |
| */ |
| |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <drm.h> |
| #include <drm/sde_drm.h> |
| #include <drm_logger.h> |
| #include <drm/drm_fourcc.h> |
| #include <string.h> |
| |
| #include <algorithm> |
| #include <map> |
| #include <sstream> |
| #include <string> |
| #include <vector> |
| #include <utility> |
| |
| #include "drm_utils.h" |
| #include "drm_crtc.h" |
| #include "drm_property.h" |
| |
| namespace sde_drm { |
| |
| using std::string; |
| using std::stringstream; |
| using std::unique_ptr; |
| using std::map; |
| using std::mutex; |
| using std::lock_guard; |
| |
| // CRTC Security Levels |
| static uint8_t SECURE_NON_SECURE = 0; |
| static uint8_t SECURE_ONLY = 1; |
| |
| // CWB Capture Modes |
| static uint8_t CAPTURE_MIXER_OUT = 0; |
| static uint8_t CAPTURE_DSPP_OUT = 1; |
| |
| // Idle PC states |
| static uint8_t IDLE_PC_STATE_NONE = 0; |
| static uint8_t IDLE_PC_STATE_ENABLE = 1; |
| static uint8_t IDLE_PC_STATE_DISABLE = 2; |
| |
| static void PopulateSecurityLevels(drmModePropertyRes *prop) { |
| static bool security_levels_populated = false; |
| if (!security_levels_populated) { |
| for (auto i = 0; i < prop->count_enums; i++) { |
| string enum_name(prop->enums[i].name); |
| if (enum_name == "sec_and_non_sec") { |
| SECURE_NON_SECURE = prop->enums[i].value; |
| } else if (enum_name == "sec_only") { |
| SECURE_ONLY = prop->enums[i].value; |
| } |
| } |
| security_levels_populated = true; |
| } |
| } |
| |
| static void PopulateCWbCaptureModes(drmModePropertyRes *prop) { |
| static bool capture_modes_populated = false; |
| if (!capture_modes_populated) { |
| for (auto i = 0; i < prop->count_enums; i++) { |
| string enum_name(prop->enums[i].name); |
| if (enum_name == "capture_mixer_out") { |
| CAPTURE_MIXER_OUT = prop->enums[i].value; |
| } else if (enum_name == "capture_pp_out") { |
| CAPTURE_DSPP_OUT = prop->enums[i].value; |
| } |
| } |
| capture_modes_populated = true; |
| } |
| } |
| |
| static void PopulateIdlePCStates(drmModePropertyRes *prop) { |
| static bool idle_pc_state_populated = false; |
| if (!idle_pc_state_populated) { |
| for (auto i = 0; i < prop->count_enums; i++) { |
| string enum_name(prop->enums[i].name); |
| if (enum_name == "idle_pc_none") { |
| IDLE_PC_STATE_NONE = prop->enums[i].value; |
| } else if (enum_name == "idle_pc_enable") { |
| IDLE_PC_STATE_ENABLE = prop->enums[i].value; |
| } else if (enum_name == "idle_pc_disable") { |
| IDLE_PC_STATE_DISABLE = prop->enums[i].value; |
| } |
| } |
| idle_pc_state_populated = true; |
| } |
| } |
| |
| #define __CLASS__ "DRMCrtcManager" |
| |
| void DRMCrtcManager::Init(drmModeRes *resource) { |
| for (int i = 0; i < resource->count_crtcs; i++) { |
| unique_ptr<DRMCrtc> crtc(new DRMCrtc(fd_, i)); |
| drmModeCrtc *libdrm_crtc = drmModeGetCrtc(fd_, resource->crtcs[i]); |
| if (libdrm_crtc) { |
| crtc->InitAndParse(libdrm_crtc); |
| crtc_pool_[resource->crtcs[i]] = std::move(crtc); |
| } else { |
| DRM_LOGE("Critical error: drmModeGetCrtc() failed for crtc %d.", resource->crtcs[i]); |
| } |
| } |
| } |
| |
| void DRMCrtcManager::DumpByID(uint32_t id) { |
| crtc_pool_.at(id)->Dump(); |
| } |
| |
| void DRMCrtcManager::DumpAll() { |
| for (auto &crtc : crtc_pool_) { |
| crtc.second->Dump(); |
| } |
| } |
| |
| void DRMCrtcManager::Perform(DRMOps code, uint32_t obj_id, drmModeAtomicReq *req, |
| va_list args) { |
| lock_guard<mutex> lock(lock_); |
| auto it = crtc_pool_.find(obj_id); |
| if (it == crtc_pool_.end()) { |
| DRM_LOGE("Invalid crtc id %d", obj_id); |
| return; |
| } |
| |
| if (code == DRMOps::CRTC_SET_DEST_SCALER_CONFIG) { |
| if (crtc_pool_.at(obj_id)->ConfigureScalerLUT(req, dir_lut_blob_id_, cir_lut_blob_id_, |
| sep_lut_blob_id_)) { |
| DRM_LOGD("CRTC %d: Configuring scaler LUTs", obj_id); |
| } |
| } |
| |
| it->second->Perform(code, req, args); |
| } |
| |
| void DRMCrtcManager::SetScalerLUT(const DRMScalerLUTInfo &lut_info) { |
| // qseed3lite lut is hardcoded in HW. No need to program from sw. |
| DRMCrtcInfo info; |
| crtc_pool_.begin()->second->GetInfo(&info); |
| if (info.qseed_version == QSEEDVersion::V3LITE) { |
| return; |
| } |
| |
| if (lut_info.dir_lut_size) { |
| drmModeCreatePropertyBlob(fd_, reinterpret_cast<void *>(lut_info.dir_lut), |
| lut_info.dir_lut_size, &dir_lut_blob_id_); |
| } |
| if (lut_info.cir_lut_size) { |
| drmModeCreatePropertyBlob(fd_, reinterpret_cast<void *>(lut_info.cir_lut), |
| lut_info.cir_lut_size, &cir_lut_blob_id_); |
| } |
| if (lut_info.sep_lut_size) { |
| drmModeCreatePropertyBlob(fd_, reinterpret_cast<void *>(lut_info.sep_lut), |
| lut_info.sep_lut_size, &sep_lut_blob_id_); |
| } |
| } |
| |
| void DRMCrtcManager::UnsetScalerLUT() { |
| if (dir_lut_blob_id_) { |
| drmModeDestroyPropertyBlob(fd_, dir_lut_blob_id_); |
| dir_lut_blob_id_ = 0; |
| } |
| if (cir_lut_blob_id_) { |
| drmModeDestroyPropertyBlob(fd_, cir_lut_blob_id_); |
| cir_lut_blob_id_ = 0; |
| } |
| if (sep_lut_blob_id_) { |
| drmModeDestroyPropertyBlob(fd_, sep_lut_blob_id_); |
| sep_lut_blob_id_ = 0; |
| } |
| } |
| |
| int DRMCrtcManager::GetCrtcInfo(uint32_t crtc_id, DRMCrtcInfo *info) { |
| if (crtc_id == 0) { |
| crtc_pool_.begin()->second->GetInfo(info); |
| } else { |
| auto iter = crtc_pool_.find(crtc_id); |
| if (iter == crtc_pool_.end()) { |
| DRM_LOGE("Invalid crtc id %d", crtc_id); |
| return -ENODEV; |
| } else { |
| iter->second->GetInfo(info); |
| } |
| } |
| |
| return 0; |
| } |
| |
| void DRMCrtcManager::GetPPInfo(uint32_t crtc_id, DRMPPFeatureInfo *info) { |
| auto it = crtc_pool_.find(crtc_id); |
| if (it == crtc_pool_.end()) { |
| DRM_LOGE("Invalid crtc id %d", crtc_id); |
| return; |
| } |
| |
| it->second->GetPPInfo(info); |
| } |
| |
| int DRMCrtcManager::Reserve(const std::set<uint32_t> &possible_crtc_indices, |
| DRMDisplayToken *token) { |
| for (auto &item : crtc_pool_) { |
| if (item.second->GetStatus() == DRMStatus::FREE) { |
| if (possible_crtc_indices.find(item.second->GetIndex()) != possible_crtc_indices.end()) { |
| item.second->Lock(); |
| token->crtc_id = item.first; |
| token->crtc_index = item.second->GetIndex(); |
| return 0; |
| } |
| } |
| } |
| |
| return -ENODEV; |
| } |
| |
| void DRMCrtcManager::Free(DRMDisplayToken *token) { |
| lock_guard<mutex> lock(lock_); |
| crtc_pool_.at(token->crtc_id)->Unlock(); |
| token->crtc_id = 0; |
| token->crtc_index = 0; |
| } |
| |
| void DRMCrtcManager::PostValidate(uint32_t crtc_id, bool success) { |
| lock_guard<mutex> lock(lock_); |
| crtc_pool_.at(crtc_id)->PostValidate(success); |
| } |
| |
| void DRMCrtcManager::PostCommit(uint32_t crtc_id, bool success) { |
| lock_guard<mutex> lock(lock_); |
| crtc_pool_.at(crtc_id)->PostCommit(success); |
| } |
| |
| // ==============================================================================================// |
| |
| #undef __CLASS__ |
| #define __CLASS__ "DRMCrtc" |
| |
| DRMCrtc::~DRMCrtc() { |
| if (drm_crtc_) { |
| drmModeFreeCrtc(drm_crtc_); |
| } |
| } |
| |
| void DRMCrtc::ParseProperties() { |
| drmModeObjectProperties *props = |
| drmModeObjectGetProperties(fd_, drm_crtc_->crtc_id, DRM_MODE_OBJECT_CRTC); |
| if (!props || !props->props || !props->prop_values) { |
| drmModeFreeObjectProperties(props); |
| return; |
| } |
| |
| for (uint32_t j = 0; j < props->count_props; j++) { |
| drmModePropertyRes *info = drmModeGetProperty(fd_, props->props[j]); |
| if (!info) { |
| continue; |
| } |
| |
| string property_name(info->name); |
| DRMProperty prop_enum = prop_mgr_.GetPropertyEnum(property_name); |
| if (prop_enum == DRMProperty::INVALID) { |
| DRM_LOGD("DRMProperty %s missing from global property mapping", info->name); |
| drmModeFreeProperty(info); |
| continue; |
| } |
| |
| if (prop_enum == DRMProperty::SECURITY_LEVEL) { |
| PopulateSecurityLevels(info); |
| } |
| |
| if (prop_enum == DRMProperty::CAPTURE_MODE) { |
| crtc_info_.concurrent_writeback = true; |
| PopulateCWbCaptureModes(info); |
| } |
| |
| if (prop_enum == DRMProperty::IDLE_PC_STATE) { |
| PopulateIdlePCStates(info); |
| } |
| |
| prop_mgr_.SetPropertyId(prop_enum, info->prop_id); |
| if (prop_enum == DRMProperty::CAPABILITIES) { |
| ParseCapabilities(props->prop_values[j]); |
| } |
| drmModeFreeProperty(info); |
| } |
| |
| drmModeFreeObjectProperties(props); |
| } |
| |
| void DRMCrtc::ParseCapabilities(uint64_t blob_id) { |
| drmModePropertyBlobRes *blob = drmModeGetPropertyBlob(fd_, blob_id); |
| if (!blob) { |
| return; |
| } |
| |
| const char *fmt_str = reinterpret_cast<const char *>(blob->data); |
| stringstream stream(fmt_str); |
| string line = {}; |
| string max_blendstages = "max_blendstages="; |
| string qseed_type = "qseed_type="; |
| string has_src_split = "has_src_split="; |
| string sdma_rev = "smart_dma_rev="; |
| string core_ib_ff = "core_ib_ff="; |
| string dest_scale_prefill_lines = "dest_scale_prefill_lines="; |
| string undersized_prefill_lines = "undersized_prefill_lines="; |
| string macrotile_prefill_lines = "macrotile_prefill_lines="; |
| string yuv_nv12_prefill_lines = "yuv_nv12_prefill_lines="; |
| string linear_prefill_lines = "linear_prefill_lines="; |
| string downscaling_prefill_lines = "downscaling_prefill_lines="; |
| string xtra_prefill_lines = "xtra_prefill_lines="; |
| string amortizable_threshold = "amortizable_threshold="; |
| string max_bandwidth_low = "max_bandwidth_low="; |
| string max_bandwidth_high = "max_bandwidth_high="; |
| string max_mdp_clk = "max_mdp_clk="; |
| string core_clk_ff = "core_clk_ff="; |
| string comp_ratio_rt = "comp_ratio_rt="; |
| string comp_ratio_nrt = "comp_ratio_nrt="; |
| string hw_version = "hw_version="; |
| string solidfill_stages = "dim_layer_v1_max_layers="; |
| string has_hdr = "has_hdr="; |
| string min_prefill_lines = "min_prefill_lines="; |
| |
| crtc_info_.max_solidfill_stages = 0; // default _ |
| string dest_scaler_count = "dest_scaler_count="; |
| string max_dest_scale_up = "max_dest_scale_up="; |
| string max_dest_scaler_input_width = "max_dest_scaler_input_width="; |
| string max_dest_scaler_output_width = "max_dest_scaler_output_width="; |
| string sec_ui_blendstage = "sec_ui_blendstage="; |
| |
| while (std::getline(stream, line)) { |
| if (line.find(max_blendstages) != string::npos) { |
| crtc_info_.max_blend_stages = std::stoi(string(line, max_blendstages.length())); |
| } else if (line.find(qseed_type) != string::npos) { |
| if (string(line, qseed_type.length()) == "qseed2") { |
| crtc_info_.qseed_version = QSEEDVersion::V2; |
| } else if (string(line, qseed_type.length()) == "qseed3") { |
| crtc_info_.qseed_version = QSEEDVersion::V3; |
| } else if (string(line, qseed_type.length()) == "qseed3lite") { |
| crtc_info_.qseed_version = QSEEDVersion::V3LITE; |
| } |
| } else if (line.find(has_src_split) != string::npos) { |
| crtc_info_.has_src_split = std::stoi(string(line, has_src_split.length())); |
| } else if (line.find(sdma_rev) != string::npos) { |
| if (string(line, sdma_rev.length()) == "smart_dma_v2p5") |
| crtc_info_.smart_dma_rev = SmartDMARevision::V2p5; |
| else if (string(line, sdma_rev.length()) == "smart_dma_v2") |
| crtc_info_.smart_dma_rev = SmartDMARevision::V2; |
| else if (string(line, sdma_rev.length()) == "smart_dma_v1") |
| crtc_info_.smart_dma_rev = SmartDMARevision::V1; |
| } else if (line.find(core_ib_ff) != string::npos) { |
| crtc_info_.ib_fudge_factor = std::stof(string(line, core_ib_ff.length())); |
| } else if (line.find(dest_scale_prefill_lines) != string::npos) { |
| crtc_info_.dest_scale_prefill_lines = |
| std::stoi(string(line, dest_scale_prefill_lines.length())); |
| } else if (line.find(undersized_prefill_lines) != string::npos) { |
| crtc_info_.undersized_prefill_lines = |
| std::stoi(string(line, undersized_prefill_lines.length())); |
| } else if (line.find(macrotile_prefill_lines) != string::npos) { |
| crtc_info_.macrotile_prefill_lines = |
| std::stoi(string(line, macrotile_prefill_lines.length())); |
| } else if (line.find(yuv_nv12_prefill_lines) != string::npos) { |
| crtc_info_.nv12_prefill_lines = std::stoi(string(line, yuv_nv12_prefill_lines.length())); |
| } else if (line.find(linear_prefill_lines) != string::npos) { |
| crtc_info_.linear_prefill_lines = std::stoi(string(line, linear_prefill_lines.length())); |
| } else if (line.find(downscaling_prefill_lines) != string::npos) { |
| crtc_info_.downscale_prefill_lines = |
| std::stoi(string(line, downscaling_prefill_lines.length())); |
| } else if (line.find(xtra_prefill_lines) != string::npos) { |
| crtc_info_.extra_prefill_lines = std::stoi(string(line, xtra_prefill_lines.length())); |
| } else if (line.find(amortizable_threshold) != string::npos) { |
| crtc_info_.amortized_threshold = std::stoi(string(line, amortizable_threshold.length())); |
| } else if (line.find(max_bandwidth_low) != string::npos) { |
| crtc_info_.max_bandwidth_low = std::stoull(string(line, max_bandwidth_low.length())); |
| } else if (line.find(max_bandwidth_high) != string::npos) { |
| crtc_info_.max_bandwidth_high = std::stoull(string(line, max_bandwidth_high.length())); |
| } else if (line.find(max_mdp_clk) != string::npos) { |
| crtc_info_.max_sde_clk = std::stoi(string(line, max_mdp_clk.length())); |
| } else if (line.find(core_clk_ff) != string::npos) { |
| crtc_info_.clk_fudge_factor = std::stof(string(line, core_clk_ff.length())); |
| } else if (line.find(comp_ratio_rt) != string::npos) { |
| ParseCompRatio(line.substr(comp_ratio_rt.length()), true); |
| } else if (line.find(comp_ratio_nrt) != string::npos) { |
| ParseCompRatio(line.substr(comp_ratio_nrt.length()), false); |
| } else if (line.find(hw_version) != string::npos) { |
| crtc_info_.hw_version = std::stoi(string(line, hw_version.length())); |
| } else if (line.find(solidfill_stages) != string::npos) { |
| crtc_info_.max_solidfill_stages = std::stoi(string(line, solidfill_stages.length())); |
| } else if (line.find(dest_scaler_count) != string::npos) { |
| crtc_info_.dest_scaler_count = std::stoi(string(line, dest_scaler_count.length())); |
| } else if (line.find(max_dest_scale_up) != string::npos) { |
| crtc_info_.max_dest_scale_up = std::stoi(string(line, max_dest_scale_up.length())); |
| } else if (line.find(max_dest_scaler_input_width) != string::npos) { |
| crtc_info_.max_dest_scaler_input_width = |
| std::stoi(string(line, max_dest_scaler_input_width.length())); |
| } else if (line.find(max_dest_scaler_output_width) != string::npos) { |
| crtc_info_.max_dest_scaler_output_width = |
| std::stoi(string(line, max_dest_scaler_output_width.length())); |
| } else if (line.find(has_hdr) != string::npos) { |
| crtc_info_.has_hdr = std::stoi(string(line, has_hdr.length())); |
| } else if (line.find(min_prefill_lines) != string::npos) { |
| crtc_info_.min_prefill_lines = std::stoi(string(line, min_prefill_lines.length())); |
| } else if (line.find(sec_ui_blendstage) != string::npos) { |
| crtc_info_.secure_disp_blend_stage = std::stoi(string(line, (sec_ui_blendstage).length())); |
| } |
| } |
| drmModeFreePropertyBlob(blob); |
| } |
| |
| void DRMCrtc::ParseCompRatio(string line, bool real_time) { |
| CompRatioMap &comp_ratio_map = |
| real_time ? crtc_info_.comp_ratio_rt_map : crtc_info_.comp_ratio_nrt_map; |
| std::vector<string> format_cr_list; |
| |
| Tokenize(line, &format_cr_list, ' '); |
| |
| for (uint32_t i = 0; i < format_cr_list.size(); i++) { |
| std::vector<string> format_cr; |
| Tokenize(format_cr_list.at(i), &format_cr, '/'); |
| std::string format = format_cr.at(0); |
| uint64_t vendor_code = stoi(format_cr.at(1)); |
| uint64_t fmt_modifier = stoi(format_cr.at(2)); |
| float comp_ratio = std::stof(format_cr.at(3)); |
| uint64_t modifier = 0; |
| |
| if (vendor_code == DRM_FORMAT_MOD_VENDOR_QCOM) { |
| // Macro from drm_fourcc.h to form modifier |
| modifier = fourcc_mod_code(QCOM, fmt_modifier); |
| } |
| |
| std::pair<uint32_t, uint64_t> drm_format = |
| std::make_pair(fourcc_code(format[0], format[1], format[2], format[3]), modifier); |
| comp_ratio_map.insert(std::make_pair(drm_format, comp_ratio)); |
| } |
| } |
| |
| void DRMCrtc::GetInfo(DRMCrtcInfo *info) { |
| *info = crtc_info_; |
| } |
| |
| void DRMCrtc::Lock() { |
| status_ = DRMStatus::BUSY; |
| } |
| |
| void DRMCrtc::Unlock() { |
| if (mode_blob_id_) { |
| drmModeDestroyPropertyBlob(fd_, mode_blob_id_); |
| mode_blob_id_ = 0; |
| } |
| |
| tmp_prop_val_map_.clear(); |
| committed_prop_val_map_.clear(); |
| status_ = DRMStatus::FREE; |
| } |
| |
| void DRMCrtc::SetModeBlobID(uint64_t blob_id) { |
| if (mode_blob_id_) { |
| drmModeDestroyPropertyBlob(fd_, mode_blob_id_); |
| } |
| |
| mode_blob_id_ = blob_id; |
| } |
| |
| void DRMCrtc::InitAndParse(drmModeCrtc *crtc) { |
| drm_crtc_ = crtc; |
| ParseProperties(); |
| pp_mgr_ = std::unique_ptr<DRMPPManager>(new DRMPPManager(fd_)); |
| pp_mgr_->Init(prop_mgr_, DRM_MODE_OBJECT_CRTC); |
| } |
| |
| void DRMCrtc::Perform(DRMOps code, drmModeAtomicReq *req, va_list args) { |
| uint32_t obj_id = drm_crtc_->crtc_id; |
| |
| switch (code) { |
| case DRMOps::CRTC_SET_MODE: { |
| drmModeModeInfo *mode = va_arg(args, drmModeModeInfo *); |
| uint32_t prop_id = prop_mgr_.GetPropertyId(DRMProperty::MODE_ID); |
| uint32_t blob_id = 0; |
| |
| if (mode) { |
| if (drmModeCreatePropertyBlob(fd_, (const void *)mode, sizeof(drmModeModeInfo), &blob_id)) { |
| DRM_LOGE("drmModeCreatePropertyBlob failed for CRTC_SET_MODE, crtc %d", obj_id); |
| return; |
| } |
| } |
| |
| AddProperty(req, obj_id, prop_id, blob_id, true /* cache */, tmp_prop_val_map_); |
| SetModeBlobID(blob_id); |
| DRM_LOGD("CRTC %d: Set mode %s", obj_id, mode ? mode->name : "null"); |
| } break; |
| |
| case DRMOps::CRTC_SET_OUTPUT_FENCE_OFFSET: { |
| uint32_t offset = va_arg(args, uint32_t); |
| AddProperty(req, obj_id, |
| prop_mgr_.GetPropertyId(DRMProperty::OUTPUT_FENCE_OFFSET), |
| offset, true /* cache */, tmp_prop_val_map_); |
| }; break; |
| |
| case DRMOps::CRTC_SET_CORE_CLK: { |
| uint32_t core_clk = va_arg(args, uint32_t); |
| AddProperty(req, obj_id, |
| prop_mgr_.GetPropertyId(DRMProperty::CORE_CLK), core_clk, true /* cache */, |
| tmp_prop_val_map_); |
| }; break; |
| |
| case DRMOps::CRTC_SET_CORE_AB: { |
| uint64_t core_ab = va_arg(args, uint64_t); |
| AddProperty(req, obj_id, |
| prop_mgr_.GetPropertyId(DRMProperty::CORE_AB), core_ab, true /* cache */, |
| tmp_prop_val_map_); |
| }; break; |
| |
| case DRMOps::CRTC_SET_CORE_IB: { |
| uint64_t core_ib = va_arg(args, uint64_t); |
| AddProperty(req, obj_id, |
| prop_mgr_.GetPropertyId(DRMProperty::CORE_IB), core_ib, true /* cache */, |
| tmp_prop_val_map_); |
| }; break; |
| |
| case DRMOps::CRTC_SET_LLCC_AB: { |
| uint64_t llcc_ab = va_arg(args, uint64_t); |
| AddProperty(req, obj_id, |
| prop_mgr_.GetPropertyId(DRMProperty::LLCC_AB), llcc_ab, true /* cache */, |
| tmp_prop_val_map_); |
| }; break; |
| |
| case DRMOps::CRTC_SET_LLCC_IB: { |
| uint64_t llcc_ib = va_arg(args, uint64_t); |
| AddProperty(req, obj_id, |
| prop_mgr_.GetPropertyId(DRMProperty::LLCC_IB), llcc_ib, true /* cache */, |
| tmp_prop_val_map_); |
| }; break; |
| |
| case DRMOps::CRTC_SET_DRAM_AB: { |
| uint64_t dram_ab = va_arg(args, uint64_t); |
| AddProperty(req, obj_id, |
| prop_mgr_.GetPropertyId(DRMProperty::DRAM_AB), dram_ab, true /* cache */, |
| tmp_prop_val_map_); |
| }; break; |
| |
| case DRMOps::CRTC_SET_DRAM_IB: { |
| uint64_t dram_ib = va_arg(args, uint64_t); |
| AddProperty(req, obj_id, |
| prop_mgr_.GetPropertyId(DRMProperty::DRAM_IB), dram_ib, true /* cache */, |
| tmp_prop_val_map_); |
| }; break; |
| |
| case DRMOps::CRTC_SET_ROT_PREFILL_BW: { |
| uint64_t rot_bw = va_arg(args, uint64_t); |
| drmModeAtomicAddProperty(req, obj_id, |
| prop_mgr_.GetPropertyId(DRMProperty::ROT_PREFILL_BW), rot_bw); |
| }; break; |
| |
| case DRMOps::CRTC_SET_ROT_CLK: { |
| uint32_t rot_clk = va_arg(args, uint32_t); |
| AddProperty(req, obj_id, |
| prop_mgr_.GetPropertyId(DRMProperty::ROT_CLK), rot_clk, true /* cache */, |
| tmp_prop_val_map_); |
| }; break; |
| |
| case DRMOps::CRTC_GET_RELEASE_FENCE: { |
| int64_t *fence = va_arg(args, int64_t *); |
| *fence = -1; |
| uint32_t prop_id = prop_mgr_.GetPropertyId(DRMProperty::OUTPUT_FENCE); |
| AddProperty(req, obj_id, prop_id, reinterpret_cast<uint64_t>(fence), false /* cache */, |
| tmp_prop_val_map_); |
| } break; |
| |
| case DRMOps::CRTC_SET_ACTIVE: { |
| uint32_t enable = va_arg(args, uint32_t); |
| AddProperty(req, obj_id, prop_mgr_.GetPropertyId(DRMProperty::ACTIVE), enable, |
| true /* cache */, tmp_prop_val_map_); |
| DRM_LOGD("CRTC %d: Set active %d", obj_id, enable); |
| if (enable == 0) { |
| ClearVotesCache(); |
| } |
| } break; |
| |
| case DRMOps::CRTC_SET_POST_PROC: { |
| DRMPPFeatureInfo *data = va_arg(args, DRMPPFeatureInfo*); |
| if (data) |
| pp_mgr_->SetPPFeature(req, obj_id, *data); |
| DRM_LOGD("CRTC %d: Set post proc", obj_id); |
| } break; |
| |
| case DRMOps::CRTC_SET_ROI: { |
| uint32_t num_roi = va_arg(args, uint32_t); |
| DRMRect *crtc_rois = va_arg(args, DRMRect*); |
| SetROI(req, obj_id, num_roi, crtc_rois); |
| } break; |
| |
| case DRMOps::CRTC_SET_SECURITY_LEVEL: { |
| int security_level = va_arg(args, int); |
| uint32_t crtc_security_level = SECURE_NON_SECURE; |
| if (security_level == (int)DRMSecurityLevel::SECURE_ONLY) { |
| crtc_security_level = SECURE_ONLY; |
| } |
| AddProperty(req, obj_id, prop_mgr_.GetPropertyId(DRMProperty::SECURITY_LEVEL), |
| crtc_security_level, true /* cache */, tmp_prop_val_map_); |
| } break; |
| |
| case DRMOps::CRTC_SET_SOLIDFILL_STAGES: { |
| uint64_t dim_stages = va_arg(args, uint64_t); |
| const std::vector<DRMSolidfillStage> *solid_fills = |
| reinterpret_cast <std::vector <DRMSolidfillStage> *> (dim_stages); |
| SetSolidfillStages(req, obj_id, solid_fills); |
| } break; |
| |
| case DRMOps::CRTC_SET_IDLE_TIMEOUT: { |
| uint32_t timeout_ms = va_arg(args, uint32_t); |
| AddProperty(req, obj_id, prop_mgr_.GetPropertyId(DRMProperty::IDLE_TIME), |
| timeout_ms, true /* cache */, tmp_prop_val_map_); |
| } break; |
| |
| case DRMOps::CRTC_SET_DEST_SCALER_CONFIG: { |
| uint32_t prop_id = prop_mgr_.GetPropertyId(DRMProperty::DEST_SCALER); |
| uint64_t dest_scaler = va_arg(args, uint64_t); |
| static sde_drm_dest_scaler_data dest_scale_copy = {}; |
| sde_drm_dest_scaler_data *ds_data = reinterpret_cast<sde_drm_dest_scaler_data *> |
| (dest_scaler); |
| dest_scale_copy = *ds_data; |
| AddProperty(req, obj_id, prop_id, |
| reinterpret_cast<uint64_t>(&dest_scale_copy), false /* cache */, |
| tmp_prop_val_map_); |
| } break; |
| |
| case DRMOps::CRTC_SET_CAPTURE_MODE: { |
| int capture_mode = va_arg(args, int); |
| uint32_t cwb_capture_mode = CAPTURE_MIXER_OUT; |
| if (capture_mode == (int)DRMCWbCaptureMode::DSPP_OUT) { |
| cwb_capture_mode = CAPTURE_DSPP_OUT; |
| } |
| uint32_t prop_id = prop_mgr_.GetPropertyId(DRMProperty::CAPTURE_MODE); |
| AddProperty(req, obj_id, prop_id, cwb_capture_mode, true /* cache */, tmp_prop_val_map_); |
| } break; |
| |
| case DRMOps::CRTC_SET_IDLE_PC_STATE: { |
| if (!prop_mgr_.IsPropertyAvailable(DRMProperty::IDLE_PC_STATE)) { |
| return; |
| } |
| int drm_idle_pc_state = va_arg(args, int); |
| uint32_t idle_pc_state = IDLE_PC_STATE_NONE; |
| switch (drm_idle_pc_state) { |
| case static_cast<int>(DRMIdlePCState::ENABLE): |
| idle_pc_state = IDLE_PC_STATE_ENABLE; |
| break; |
| case static_cast<int>(DRMIdlePCState::DISABLE): |
| idle_pc_state = IDLE_PC_STATE_DISABLE; |
| break; |
| default: |
| idle_pc_state = IDLE_PC_STATE_NONE; |
| break; |
| } |
| AddProperty(req, obj_id, prop_mgr_.GetPropertyId(DRMProperty::IDLE_PC_STATE), idle_pc_state, |
| true /* cache */, tmp_prop_val_map_); |
| DRM_LOGD("CRTC %d: Set idle_pc_state %d", obj_id, idle_pc_state); |
| }; break; |
| |
| default: |
| DRM_LOGE("Invalid opcode %d to set the property on crtc %d", code, obj_id); |
| break; |
| } |
| } |
| |
| void DRMCrtc::SetROI(drmModeAtomicReq *req, uint32_t obj_id, uint32_t num_roi, |
| DRMRect *crtc_rois) { |
| #ifdef SDE_MAX_ROI_V1 |
| if (num_roi > SDE_MAX_ROI_V1 || !prop_mgr_.IsPropertyAvailable(DRMProperty::ROI_V1)) { |
| return; |
| } |
| if (!num_roi || !crtc_rois) { |
| AddProperty(req, obj_id, prop_mgr_.GetPropertyId(DRMProperty::ROI_V1), |
| 0, false /* cache */, tmp_prop_val_map_); |
| DRM_LOGD("CRTC ROI is set to NULL to indicate full frame update"); |
| return; |
| } |
| static struct sde_drm_roi_v1 roi_v1 {}; |
| memset(&roi_v1, 0, sizeof(roi_v1)); |
| roi_v1.num_rects = num_roi; |
| |
| for (uint32_t i = 0; i < num_roi; i++) { |
| roi_v1.roi[i].x1 = crtc_rois[i].left; |
| roi_v1.roi[i].x2 = crtc_rois[i].right; |
| roi_v1.roi[i].y1 = crtc_rois[i].top; |
| roi_v1.roi[i].y2 = crtc_rois[i].bottom; |
| DRM_LOGD("CRTC %d, ROI[l,t,b,r][%d %d %d %d]", obj_id, |
| roi_v1.roi[i].x1, roi_v1.roi[i].y1, roi_v1.roi[i].x2, roi_v1.roi[i].y2); |
| } |
| AddProperty(req, obj_id, prop_mgr_.GetPropertyId(DRMProperty::ROI_V1), |
| reinterpret_cast<uint64_t>(&roi_v1), false /* cache */, tmp_prop_val_map_); |
| #endif |
| } |
| |
| void DRMCrtc::SetSolidfillStages(drmModeAtomicReq *req, uint32_t obj_id, |
| const std::vector<DRMSolidfillStage> *solid_fills) { |
| #if defined SDE_MAX_DIM_LAYERS |
| static struct sde_drm_dim_layer_v1 drm_dim_layer_v1 {}; |
| memset(&drm_dim_layer_v1, 0, sizeof(drm_dim_layer_v1)); |
| uint32_t shift; |
| |
| drm_dim_layer_v1.num_layers = solid_fills->size(); |
| for (uint32_t i = 0; i < solid_fills->size(); i++) { |
| const DRMSolidfillStage &sf = solid_fills->at(i); |
| float plane_alpha = (sf.plane_alpha / 255.0f); |
| drm_dim_layer_v1.layer_cfg[i].stage = sf.z_order; |
| drm_dim_layer_v1.layer_cfg[i].rect.x1 = (uint16_t)sf.bounding_rect.left; |
| drm_dim_layer_v1.layer_cfg[i].rect.y1 = (uint16_t)sf.bounding_rect.top; |
| drm_dim_layer_v1.layer_cfg[i].rect.x2 = (uint16_t)sf.bounding_rect.right; |
| drm_dim_layer_v1.layer_cfg[i].rect.y2 = (uint16_t)sf.bounding_rect.bottom; |
| drm_dim_layer_v1.layer_cfg[i].flags = |
| sf.is_exclusion_rect ? SDE_DRM_DIM_LAYER_EXCLUSIVE : SDE_DRM_DIM_LAYER_INCLUSIVE; |
| |
| // @sde_mdss_color: expects in [g b r a] order where as till now solidfill is in [a r g b]. |
| // As no support for passing plane alpha, Multiply Alpha color component with plane_alpa. |
| shift = kSolidFillHwBitDepth - sf.color_bit_depth; |
| drm_dim_layer_v1.layer_cfg[i].color_fill.color_0 = (sf.green & 0x3FF) << shift; |
| drm_dim_layer_v1.layer_cfg[i].color_fill.color_1 = (sf.blue & 0x3FF) << shift; |
| drm_dim_layer_v1.layer_cfg[i].color_fill.color_2 = (sf.red & 0x3FF) << shift; |
| // alpha is 8 bit |
| drm_dim_layer_v1.layer_cfg[i].color_fill.color_3 = |
| ((uint32_t)((((sf.alpha & 0xFF)) * plane_alpha))); |
| } |
| |
| AddProperty(req, obj_id, prop_mgr_.GetPropertyId(DRMProperty::DIM_STAGES_V1), |
| reinterpret_cast<uint64_t> (&drm_dim_layer_v1), false /* cache */, |
| tmp_prop_val_map_); |
| #endif |
| } |
| |
| void DRMCrtc::Dump() { |
| DRM_LOGE("id: %d\tbuffer_id: %d\tpos:(%d, %d)\tsize:(%dx%d)\n", drm_crtc_->crtc_id, |
| drm_crtc_->buffer_id, drm_crtc_->x, drm_crtc_->y, drm_crtc_->width, drm_crtc_->height); |
| } |
| |
| bool DRMCrtc::ConfigureScalerLUT(drmModeAtomicReq *req, uint32_t dir_lut_blob_id, |
| uint32_t cir_lut_blob_id, uint32_t sep_lut_blob_id) { |
| if (is_lut_configured_ && is_lut_validated_) { |
| return false; |
| } |
| if (dir_lut_blob_id) { |
| AddProperty(req, drm_crtc_->crtc_id, |
| prop_mgr_.GetPropertyId(DRMProperty::DS_LUT_ED), dir_lut_blob_id, |
| false /* cache */, tmp_prop_val_map_); |
| } |
| if (cir_lut_blob_id) { |
| AddProperty(req, drm_crtc_->crtc_id, |
| prop_mgr_.GetPropertyId(DRMProperty::DS_LUT_CIR), cir_lut_blob_id, |
| false /* cache */, tmp_prop_val_map_); |
| } |
| if (sep_lut_blob_id) { |
| AddProperty(req, drm_crtc_->crtc_id, |
| prop_mgr_.GetPropertyId(DRMProperty::DS_LUT_SEP), sep_lut_blob_id, |
| false /* cache */, tmp_prop_val_map_); |
| } |
| is_lut_validation_in_progress_ = true; |
| return true; |
| } |
| |
| void DRMCrtc::PostCommit(bool success) { |
| if (success) { |
| if (is_lut_validated_) { |
| is_lut_configured_ = true; |
| } |
| committed_prop_val_map_ = tmp_prop_val_map_; |
| } else { |
| tmp_prop_val_map_ = committed_prop_val_map_; |
| } |
| } |
| |
| void DRMCrtc::PostValidate(bool success) { |
| if (success && is_lut_validation_in_progress_) { |
| is_lut_validated_ = true; |
| } |
| |
| tmp_prop_val_map_ = committed_prop_val_map_; |
| } |
| |
| void DRMCrtc::ClearVotesCache() { |
| // On subsequent SET_ACTIVE 1, commit these to MDP driver and re-add to cache automatically |
| tmp_prop_val_map_.erase(prop_mgr_.GetPropertyId(DRMProperty::CORE_CLK)); |
| tmp_prop_val_map_.erase(prop_mgr_.GetPropertyId(DRMProperty::CORE_AB)); |
| tmp_prop_val_map_.erase(prop_mgr_.GetPropertyId(DRMProperty::CORE_IB)); |
| tmp_prop_val_map_.erase(prop_mgr_.GetPropertyId(DRMProperty::LLCC_AB)); |
| tmp_prop_val_map_.erase(prop_mgr_.GetPropertyId(DRMProperty::LLCC_IB)); |
| tmp_prop_val_map_.erase(prop_mgr_.GetPropertyId(DRMProperty::DRAM_AB)); |
| tmp_prop_val_map_.erase(prop_mgr_.GetPropertyId(DRMProperty::DRAM_IB)); |
| } |
| |
| } // namespace sde_drm |