blob: 1c53082a98b1c0d8540abffc1df58fb57b6d1ad5 [file] [log] [blame]
/*
* Copyright (c) 2019-2021, 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;
using std::pair;
using std::vector;
// 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;
}
if (!blob->data) {
return;
}
char *fmt_str = new char[blob->length + 1];
memcpy (fmt_str, blob->data, blob->length);
fmt_str[blob->length] = '\0';
stringstream stream(fmt_str);
DRM_LOGI("stream str %s len %zu blob str %s len %d", stream.str().c_str(), stream.str().length(),
blob->data, blob->length);
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 has_micro_idle = "has_uidle=";
string min_prefill_lines = "min_prefill_lines=";
string num_mnocports = "num_mnoc_ports=";
string mnoc_bus_width = "axi_bus_width=";
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=";
string vig = "vig=";
string dma = "dma=";
string scaling = "scale=";
string rotation = "inline_rot=";
string linewidth_constraints = "sspp_linewidth_usecases=";
string linewidth_values = "sspp_linewidth_values=";
string limit_constraint = "limit_usecase=";
string limit_value = "limit_value=";
string use_baselayer_for_stage = "use_baselayer_for_stage=";
string ubwc_version = "UBWC version=";
string rc_total_mem_size = "rc_mem_size=";
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()));
} else if (line.find(num_mnocports) != string::npos) {
crtc_info_.num_mnocports = std::stoi(string(line, num_mnocports.length()));
} else if (line.find(mnoc_bus_width) != string::npos) {
crtc_info_.mnoc_bus_width = std::stoi(string(line, mnoc_bus_width.length()));
} else if (line.find(linewidth_constraints) != string::npos) {
crtc_info_.line_width_constraints_count =
std::stoi(string(line, (linewidth_constraints).length()));
} else if (line.find(vig) != string::npos) {
crtc_info_.vig_limit_index = std::stoi(string(line, (vig).length()));
} else if (line.find(dma) != string::npos) {
crtc_info_.dma_limit_index = std::stoi(string(line, (dma).length()));
} else if (line.find(scaling) != string::npos) {
crtc_info_.scaling_limit_index = std::stoi(string(line, (scaling).length()));
} else if (line.find(rotation) != string::npos) {
crtc_info_.rotation_limit_index = std::stoi(string(line, (rotation).length()));
} else if (line.find(linewidth_values) != string::npos) {
uint32_t num_linewidth_values = std::stoi(string(line, (linewidth_values).length()));
vector< pair <uint32_t,uint32_t> > constraint_vector;
for (uint32_t i = 0; i < num_linewidth_values; i++) {
uint32_t constraint = 0;
uint32_t value = 0;
std::getline(stream, line);
if (line.find(limit_constraint) != string::npos) {
constraint = std::stoi(string(line, (limit_constraint).length()));
}
std::getline(stream, line);
if (line.find(limit_value) != string::npos) {
value = std::stoi(string(line, (limit_value).length()));
}
if (value) {
constraint_vector.push_back(std::make_pair(constraint,value));
}
}
crtc_info_.line_width_limits = std::move(constraint_vector);
} else if (line.find(has_micro_idle) != string::npos) {
crtc_info_.has_micro_idle = std::stoi(string(line, (has_micro_idle).length()));
} else if (line.find(use_baselayer_for_stage) != string::npos) {
crtc_info_.use_baselayer_for_stage =
std::stoi(string(line, use_baselayer_for_stage.length()));
} else if (line.find(ubwc_version) != string::npos) {
crtc_info_.ubwc_version = (std::stoi(string(line, ubwc_version.length()))) >> 28;
} else if (line.find(rc_total_mem_size) != string::npos) {
crtc_info_.rc_total_mem_size = std::stoi(string(line, rc_total_mem_size.length()));
}
}
drmModeFreePropertyBlob(blob);
delete[] fmt_str;
}
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