/* * Copyright (c) 2019-2020, 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 #include #include // The 3 headers above are a workaround to prevent kernel drm.h from being used that has the // "virtual" keyword used for a variable. In future replace libdrm version drm.h with kernel // version drm/drm.h #include #include #include #include #include #include #include #include #include #include #include "drm_utils.h" #include "drm_plane.h" #include "drm_property.h" namespace sde_drm { using std::string; using std::pair; using std::vector; using std::unique_ptr; using std::tuple; using std::stringstream; using std::mutex; using std::lock_guard; #define MAX_SCALER_LINEWIDTH 2560 static struct sde_drm_csc_v1 csc_10bit_convert[kCscTypeMax] = { [kCscYuv2Rgb601L] = { { 0x12A000000, 0x000000000, 0x198800000, 0x12A000000, 0x7F9B800000, 0x7F30000000, 0x12A000000, 0x204800000, 0x000000000, }, { 0xffc0, 0xfe00, 0xfe00,}, { 0x0, 0x0, 0x0,}, { 0x40, 0x3ac, 0x40, 0x3c0, 0x40, 0x3c0,}, { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,}, }, [kCscYuv2Rgb601FR] = { { 0x100000000, 0x0, 0x167000000, 0x100000000, 0x7fa8000000, 0x7f49000000, 0x100000000, 0x1c5800000, 0x0, }, { 0x0000, 0xfe00, 0xfe00,}, { 0x0, 0x0, 0x0,}, { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,}, { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,}, }, [kCscYuv2Rgb709L] = { { 0x12a000000, 0x0, 0x1cb000000, 0x12a000000, 0x7fc9800000, 0x7f77800000, 0x12a000000, 0x21d000000, 0x0, }, { 0xffc0, 0xfe00, 0xfe00,}, { 0x0, 0x0, 0x0,}, { 0x40, 0x3ac, 0x40, 0x3c0, 0x40, 0x3c0,}, { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,}, }, [kCscYuv2Rgb2020L] = { { 0x12b000000, 0x0, 0x1af000000, 0x12b000000, 0x7fd0000000, 0x7f59000000, 0x12b000000, 0x226000000, 0x0, }, { 0xffc0, 0xfe00, 0xfe00,}, { 0x0, 0x0, 0x0,}, { 0x40, 0x3ac, 0x40, 0x3c0, 0x40, 0x3c0,}, { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,}, }, [kCscYuv2Rgb2020FR] = { { 0x100000000, 0x0, 0x179800000, 0x100000000, 0x7fd6000000, 0x7f6d800000, 0x100000000, 0x1e1800000, 0x0, }, { 0x0000, 0xfe00, 0xfe00,}, { 0x0, 0x0, 0x0,}, { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,}, { 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,}, }, }; static uint8_t REFLECT_X = 0; static uint8_t REFLECT_Y = 0; static uint8_t ROTATE_90 = 0; static uint8_t ROTATE_0 = 0; // FB Secure Modes static uint8_t NON_SECURE = 0; static uint8_t SECURE = 1; static uint8_t NON_SECURE_DIR_TRANSLATION = 2; static uint8_t SECURE_DIR_TRANSLATION = 3; // Multi rect modes static uint8_t MULTIRECT_NONE = 0; static uint8_t MULTIRECT_PARALLEL = 1; static uint8_t MULTIRECT_SERIAL = 2; static void SetRect(DRMRect &source, drm_clip_rect *target) { target->x1 = uint16_t(source.left); target->y1 = uint16_t(source.top); target->x2 = uint16_t(source.right); target->y2 = uint16_t(source.bottom); } static void PopulateReflect(drmModePropertyRes *prop) { if (REFLECT_X) { return; } if (!drm_property_type_is(prop, DRM_MODE_PROP_BITMASK)) { return; } for (auto i = 0; i < prop->count_enums; i++) { string enum_name(prop->enums[i].name); if (enum_name == "reflect-x") { REFLECT_X = prop->enums[i].value; } else if (enum_name == "reflect-y") { REFLECT_Y = prop->enums[i].value; } else if (enum_name == "rotate-90") { ROTATE_90 = prop->enums[i].value; } else if (enum_name == "rotate-0") { ROTATE_0 = prop->enums[i].value; } } } static void PopulateSecureModes(drmModePropertyRes *prop) { static bool secure_modes_populated = false; if (!secure_modes_populated) { for (auto i = 0; i < prop->count_enums; i++) { string enum_name(prop->enums[i].name); if (enum_name == "non_sec") { NON_SECURE = prop->enums[i].value; } else if (enum_name == "sec") { SECURE = prop->enums[i].value; } else if (enum_name == "non_sec_direct_translation") { NON_SECURE_DIR_TRANSLATION = prop->enums[i].value; } else if (enum_name == "sec_direct_translation") { SECURE_DIR_TRANSLATION = prop->enums[i].value; } } secure_modes_populated = true; } } static InlineRotationVersion PopulateInlineRotationVersion(uint32_t ver) { switch (ver) { case 0x0000: return InlineRotationVersion::kInlineRotationNone; case 0x0001: case 0x0100: return InlineRotationVersion::kInlineRotationV1; case 0x0200: return InlineRotationVersion::kInlineRotationV2; default: return InlineRotationVersion::kInlineRotationNone; } } static QSEEDStepVersion PopulateQseedStepVersion(uint32_t hw_ver) { switch (hw_ver) { case 0x1003: return QSEEDStepVersion::V3; case 0x1004: return QSEEDStepVersion::V4; case 0x2004: return QSEEDStepVersion::V3LITE_V4; case 0x3000: return QSEEDStepVersion::V3LITE_V5; // default value. also corresponds to (hw_ver == 0x1002) default: return QSEEDStepVersion::V2; } } static void PopulateMultiRectModes(drmModePropertyRes *prop) { static bool multirect_modes_populated = false; if (!multirect_modes_populated) { for (auto i = 0; i < prop->count_enums; i++) { string enum_name(prop->enums[i].name); if (enum_name == "none") { MULTIRECT_NONE = prop->enums[i].value; } else if (enum_name == "parallel") { MULTIRECT_PARALLEL = prop->enums[i].value; } else if (enum_name == "serial") { MULTIRECT_SERIAL = prop->enums[i].value; } } multirect_modes_populated = true; } } static const char *GetColorLutString(DRMTonemapLutType lut_type) { switch (lut_type) { case DRMTonemapLutType::DMA_1D_IGC: return "DMA IGC"; case DRMTonemapLutType::DMA_1D_GC: return "DMA GC"; case DRMTonemapLutType::VIG_1D_IGC: return "VIG IGC"; case DRMTonemapLutType::VIG_3D_GAMUT: return "VIG 3D"; default: return "Unknown Lut"; } } #define __CLASS__ "DRMPlaneManager" static bool GetDRMonemapLutTypeFromPPFeatureID(DRMPPFeatureID id, DRMTonemapLutType *lut_type) { switch (id) { case kFeatureDgmIgc: *lut_type = DRMTonemapLutType::DMA_1D_IGC; break; case kFeatureDgmGc: *lut_type = DRMTonemapLutType::DMA_1D_GC; break; case kFeatureVigIgc: *lut_type = DRMTonemapLutType::VIG_1D_IGC; break; case kFeatureVigGamut: *lut_type = DRMTonemapLutType::VIG_3D_GAMUT; break; default: DRM_LOGE("Invalid DRMPPFeature id = %d", id); return false; } return true; } DRMPlaneManager::DRMPlaneManager(int fd) : fd_(fd) {} void DRMPlaneManager::Init() { drmModePlaneRes *resource = drmModeGetPlaneResources(fd_); if (!resource) { return; } for (uint32_t i = 0; i < resource->count_planes; i++) { // The enumeration order itself is the priority from high to low unique_ptr plane(new DRMPlane(fd_, i)); drmModePlane *libdrm_plane = drmModeGetPlane(fd_, resource->planes[i]); if (libdrm_plane) { plane->InitAndParse(libdrm_plane); object_pool_[resource->planes[i]] = std::move(plane); } else { DRM_LOGE("Critical error: drmModeGetPlane() failed for plane %d.", resource->planes[i]); } } drmModeFreePlaneResources(resource); } void DRMPlaneManager::Perform(DRMOps code, uint32_t obj_id, drmModeAtomicReq *req, va_list args) { auto plane = GetObject(obj_id); if (plane == nullptr) { DRM_LOGE("Invalid plane id %d", obj_id); return; } if (code == DRMOps::PLANE_SET_SCALER_CONFIG) { if (plane->ConfigureScalerLUT(dir_lut_blob_id_, cir_lut_blob_id_, sep_lut_blob_id_)) { DRM_LOGD("Plane %d: Configuring scaler LUTs", obj_id); } } plane->Perform(code, req, args); } void DRMPlaneManager::Perform(DRMOps code, drmModeAtomicReq *req, uint32_t obj_id, ...) { lock_guard lock(lock_); va_list args; va_start(args, obj_id); Perform(code, obj_id, req, args); va_end(args); } void DRMPlaneManager::GetPlanesInfo(DRMPlanesInfo *info) { for (auto &plane : object_pool_) { info->push_back(std::make_pair(plane.first, plane.second->GetPlaneTypeInfo())); } } void DRMPlaneManager::UnsetUnusedResources(uint32_t crtc_id, bool is_commit, drmModeAtomicReq *req) { // Unset planes that were assigned to the crtc referred to by crtc_id but are not requested // in this round lock_guard lock(lock_); for (auto &plane : object_pool_) { uint32_t assigned_crtc = 0; uint32_t requested_crtc = 0; plane.second->GetAssignedCrtc(&assigned_crtc); plane.second->GetRequestedCrtc(&requested_crtc); if (assigned_crtc == crtc_id && requested_crtc == 0) { plane.second->Unset(is_commit, req); } else if (requested_crtc == crtc_id) { // Plane is acquired, call reset color luts, which will reset if needed plane.second->ResetColorLUTs(is_commit, req); } } } void DRMPlaneManager::RetainPlanes(uint32_t crtc_id) { for (auto &plane : object_pool_) { uint32_t assigned_crtc = 0; plane.second->GetAssignedCrtc(&assigned_crtc); if (assigned_crtc == crtc_id) { // Pretend this plane was requested by client plane.second->SetRequestedCrtc(crtc_id); const uint32_t plane_id = plane.first; DRM_LOGD("Plane %d: Retaining on CRTC %d", plane_id, crtc_id); } } } void DRMPlaneManager::PostValidate(uint32_t crtc_id) { lock_guard lock(lock_); for (auto &plane : object_pool_) { plane.second->PostValidate(crtc_id); } } void DRMPlaneManager::PostCommit(uint32_t crtc_id, bool success) { lock_guard lock(lock_); DRM_LOGD("crtc %d", crtc_id); for (auto &plane : object_pool_) { plane.second->PostCommit(crtc_id, success); } } void DRMPlaneManager::SetScalerLUT(const DRMScalerLUTInfo &lut_info) { if (lut_info.dir_lut_size) { drmModeCreatePropertyBlob(fd_, reinterpret_cast(lut_info.dir_lut), lut_info.dir_lut_size, &dir_lut_blob_id_); } if (lut_info.cir_lut_size) { drmModeCreatePropertyBlob(fd_, reinterpret_cast(lut_info.cir_lut), lut_info.cir_lut_size, &cir_lut_blob_id_); } if (lut_info.sep_lut_size) { drmModeCreatePropertyBlob(fd_, reinterpret_cast(lut_info.sep_lut), lut_info.sep_lut_size, &sep_lut_blob_id_); } } void DRMPlaneManager::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; } } // ==============================================================================================// #undef __CLASS__ #define __CLASS__ "DRMPlane" DRMPlane::DRMPlane(int fd, uint32_t priority) : DRMObject(prop_mgr_), fd_(fd), priority_(priority) {} DRMPlane::~DRMPlane() { drmModeFreePlane(drm_plane_); } void DRMPlane::GetTypeInfo(const PropertyMap &prop_map) { uint64_t blob_id = 0; drmModePropertyRes *prop = nullptr; DRMPlaneTypeInfo *info = &plane_type_info_; // Ideally we should check if this property type is a blob and then proceed. std::tie(blob_id, prop) = prop_map.at(DRMProperty::CAPABILITIES); 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'; info->max_linewidth = 2560; info->max_scaler_linewidth = MAX_SCALER_LINEWIDTH; info->max_upscale = 1; info->max_downscale = 1; info->max_horizontal_deci = 0; info->max_vertical_deci = 0; info->master_plane_id = 0; if (info->type == DRMPlaneType::CURSOR) { info->max_linewidth = 128; } // TODO(user): change default to V2 once we start getting V3 via capabilities blob info->qseed3_version = QSEEDStepVersion::V3; info->has_excl_rect = has_excl_rect_; // We may have multiple lines with each one dedicated for something specific // like formats etc stringstream stream(fmt_str); DRM_LOGI("stream str %s len %zu blob str %s len %d", stream.str().c_str(), stream.str().length(), static_cast(blob->data), blob->length); string line = {}; string pixel_formats = "pixel_formats="; string max_linewidth = "max_linewidth="; string max_upscale = "max_upscale="; string max_downscale = "max_downscale="; string max_horizontal_deci = "max_horizontal_deci="; string max_vertical_deci = "max_vertical_deci="; string master_plane_id = "primary_smart_plane_id="; string max_pipe_bw = "max_per_pipe_bw="; string max_pipe_bw_high = "max_per_pipe_bw_high="; string scaler_version = "scaler_step_ver="; string block_sec_ui = "block_sec_ui="; string true_inline_rot_rev = "true_inline_rot_rev="; string inline_rot_pixel_formats = "inline_rot_pixel_formats="; string true_inline_dwnscale_rt_numerator = "true_inline_dwnscale_rt_numerator="; string true_inline_dwnscale_rt_denominator = "true_inline_dwnscale_rt_denominator="; string true_inline_max_height = "true_inline_max_height="; while (std::getline(stream, line)) { if (line.find(inline_rot_pixel_formats) != string::npos) { vector> inrot_formats_supported; ParseFormats(line.erase(0, inline_rot_pixel_formats.length()), &inrot_formats_supported); info->inrot_fmts_supported = std::move(inrot_formats_supported); } else if (line.find(pixel_formats) != string::npos) { vector> formats_supported; ParseFormats(line.erase(0, pixel_formats.length()), &formats_supported); info->formats_supported = std::move(formats_supported); } else if (line.find(max_linewidth) != string::npos) { info->max_linewidth = std::stoi(line.erase(0, max_linewidth.length())); } else if (line.find(max_upscale) != string::npos) { info->max_upscale = std::stoi(line.erase(0, max_upscale.length())); } else if (line.find(max_downscale) != string::npos) { info->max_downscale = std::stoi(line.erase(0, max_downscale.length())); } else if (line.find(max_horizontal_deci) != string::npos) { info->max_horizontal_deci = std::stoi(line.erase(0, max_horizontal_deci.length())); } else if (line.find(max_vertical_deci) != string::npos) { info->max_vertical_deci = std::stoi(line.erase(0, max_vertical_deci.length())); } else if (line.find(master_plane_id) != string::npos) { info->master_plane_id = std::stoi(line.erase(0, master_plane_id.length())); DRM_LOGI("info->master_plane_id: detected master_plane=%d", info->master_plane_id); } else if (line.find(max_pipe_bw) != string::npos) { info->max_pipe_bandwidth = std::stoull(line.erase(0, max_pipe_bw.length())); } else if (line.find(max_pipe_bw_high) != string::npos) { info->max_pipe_bandwidth_high = std::stoull(line.erase(0, max_pipe_bw_high.length())); } else if (line.find(scaler_version) != string::npos) { info->qseed3_version = PopulateQseedStepVersion(std::stoi(line.erase(0, scaler_version.length()))); } else if (line.find(block_sec_ui) != string::npos) { info->block_sec_ui = !!(std::stoi(line.erase(0, block_sec_ui.length()))); } else if (line.find(true_inline_rot_rev) != string::npos) { info->inrot_version = PopulateInlineRotationVersion(std::stoi(line.erase(0, true_inline_rot_rev.length()))); } else if (line.find(true_inline_dwnscale_rt_numerator) != string::npos) { info->true_inline_dwnscale_rt_num = std::stof(line.erase(0, true_inline_dwnscale_rt_numerator.length())); } else if (line.find(true_inline_dwnscale_rt_denominator) != string::npos) { info->true_inline_dwnscale_rt_denom = std::stof(line.erase(0, true_inline_dwnscale_rt_denominator.length())); } else if (line.find(true_inline_max_height) != string::npos) { info->max_rotation_linewidth = std::stoi(line.erase(0, true_inline_max_height.length())); } } // TODO(user): Get max_scaler_linewidth and non_scaler_linewidth from driver // max_linewidth can be smaller than 2560 for few target, so make sure to assign the minimum of both info->max_scaler_linewidth = (info->qseed3_version < QSEEDStepVersion::V4) ? info->max_linewidth : std::min((uint32_t)MAX_SCALER_LINEWIDTH, info->max_linewidth); drmModeFreePropertyBlob(blob); delete[] fmt_str; } void DRMPlane::ParseProperties() { // Map of property name to current value and property info pointer PropertyMap prop_map; bool csc = false; bool scaler = false; bool cursor = false; drmModeObjectProperties *props = drmModeObjectGetProperties(fd_, drm_plane_->plane_id, DRM_MODE_OBJECT_PLANE); 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::EXCL_RECT) { has_excl_rect_ = true; } if (prop_enum == DRMProperty::ROTATION) { PopulateReflect(info); } else if (prop_enum == DRMProperty::FB_TRANSLATION_MODE) { PopulateSecureModes(info); } else if (prop_enum == DRMProperty::MULTIRECT_MODE) { PopulateMultiRectModes(info); plane_type_info_.multirect_prop_present = true; } prop_mgr_.SetPropertyId(prop_enum, info->prop_id); prop_map[prop_enum] = std::make_tuple(props->prop_values[j], info); csc = prop_enum == DRMProperty::CSC_V1 ? true : csc; scaler = (prop_enum == DRMProperty::SCALER_V1 || prop_enum == DRMProperty::SCALER_V2) \ ? true : scaler; cursor = (prop_enum == DRMProperty::TYPE && props->prop_values[j] == DRM_PLANE_TYPE_CURSOR) \ ? true : cursor; // Tone mapping properties. if (prop_enum == DRMProperty::INVERSE_PMA) { plane_type_info_.inverse_pma = true; } if ((uint32_t)prop_enum >= (uint32_t)DRMProperty::CSC_DMA_V1 && (uint32_t)prop_enum <= (uint32_t)DRMProperty::CSC_DMA_V1) { plane_type_info_.dgm_csc_version = ((uint32_t)prop_enum - (uint32_t)DRMProperty::CSC_DMA_V1 + 1); } if ((uint32_t)prop_enum >= (uint32_t)DRMProperty::SDE_DGM_1D_LUT_IGC_V5 && (uint32_t)prop_enum <= (uint32_t)DRMProperty::SDE_DGM_1D_LUT_IGC_V5) { plane_type_info_.tonemap_lut_version_map[DRMTonemapLutType::DMA_1D_IGC] = ((uint32_t)prop_enum - (uint32_t)DRMProperty::SDE_DGM_1D_LUT_IGC_V5 + 5); } if ((uint32_t)prop_enum >= (uint32_t)DRMProperty::SDE_DGM_1D_LUT_GC_V5 && (uint32_t)prop_enum <= (uint32_t)DRMProperty::SDE_DGM_1D_LUT_GC_V5) { plane_type_info_.tonemap_lut_version_map[DRMTonemapLutType::DMA_1D_GC] = ((uint32_t)prop_enum - (uint32_t)DRMProperty::SDE_DGM_1D_LUT_GC_V5 + 5); } if ((uint32_t)prop_enum >= (uint32_t)DRMProperty::SDE_VIG_1D_LUT_IGC_V5 && (uint32_t)prop_enum <= (uint32_t)DRMProperty::SDE_VIG_1D_LUT_IGC_V6) { plane_type_info_.tonemap_lut_version_map[DRMTonemapLutType::VIG_1D_IGC] = ((uint32_t)prop_enum - (uint32_t)DRMProperty::SDE_VIG_1D_LUT_IGC_V5 + 5); } if ((uint32_t)prop_enum >= (uint32_t)DRMProperty::SDE_VIG_3D_LUT_GAMUT_V5 && (uint32_t)prop_enum <= (uint32_t)DRMProperty::SDE_VIG_3D_LUT_GAMUT_V6) { plane_type_info_.tonemap_lut_version_map[DRMTonemapLutType::VIG_3D_GAMUT] = ((uint32_t)prop_enum - (uint32_t)DRMProperty::SDE_VIG_3D_LUT_GAMUT_V5 + 5); } } DRMPlaneType type = DRMPlaneType::DMA; if (csc && scaler) { type = DRMPlaneType::VIG; } else if (cursor) { type = DRMPlaneType::CURSOR; } plane_type_info_.type = type; GetTypeInfo(prop_map); for (auto &prop : prop_map) { drmModeFreeProperty(std::get<1>(prop.second)); } drmModeFreeObjectProperties(props); } void DRMPlane::InitAndParse(drmModePlane *plane) { drm_plane_ = plane; ParseProperties(); unique_ptr pp_mgr(new DRMPPManager(fd_)); pp_mgr_ = std::move(pp_mgr); pp_mgr_->Init(prop_mgr_, DRM_MODE_OBJECT_PLANE); } bool DRMPlane::ConfigureScalerLUT(uint32_t dir_lut_blob_id, uint32_t cir_lut_blob_id, uint32_t sep_lut_blob_id) { if (plane_type_info_.type != DRMPlaneType::VIG || is_lut_configured_) { return false; } if (dir_lut_blob_id) { AddProperty(DRMProperty::LUT_ED, dir_lut_blob_id, true); } if (cir_lut_blob_id) { AddProperty(DRMProperty::LUT_CIR, cir_lut_blob_id, true); } if (sep_lut_blob_id) { AddProperty(DRMProperty::LUT_SEP, sep_lut_blob_id, true); } return true; } void DRMPlane::SetExclRect(DRMRect rect) { drm_clip_rect clip_rect; SetRect(rect, &clip_rect); excl_rect_copy_ = clip_rect; AddProperty(DRMProperty::EXCL_RECT, reinterpret_cast(&excl_rect_copy_), true); DRM_LOGD("Plane %d: Setting exclusion rect [x,y,w,h][%d,%d,%d,%d]", drm_plane_->plane_id, clip_rect.x1, clip_rect.y1, (clip_rect.x2 - clip_rect.x1), (clip_rect.y2 - clip_rect.y1)); } bool DRMPlane::SetCscConfig(DRMCscType csc_type) { if (plane_type_info_.type != DRMPlaneType::VIG) { return false; } if (csc_type > kCscTypeMax) { return false; } if (!prop_mgr_.IsPropertyAvailable(DRMProperty::CSC_V1)) { return false; } if (csc_type == kCscTypeMax) { AddProperty(DRMProperty::CSC_V1, 0); } else { csc_config_copy_ = csc_10bit_convert[csc_type]; AddProperty(DRMProperty::CSC_V1, reinterpret_cast(&csc_config_copy_), true); } return true; } bool DRMPlane::SetScalerConfig(uint64_t handle) { if (plane_type_info_.type != DRMPlaneType::VIG) { return false; } if (prop_mgr_.IsPropertyAvailable(DRMProperty::SCALER_V2)) { sde_drm_scaler_v2 *scaler_v2_config = reinterpret_cast(handle); uint64_t scaler_data = 0; // The address needs to be valid even after async commit, since we are sending address to // driver directly, instead of blob. So we need to copy over contents that client sent. Client // may have sent an address of object on stack which will be released after this call. scaler_v2_config_copy_ = *scaler_v2_config; if (scaler_v2_config_copy_.enable) { scaler_data = reinterpret_cast(&scaler_v2_config_copy_); } AddProperty(DRMProperty::SCALER_V2, scaler_data, scaler_data != 0); return true; } return false; } void DRMPlane::SetDecimation(DRMProperty prop, uint32_t prop_value) { if (plane_type_info_.type == DRMPlaneType::DMA || plane_type_info_.master_plane_id) { // if value is 0, client is just trying to clear previous decimation, so bail out silently if (prop_value > 0) { DRM_LOGE("Plane %d: Setting decimation %d is not supported.", drm_plane_->plane_id, prop_value); } return; } // TODO(user): Currently a ViG plane in smart DMA mode could receive a non-zero decimation value // but there is no good way to catch. In any case fix will be in client AddProperty(prop, prop_value); DRM_LOGD("Plane %d: Setting decimation %d", drm_plane_->plane_id, prop_value); } void DRMPlane::PostValidate(uint32_t crtc_id) { DiscardDirtyProperties(); if (requested_crtc_id_ == crtc_id) { SetRequestedCrtc(0); } } void DRMPlane::PostCommit(uint32_t crtc_id, bool success) { DRM_LOGD("crtc %d", crtc_id); if (!success) { // To reset PostValidate(crtc_id); return; } uint32_t assigned_crtc = 0; uint32_t requested_crtc = 0; CommitProperties(); GetAssignedCrtc(&assigned_crtc); GetRequestedCrtc(&requested_crtc); // In future, it is possible that plane is already attached in case of continuous splash. This // will cause the first commit to only unstage pipes. We want to mark luts as configured only // when they really are, which typically happens if a crtc is requested for a plane if (requested_crtc == crtc_id && !is_lut_configured_) { is_lut_configured_ = true; } if (requested_crtc && assigned_crtc && requested_crtc != assigned_crtc) { // We should never be here DRM_LOGE("Found plane %d switching from crtc %d to crtc %d", drm_plane_->plane_id, assigned_crtc, requested_crtc); } // If we have set a pipe OR unset a pipe during commit, update states if (requested_crtc == crtc_id || assigned_crtc == crtc_id) { SetAssignedCrtc(requested_crtc); SetRequestedCrtc(0); } } void DRMPlane::Perform(DRMOps code, drmModeAtomicReq *req, va_list args) { uint32_t obj_id = drm_plane_->plane_id; switch (code) { // TODO(user): Check if these exist in map before attempting to access case DRMOps::PLANE_SET_SRC_RECT: { DRMRect rect = va_arg(args, DRMRect); // source co-ordinates accepted by DRM are 16.16 fixed point AddProperty(DRMProperty::SRC_X, rect.left << 16); AddProperty(DRMProperty::SRC_Y, rect.top << 16); AddProperty(DRMProperty::SRC_W, (rect.right - rect.left) << 16); AddProperty(DRMProperty::SRC_H, (rect.bottom - rect.top) << 16); DRM_LOGV("Plane %d: Setting crop [x,y,w,h][%d,%d,%d,%d]", obj_id, rect.left, rect.top, (rect.right - rect.left), (rect.bottom - rect.top)); } break; case DRMOps::PLANE_SET_DST_RECT: { DRMRect rect = va_arg(args, DRMRect); AddProperty(DRMProperty::CRTC_X, rect.left); AddProperty(DRMProperty::CRTC_Y, rect.top); AddProperty(DRMProperty::CRTC_W, (rect.right - rect.left)); AddProperty(DRMProperty::CRTC_H, (rect.bottom - rect.top)); DRM_LOGV("Plane %d: Setting dst [x,y,w,h][%d,%d,%d,%d]", obj_id, rect.left, rect.top, (rect.right - rect.left), (rect.bottom - rect.top)); } break; case DRMOps::PLANE_SET_EXCL_RECT: { DRMRect excl_rect = va_arg(args, DRMRect); SetExclRect(excl_rect); } break; case DRMOps::PLANE_SET_ZORDER: { uint32_t zpos = va_arg(args, uint32_t); AddProperty(DRMProperty::ZPOS, zpos); DRM_LOGD("Plane %d: Setting z %d", obj_id, zpos); } break; case DRMOps::PLANE_SET_ROTATION: { uint32_t rot_bit_mask = va_arg(args, uint32_t); uint32_t drm_rot_bit_mask = 0; if (rot_bit_mask & static_cast(DRMRotation::FLIP_H)) { drm_rot_bit_mask |= 1 << REFLECT_X; } if (rot_bit_mask & static_cast(DRMRotation::FLIP_V)) { drm_rot_bit_mask |= 1 << REFLECT_Y; } if (rot_bit_mask & static_cast(DRMRotation::ROT_90)) { drm_rot_bit_mask |= 1 << ROTATE_90; } else { drm_rot_bit_mask |= 1 << ROTATE_0; } AddProperty(DRMProperty::ROTATION, drm_rot_bit_mask); DRM_LOGV("Plane %d: Setting rotation mask %x", obj_id, drm_rot_bit_mask); } break; case DRMOps::PLANE_SET_ALPHA: { uint32_t alpha = va_arg(args, uint32_t); AddProperty(DRMProperty::ALPHA, alpha); DRM_LOGV("Plane %d: Setting alpha %d", obj_id, alpha); } break; case DRMOps::PLANE_SET_BLEND_TYPE: { uint32_t blending = va_arg(args, uint32_t); AddProperty(DRMProperty::BLEND_OP, blending); DRM_LOGV("Plane %d: Setting blending %d", obj_id, blending); } break; case DRMOps::PLANE_SET_H_DECIMATION: { uint32_t deci = va_arg(args, uint32_t); SetDecimation(DRMProperty::H_DECIMATE, deci); } break; case DRMOps::PLANE_SET_V_DECIMATION: { uint32_t deci = va_arg(args, uint32_t); SetDecimation(DRMProperty::V_DECIMATE, deci); } break; case DRMOps::PLANE_SET_SRC_CONFIG: { bool src_config = va_arg(args, uint32_t); AddProperty(DRMProperty::SRC_CONFIG, src_config); DRM_LOGV("Plane %d: Setting src_config flags-%x", obj_id, src_config); } break; case DRMOps::PLANE_SET_CRTC: { uint32_t crtc_id = va_arg(args, uint32_t); AddProperty(DRMProperty::CRTC_ID, crtc_id); SetRequestedCrtc(crtc_id); DRM_LOGV("Plane %d: Setting crtc %d", obj_id, crtc_id); } break; case DRMOps::PLANE_SET_FB_ID: { uint32_t fb_id = va_arg(args, uint32_t); AddProperty(DRMProperty::FB_ID, fb_id); DRM_LOGV("Plane %d: Setting fb_id %d", obj_id, fb_id); } break; case DRMOps::PLANE_SET_ROT_FB_ID: { uint32_t fb_id = va_arg(args, uint32_t); AddProperty(DRMProperty::ROT_FB_ID, fb_id, true); DRM_LOGV("Plane %d: Setting rot_fb_id %d", obj_id, fb_id); } break; case DRMOps::PLANE_SET_INPUT_FENCE: { int fence = va_arg(args, int); AddProperty(DRMProperty::INPUT_FENCE, fence, true); DRM_LOGV("Plane %d: Setting input fence %d", obj_id, fence); } break; case DRMOps::PLANE_SET_SCALER_CONFIG: { uint64_t handle = va_arg(args, uint64_t); if (SetScalerConfig(handle)) { DRM_LOGV("Plane %d: Setting scaler config", obj_id); } } break; case DRMOps::PLANE_SET_FB_SECURE_MODE: { int secure_mode = va_arg(args, int); uint32_t fb_secure_mode = NON_SECURE; switch (secure_mode) { case (int)DRMSecureMode::NON_SECURE: fb_secure_mode = NON_SECURE; break; case (int)DRMSecureMode::SECURE: fb_secure_mode = SECURE; break; case (int)DRMSecureMode::NON_SECURE_DIR_TRANSLATION: fb_secure_mode = NON_SECURE_DIR_TRANSLATION; break; case (int)DRMSecureMode::SECURE_DIR_TRANSLATION: fb_secure_mode = SECURE_DIR_TRANSLATION; break; default: DRM_LOGE("Invalid secure mode %d to set on plane %d", secure_mode, obj_id); break; } AddProperty(DRMProperty::FB_TRANSLATION_MODE, fb_secure_mode); DRM_LOGD("Plane %d: Setting FB secure mode %d", obj_id, fb_secure_mode); } break; case DRMOps::PLANE_SET_CSC_CONFIG: { uint32_t* csc_type = va_arg(args, uint32_t*); if (csc_type) { SetCscConfig((DRMCscType)*csc_type); } } break; case DRMOps::PLANE_SET_MULTIRECT_MODE: { DRMMultiRectMode drm_multirect_mode = (DRMMultiRectMode)va_arg(args, uint32_t); SetMultiRectMode(drm_multirect_mode); } break; case DRMOps::PLANE_SET_INVERSE_PMA: { uint32_t pma = va_arg(args, uint32_t); AddProperty(DRMProperty::INVERSE_PMA, pma); DRM_LOGD("Plane %d: %s inverse pma", obj_id, pma ? "Setting" : "Resetting"); } break; case DRMOps::PLANE_SET_DGM_CSC_CONFIG: { uint64_t handle = va_arg(args, uint64_t); if (SetDgmCscConfig(handle)) { DRM_LOGD("Plane %d: Setting Csc Lut config", obj_id); } } break; case DRMOps::PLANE_SET_POST_PROC: { DRMPPFeatureInfo *data = va_arg(args, DRMPPFeatureInfo*); if (data) { DRM_LOGD("Plane %d: Set post proc feature id - %d", obj_id, data->id); pp_mgr_->SetPPFeature(req, obj_id, *data); UpdatePPLutFeatureInuse(data); } } break; case DRMOps::PLANE_SET_SSPP_LAYOUT: { if (!prop_mgr_.IsPropertyAvailable(DRMProperty::SDE_SSPP_LAYOUT)) { DRM_LOGD("SSPP_LAYOUT property isn't exposed"); break; } DRMSSPPLayoutIndex layout_index = (DRMSSPPLayoutIndex) va_arg(args, uint32_t); AddProperty(DRMProperty::SDE_SSPP_LAYOUT, (uint32_t)layout_index); DRM_LOGD("Plane %d: Setting SSPP Layout to %d", obj_id, layout_index); } break; default: DRM_LOGE("Invalid opcode %d for DRM Plane %d", code, obj_id); } } void DRMPlane::UpdatePPLutFeatureInuse(DRMPPFeatureInfo *data) { DRMTonemapLutType lut_type = {}; bool ret = GetDRMonemapLutTypeFromPPFeatureID(data->id, &lut_type); if (ret == false) { DRM_LOGE("Failed to get the lut type from PPFeatureID = %d", data->id); return; } const auto state = data->payload ? kActive : kInactive; switch (lut_type) { case DRMTonemapLutType::DMA_1D_GC: dgm_1d_lut_gc_state_ = state; break; case DRMTonemapLutType::DMA_1D_IGC: dgm_1d_lut_igc_state_ = state; break; case DRMTonemapLutType::VIG_1D_IGC: vig_1d_lut_igc_state_ = state; break; case DRMTonemapLutType::VIG_3D_GAMUT: vig_3d_lut_gamut_state_ = state; break; default: DRM_LOGE("Invalid lut_type = %d state = %d", lut_type, state); } return; } void DRMPlane::PerformWrapper(DRMOps code, drmModeAtomicReq *req, ...) { va_list args; va_start(args, req); Perform(code, req, args); va_end(args); } void DRMPlane::Dump() { DRM_LOGE( "id: %d\tcrtc id: %d\tfb id: %d\tCRTC_xy: %dx%d\txy: %dx%d\tgamma " "size: %d\tpossible crtc: 0x%x\n", drm_plane_->plane_id, drm_plane_->crtc_id, drm_plane_->fb_id, drm_plane_->crtc_x, drm_plane_->crtc_y, drm_plane_->x, drm_plane_->y, drm_plane_->gamma_size, drm_plane_->possible_crtcs); DRM_LOGE("Format Suported: \n"); for (uint32_t i = 0; i < (uint32_t)drm_plane_->count_formats; i++) DRM_LOGE(" %4.4s", (char *)&drm_plane_->formats[i]); } void DRMPlane::SetMultiRectMode(DRMMultiRectMode drm_multirect_mode) { if (!plane_type_info_.multirect_prop_present) { return; } uint32_t obj_id = drm_plane_->plane_id; uint32_t multirect_mode = MULTIRECT_NONE; switch (drm_multirect_mode) { case DRMMultiRectMode::NONE: multirect_mode = MULTIRECT_NONE; break; case DRMMultiRectMode::PARALLEL: multirect_mode = MULTIRECT_PARALLEL; break; case DRMMultiRectMode::SERIAL: multirect_mode = MULTIRECT_SERIAL; break; default: DRM_LOGE("Invalid multirect mode %d to set on plane %d", drm_multirect_mode, GetObjectId()); break; } AddProperty(DRMProperty::MULTIRECT_MODE, multirect_mode); DRM_LOGD("Plane %d: Setting multirect_mode %d", obj_id, multirect_mode); } void DRMPlane::Unset(bool is_commit, drmModeAtomicReq *req) { DRM_LOGD("Plane %d: Unsetting from crtc %d", drm_plane_->plane_id, assigned_crtc_id_); PerformWrapper(DRMOps::PLANE_SET_FB_ID, req, 0); PerformWrapper(DRMOps::PLANE_SET_CRTC, req, 0); DRMRect rect = {0, 0, 0, 0}; PerformWrapper(DRMOps::PLANE_SET_SRC_RECT, req, rect); PerformWrapper(DRMOps::PLANE_SET_DST_RECT, req, rect); PerformWrapper(DRMOps::PLANE_SET_EXCL_RECT, req, rect); if (plane_type_info_.inverse_pma) { PerformWrapper(DRMOps::PLANE_SET_INVERSE_PMA, req, 0); } // Reset the sspp tonemap properties if they were set and update the in-use only if // its a Commit as Unset is called in Validate as well. if (dgm_csc_in_use_) { uint64_t csc_v1 = 0; AddProperty(DRMProperty::CSC_DMA_V1, csc_v1, true); DRM_LOGV("Plane %d Clearing DGM CSC", drm_plane_->plane_id); dgm_csc_in_use_ = !is_commit; } ResetColorLUTs(is_commit, req); } bool DRMPlane::SetDgmCscConfig(uint64_t handle) { if (plane_type_info_.type == DRMPlaneType::DMA && prop_mgr_.IsPropertyAvailable(DRMProperty::CSC_DMA_V1)) { sde_drm_csc_v1 *csc_v1 = reinterpret_cast(handle); uint64_t csc_v1_data = 0; sde_drm_csc_v1 csc_v1_tmp = {}; csc_config_copy_ = *csc_v1; if (std::memcmp(&csc_config_copy_, &csc_v1_tmp, sizeof(sde_drm_csc_v1)) != 0) { csc_v1_data = reinterpret_cast(&csc_config_copy_); } AddProperty(DRMProperty::CSC_DMA_V1, csc_v1_data, true); dgm_csc_in_use_ = (csc_v1_data != 0); DRM_LOGV("Plane %d in_use = %d", drm_plane_->plane_id, dgm_csc_in_use_); return true; } return false; } void DRMPlane::ResetColorLUTs(bool is_commit, drmModeAtomicReq *req) { // Reset the color luts if they were set and update the state only if its a Commit as Unset // is called in Validate as well. for (int i = 0; i <= static_cast(DRMTonemapLutType::VIG_3D_GAMUT); i++) { auto itr = plane_type_info_.tonemap_lut_version_map.find(static_cast(i)); if (itr != plane_type_info_.tonemap_lut_version_map.end()) { ResetColorLUTState(static_cast(i), is_commit, req); } } } void DRMPlane::ResetColorLUTState(DRMTonemapLutType lut_type, bool is_commit, drmModeAtomicReq *req) { DRMPlaneLutState *lut_state = nullptr; DRMPPFeatureID feature_id = {}; switch (lut_type) { case DRMTonemapLutType::DMA_1D_GC: lut_state = &dgm_1d_lut_gc_state_; feature_id = kFeatureDgmGc; break; case DRMTonemapLutType::DMA_1D_IGC: lut_state = &dgm_1d_lut_igc_state_; feature_id = kFeatureDgmIgc; break; case DRMTonemapLutType::VIG_1D_IGC: lut_state = &vig_1d_lut_igc_state_; feature_id = kFeatureVigIgc; break; case DRMTonemapLutType::VIG_3D_GAMUT: lut_state = &vig_3d_lut_gamut_state_; feature_id = kFeatureVigGamut; break; default: DLOGE("Invalid lut type = %d", lut_type); return; } if (*lut_state == kInactive) { DRM_LOGV("Plane %d %s Lut not used", drm_plane_->plane_id, GetColorLutString(lut_type)); return; } DRMPlaneLutState target_state; // If plane is getting unset, clearing of LUT will not be applied in hw. // In that case, mark LUT as dirty and make sure that these are cleared the // next time the plane gets used if (*lut_state == kActive && requested_crtc_id_ == 0) { target_state = kDirty; } else if (*lut_state == kDirty && requested_crtc_id_ != 0) { // If plane is getting activated while LUT is in dirty state, the new state // should be inactive but still need to clear exiting LUT config in hw target_state = kInactive; } else { return; } if (is_commit) { DRM_LOGD("Plane %d Clearing %s Lut, moving from (%d) -> (%d)", drm_plane_->plane_id, GetColorLutString(lut_type), *lut_state, target_state); *lut_state = target_state; } ResetColorLUT(feature_id, req); } void DRMPlane::ResetColorLUT(DRMPPFeatureID id, drmModeAtomicReq *req) { DRMPPFeatureInfo pp_feature_info = {}; pp_feature_info.type = kPropBlob; pp_feature_info.payload = nullptr; pp_feature_info.id = id; pp_mgr_->SetPPFeature(req, drm_plane_->plane_id, pp_feature_info); } } // namespace sde_drm