blob: e1d47450809525996f7d03608c08a47e01c515c7 [file] [log] [blame]
/* Copyright (c) 2020-2021, The Linux Foundataion. 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 <sstream>
#include <string>
#include <tuple>
#include <errno.h>
#include <string>
#include <drm_logger.h>
#include <cstring>
#include <regex>
#include <inttypes.h>
#include "drm_panel_feature_mgr.h"
#define __CLASS__ "DRMPanelFeatureMgr"
namespace sde_drm {
using std::map;
using std::vector;
using std::mutex;
using std::lock_guard;
static DRMPanelFeatureMgr panel_feature_mgr;
// Demura Planes' Default Bit Indices
static uint8_t DEMURA_DMA1RECT0 = 0x1;
static uint8_t DEMURA_DMA1RECT1 = 0x2;
static uint8_t DEMURA_DMA3RECT0 = 0x3;
static uint8_t DEMURA_DMA3RECT1 = 0x4;
DRMPanelFeatureMgrIntf *GetPanelFeatureManagerIntf() {
return &panel_feature_mgr;
}
void DRMPanelFeatureMgr::Init(int fd, drmModeRes* res) {
lock_guard<mutex> lock(lock_);
if (!res || (fd < 0)) {
DRM_LOGE("Invalid arguments for init - fd %d and DRM resources pointer 0x%pK", fd, (void *)res);
return;
}
drm_res_ = res;
dev_fd_ = fd;
for (int i = 0; i < res->count_crtcs; i++) {
drmModeCrtc *crtc = drmModeGetCrtc(dev_fd_, res->crtcs[i]);
if (crtc) {
int err = InitObjectProps(crtc->crtc_id, DRM_MODE_OBJECT_CRTC);
if (err) {
DRM_LOGE("Failed to get crtc props %d", crtc->crtc_id);
}
drmModeFreeCrtc(crtc);
}
}
for (int i = 0; i < res->count_connectors; i++) {
drmModeConnector *conn = drmModeGetConnector(dev_fd_, res->connectors[i]);
if (conn) {
int err = InitObjectProps(conn->connector_id, DRM_MODE_OBJECT_CONNECTOR);
if (err) {
DRM_LOGE("Failed to get conn %d properties", conn->connector_id);
}
drmModeFreeConnector(conn);
}
}
drm_property_map_[kDRMPanelFeatureDemuraResources] = DRMProperty::DEMURA_BOOT_PLANE_V1;
drm_property_map_[kDRMPanelFeatureDemuraInit] = DRMProperty::DEMURA_INIT_CFG_V1;
drm_property_map_[kDRMPanelFeaturePanelId] = DRMProperty::DEMURA_PANEL_ID;
drm_property_map_[kDRMPanelFeatureSPRInit] = DRMProperty::SPR_INIT_CFG_V1;
drm_property_map_[kDRMPanelFeatureSPRPackType] = DRMProperty::CAPABILITIES;
drm_property_map_[kDRMPanelFeatureDsppIndex] = DRMProperty::DSPP_CAPABILITIES;
drm_property_map_[kDRMPanelFeatureDsppSPRInfo] = DRMProperty::DSPP_CAPABILITIES;
drm_property_map_[kDRMPanelFeatureDsppDemuraInfo] = DRMProperty::DSPP_CAPABILITIES;
drm_property_map_[kDRMPanelFeatureDsppRCInfo] = DRMProperty::DSPP_CAPABILITIES;
drm_property_map_[kDRMPanelFeatureRCInit] = DRMProperty::DSPP_RC_MASK_V1;
drm_prop_type_map_[kDRMPanelFeatureDemuraResources] = DRMPropType::kPropBitmask;
drm_prop_type_map_[kDRMPanelFeatureDemuraInit] = DRMPropType::kPropBlob;
drm_prop_type_map_[kDRMPanelFeaturePanelId] = DRMPropType::kPropBlob;
drm_prop_type_map_[kDRMPanelFeatureSPRInit] = DRMPropType::kPropBlob;
drm_prop_type_map_[kDRMPanelFeatureRCInit] = DRMPropType::kPropBlob;
drm_prop_type_map_[kDRMPanelFeatureSPRPackType] = DRMPropType::kPropBlob;
drm_prop_type_map_[kDRMPanelFeatureDsppIndex] = DRMPropType::kPropRange;
drm_prop_type_map_[kDRMPanelFeatureDsppSPRInfo] = DRMPropType::kPropRange;
drm_prop_type_map_[kDRMPanelFeatureDsppDemuraInfo] = DRMPropType::kPropRange;
drm_prop_type_map_[kDRMPanelFeatureDsppRCInfo] = DRMPropType::kPropRange;
feature_info_tbl_[kDRMPanelFeatureDemuraResources] = DRMPanelFeatureInfo {
kDRMPanelFeatureDemuraResources, DRM_MODE_OBJECT_CRTC, UINT32_MAX, 1, 0, 0};
feature_info_tbl_[kDRMPanelFeatureDemuraInit] = DRMPanelFeatureInfo {kDRMPanelFeatureDemuraInit,
DRM_MODE_OBJECT_CRTC, UINT32_MAX, 1, sizeof(drm_msm_dem_cfg), 0};
feature_info_tbl_[kDRMPanelFeaturePanelId] = DRMPanelFeatureInfo {kDRMPanelFeaturePanelId,
DRM_MODE_OBJECT_CONNECTOR, UINT32_MAX, 1, sizeof(uint64_t), 0};
feature_info_tbl_[kDRMPanelFeatureSPRInit] = DRMPanelFeatureInfo {kDRMPanelFeatureSPRInit,
DRM_MODE_OBJECT_CRTC, UINT32_MAX, 1, sizeof(drm_msm_spr_init_cfg), 0};
feature_info_tbl_[kDRMPanelFeatureRCInit] = DRMPanelFeatureInfo {
kDRMPanelFeatureRCInit, DRM_MODE_OBJECT_CRTC, UINT32_MAX, 1, sizeof(drm_msm_rc_mask_cfg), 0};
feature_info_tbl_[kDRMPanelFeatureSPRPackType] = DRMPanelFeatureInfo {kDRMPanelFeatureSPRPackType,
DRM_MODE_OBJECT_CONNECTOR, UINT32_MAX, 1, 64, 0};
feature_info_tbl_[kDRMPanelFeatureDsppIndex] = DRMPanelFeatureInfo {kDRMPanelFeatureDsppIndex,
DRM_MODE_OBJECT_CRTC, UINT32_MAX, 1, 64, 0};
feature_info_tbl_[kDRMPanelFeatureDsppSPRInfo] = DRMPanelFeatureInfo {
kDRMPanelFeatureDsppSPRInfo, DRM_MODE_OBJECT_CRTC, UINT32_MAX, 1, 64, 0};
feature_info_tbl_[kDRMPanelFeatureDsppDemuraInfo] = DRMPanelFeatureInfo {
kDRMPanelFeatureDsppDemuraInfo, DRM_MODE_OBJECT_CRTC, UINT32_MAX, 1, 64, 0};
feature_info_tbl_[kDRMPanelFeatureDsppRCInfo] = DRMPanelFeatureInfo {
kDRMPanelFeatureDsppRCInfo, DRM_MODE_OBJECT_CRTC, UINT32_MAX, 1, 64, 0};
}
void DRMPanelFeatureMgr::Deinit() {
int ret = 0;
for (int i = kDRMPanelFeatureDsppIndex; i < kDRMPanelFeatureMax; i++) {
DRMPanelFeatureID prop_id = static_cast<DRMPanelFeatureID>(i);
if (drm_prop_blob_ids_map_[prop_id]) {
ret = drmModeDestroyPropertyBlob(dev_fd_, drm_prop_blob_ids_map_[prop_id]);
if (ret) {
DRM_LOGE("failed to destroy blob for feature %d, ret = %d", prop_id, ret);
return;
} else {
drm_prop_blob_ids_map_[prop_id] = 0;
}
}
}
}
int DRMPanelFeatureMgr::InitObjectProps(int obj_id, int obj_type) {
if (dev_fd_ < 0 || obj_id < 0) {
DRM_LOGE("Invalid dev_fd_ %d or crtc_id %d", dev_fd_, obj_id);
return -EINVAL;
}
drmModeObjectProperties *props =
drmModeObjectGetProperties(dev_fd_, obj_id, obj_type);
if (!props || !props->props || !props->prop_values) {
DRM_LOGE("Failed to get props for obj_id:%d obj_type:%d", obj_id, obj_type);
drmModeFreeObjectProperties(props);
return -EINVAL;
}
for (uint32_t j = 0; j < props->count_props; j++) {
drmModePropertyRes *info = drmModeGetProperty(dev_fd_, props->props[j]);
if (!info) {
continue;
}
std::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;
}
prop_mgr_.SetPropertyId(prop_enum, info->prop_id);
drmModeFreeProperty(info);
}
drmModeFreeObjectProperties(props);
return 0;
}
void DRMPanelFeatureMgr::ParsePanelId(uint32_t blob_id, DRMPanelFeatureInfo *info) {
drmModePropertyBlobRes *blob = drmModeGetPropertyBlob(dev_fd_, blob_id);
if (!blob) {
return;
}
if (!blob->data) {
return;
}
if (blob->length != sizeof(uint64_t)) {
DRM_LOGE("Expecting %zu bytes but got %u", sizeof(uint64_t), blob->length);
return;
}
uint64_t* panel_id = reinterpret_cast<uint64_t *>(info->prop_ptr);
// Read as-is / big endian. Driver has supplied the value in this manner.
uint8_t *data = (uint8_t*)(blob->data);
for (size_t i = 0; i < blob->length; i++) {
*panel_id = (*panel_id << 8) | *data;
data++;
}
info->prop_size = sizeof(uint64_t);
drmModeFreePropertyBlob(blob);
}
void DRMPanelFeatureMgr::ParseDemuraResources(drmModePropertyRes *prop, uint64_t value,
DRMPanelFeatureInfo *info) {
// Values come as bit indices, not fully-realized values, for DRM_MODE_PROP_BITMASK
for (auto i = 0; i < prop->count_enums; i++) {
std::string enum_name(prop->enums[i].name);
if (enum_name == "demura_dma1_rect0") {
DEMURA_DMA1RECT0 = (1 << prop->enums[i].value);
} else if (enum_name == "demura_dma1_rect1") {
DEMURA_DMA1RECT1 = (1 << prop->enums[i].value);
} else if (enum_name == "demura_dma3_rect0") {
DEMURA_DMA3RECT0 = (1 << prop->enums[i].value);
} else if (enum_name == "demura_dma3_rect1") {
DEMURA_DMA3RECT1 = (1 << prop->enums[i].value);
}
}
FetchResourceList *frl = reinterpret_cast<FetchResourceList*>(info->prop_ptr);
if (value & DEMURA_DMA1RECT0) {
frl->push_back(std::make_tuple("DMA", 1, 0));
}
if (value & DEMURA_DMA1RECT1) {
frl->push_back(std::make_tuple("DMA", 1, 1));
}
if (value & DEMURA_DMA3RECT0) {
frl->push_back(std::make_tuple("DMA", 3, 0));
}
if (value & DEMURA_DMA3RECT1) {
frl->push_back(std::make_tuple("DMA", 3, 1));
}
info->prop_size += frl->size();
}
void DRMPanelFeatureMgr::ParseDsppCapabilities(uint32_t blob_id, std::vector<int> *values,
uint32_t *size, const std::string str) {
drmModePropertyBlobRes *blob = drmModeGetPropertyBlob(dev_fd_, blob_id);
if (!blob) {
DRM_LOGW("Unable to find blob for id %d", blob_id);
return;
}
if (!blob->data) {
DRM_LOGW("Invalid blob - no data for for blob-id %d", blob_id);
return;
}
char *fmt_str = new char[blob->length + 1];
std::memcpy(fmt_str, blob->data, blob->length);
fmt_str[blob->length] = '\0';
std::stringstream stream(fmt_str);
std::string line = {};
// Search for panel feature property pattern. Which is defined as rc0=1, rc1=1
const std::regex exp(str + "(\\d+)=1");
std::smatch sm;
while (std::getline(stream, line)) {
std::regex_match(line, sm, exp);
// smatch shall include full line as a match followed by the hw block # as a match
if (sm.size() == 2) {
std::string tmpstr(sm[1]);
int temp = atoi(tmpstr.c_str()); // atoi safe to use due to regex success
values->push_back(temp);
}
}
*size = sizeof(int) * values->size();
delete[] fmt_str;
}
void DRMPanelFeatureMgr::ParseCapabilities(uint32_t blob_id, char* value, uint32_t max_len,
const std::string str) {
drmModePropertyBlobRes *blob = drmModeGetPropertyBlob(dev_fd_, blob_id);
if (!blob) {
DRM_LOGW("Unable to find blob for id %d", blob_id);
return;
}
if (!blob->data) {
DRM_LOGW("Invalid blob - no data for for blob-id %d", blob_id);
return;
}
char *fmt_str = new char[blob->length + 1];
std::memcpy(fmt_str, blob->data, blob->length);
fmt_str[blob->length] = '\0';
std::stringstream stream(fmt_str);
std::string line = {};
std::string val = {};
const std::string goal = str + "=";
while (std::getline(stream, line)) {
if (line.find(goal) != std::string::npos) {
val = std::string(line, goal.length());
}
}
if (max_len <= val.size()) {
DRM_LOGW("Insufficient size max_len: %d actual size: %zu", max_len, val.size());
return;
}
std::copy(val.begin(), val.end(), value);
value[val.size()] = '\0';
delete[] fmt_str;
}
void DRMPanelFeatureMgr::GetPanelFeatureInfo(DRMPanelFeatureInfo *info) {
lock_guard<mutex> lock(lock_);
if (!info) {
DRM_LOGE("Invalid input, DRMPanelFeatureInfo is NULL");
return;
}
if (info->prop_id > kDRMPanelFeatureMax) {
DRM_LOGE("Invalid feature id %d", info->prop_id);
return;
}
DRMProperty prop_enum = drm_property_map_[info->prop_id];
if (!prop_mgr_.IsPropertyAvailable(prop_enum)) {
DRM_LOGW("Property id is not available for DRMProperty: %d feature-id: %d",
prop_enum, info->prop_id);
return;
}
// memory is not allocated by client - populate default property info
if (!info->prop_ptr) {
*info = feature_info_tbl_[info->prop_id];
return;
}
drmModeObjectProperties *props =
drmModeObjectGetProperties(dev_fd_, info->obj_id, info->obj_type);
if (!props || !props->props || !props->prop_values) {
drmModeFreeObjectProperties(props);
DRM_LOGE("Failed to Get properties for obj: %d type:%d", info->obj_id, info->obj_type);
return;
}
for (uint32_t j = 0; j < props->count_props; j++) {
drmModePropertyRes *property = drmModeGetProperty(dev_fd_, props->props[j]);
if (!property) {
continue;
}
std::string property_name(property->name);
if (prop_enum != prop_mgr_.GetPropertyEnum(property_name)) {
drmModeFreeProperty(property);
continue;
}
if (info->prop_id == kDRMPanelFeatureSPRPackType) {
ParseCapabilities(props->prop_values[j],
reinterpret_cast<char *> (info->prop_ptr), info->prop_size, "spr_pack_type");
} else if (info->prop_id == kDRMPanelFeatureDsppIndex) {
ParseDsppCapabilities(props->prop_values[j],
reinterpret_cast<std::vector<int> *>(info->prop_ptr), &(info->prop_size), "dspp");
} else if (info->prop_id == kDRMPanelFeatureDemuraResources) {
ParseDemuraResources(property, props->prop_values[j], info);
} else if (info->prop_id == kDRMPanelFeaturePanelId) {
ParsePanelId(props->prop_values[j], info);
} else if (info->prop_id == kDRMPanelFeatureDsppSPRInfo) {
ParseDsppCapabilities(props->prop_values[j],
reinterpret_cast<std::vector<int> *>(info->prop_ptr), &(info->prop_size), "spr");
} else if (info->prop_id == kDRMPanelFeatureDsppDemuraInfo) {
ParseDsppCapabilities(props->prop_values[j],
reinterpret_cast<std::vector<int> *>(info->prop_ptr), &(info->prop_size), "demura");
} else if (info->prop_id == kDRMPanelFeatureDsppRCInfo) {
ParseDsppCapabilities(props->prop_values[j],
reinterpret_cast<std::vector<int> *>(info->prop_ptr), &(info->prop_size), "rc");
} else if (drm_prop_type_map_[info->prop_id] == DRMPropType::kPropBlob) {
drmModePropertyBlobRes *blob = drmModeGetPropertyBlob(dev_fd_, props->prop_values[j]);
if (!blob || !blob->data || !blob->length) {
return;
}
uint8_t *src_begin = reinterpret_cast<uint8_t *> (blob->data);
uint8_t *src_end = src_begin + blob->length;
uint8_t *dst = reinterpret_cast<uint8_t *> (info->prop_ptr);
std::copy(src_begin, src_end, dst);
} else {
uint8_t *src_begin = reinterpret_cast<uint8_t *> (props->prop_values[j]);
uint8_t *src_end = src_begin + info->prop_size;
uint8_t *dst = reinterpret_cast<uint8_t *> (info->prop_ptr);
std::copy(src_begin, src_end, dst);
}
drmModeFreeProperty(property);
}
drmModeFreeObjectProperties(props);
}
void DRMPanelFeatureMgr::CachePanelFeature(const DRMPanelFeatureInfo &info) {
lock_guard<mutex> lock(lock_);
if (info.prop_id >= kDRMPanelFeatureMax || info.obj_id == UINT32_MAX) {
DRM_LOGE("invalid property info to set id %d value ptr %" PRIu64 , info.prop_id, info.prop_ptr);
return;
}
dirty_features_[info.prop_id].first = true;
dirty_features_[info.prop_id].second = info;
}
void DRMPanelFeatureMgr::CommitPanelFeatures(drmModeAtomicReq *req, const DRMDisplayToken &token) {
lock_guard<mutex> lock(lock_);
for (auto it = dirty_features_.begin(); it != dirty_features_.end(); it++) {
DRMPanelFeatureInfo &info = it->second;
if (!it->first)
continue;
ApplyDirtyFeature(req, token, info);
*it = {};
}
}
void DRMPanelFeatureMgr::NullCommitPanelFeatures(drmModeAtomicReq *req,
const DRMDisplayToken &token) {
lock_guard<mutex> lock(lock_);
for (auto it = dirty_features_.begin(); it != dirty_features_.end(); it++) {
DRMPanelFeatureInfo &info = it->second;
auto entry_iter = apply_in_null_commit_.find(info.obj_id);
if (entry_iter == apply_in_null_commit_.end())
continue;
if (entry_iter->second != info.prop_id)
continue;
if (!it->first) {
DLOGW("Prop %u is already committed via draw cycle", info.prop_id);
apply_in_null_commit_.erase(info.obj_id);
continue;
}
ApplyDirtyFeature(req, token, info);
apply_in_null_commit_.erase(info.obj_id);
*it = {};
}
}
// LCOV_EXCL_START
void DRMPanelFeatureMgr::ResetPanelFeatures(drmModeAtomicReq *req,
const DRMDisplayToken &token) {
lock_guard<mutex> lock(lock_);
DRMPanelFeatureInfo info;
info.prop_id = kDRMPanelFeatureSPRInit;
info.obj_id = token.crtc_id;
info.prop_ptr = 0;
ApplyDirtyFeature(req, token, info);
info.prop_id = kDRMPanelFeatureDemuraInit;
ApplyDirtyFeature(req, token, info);
}
// LCOV_EXCL_STOP
void DRMPanelFeatureMgr::MarkForNullCommit(const DRMDisplayToken &token, const DRMPanelFeatureID &id) {
DRMPanelFeatureInfo &info = feature_info_tbl_[id];
uint32_t obj_id = 0;
switch (info.obj_type) {
case DRM_MODE_OBJECT_CRTC:
obj_id = token.crtc_id;
break;
case DRM_MODE_OBJECT_CONNECTOR:
obj_id = token.conn_id;
break;
default:
return;
}
apply_in_null_commit_[obj_id] = id;
DLOGI("Marked %u for null commit", id);
}
void DRMPanelFeatureMgr::ApplyDirtyFeature(drmModeAtomicReq *req, const DRMDisplayToken &token,
DRMPanelFeatureInfo &info) {
int ret = 0;
if (info.prop_id >= kDRMPanelFeatureMax) {
DRM_LOGE("invalid property info to set id %d value ptr %" PRIu64, info.prop_id, info.prop_ptr);
return;
}
// Commit only features meant for the given DisplayToken
if (token.crtc_id != info.obj_id && token.conn_id != info.obj_id) {
return;
}
uint32_t prop_id = prop_mgr_.GetPropertyId(drm_property_map_[info.prop_id]);
if (!prop_id) {
DRM_LOGE("prop_id is 0 for panel feature-id %u", info.prop_id);
return;
}
uint64_t value = 0;
if (DRMPropType::kPropBlob == drm_prop_type_map_[info.prop_id]) {
uint32_t blob_id = 0;
if (!info.prop_ptr) {
// Reset the feature.
ret = drmModeAtomicAddProperty(req, info.obj_id, prop_id, 0);
if (ret < 0) {
DRM_LOGE("failed to add property ret:%d, obj_id:%d prop_id:%u value:%" PRIu64,
ret, info.obj_id, prop_id, value);
}
DLOGI("Commited panel feature [disabled]: %u-%u", info.prop_id, prop_id);
return;
}
ret = drmModeCreatePropertyBlob(dev_fd_, reinterpret_cast<void *> (info.prop_ptr),
info.prop_size, &blob_id);
if (ret || blob_id == 0) {
DRM_LOGE("failed to create blob ret %d, id = %d prop_ptr:%" PRIu64 " prop_sz:%d",
ret, blob_id, info.prop_ptr, info.prop_size);
return;
}
if (drm_prop_blob_ids_map_[info.prop_id]) {
ret = drmModeDestroyPropertyBlob(dev_fd_, drm_prop_blob_ids_map_[info.prop_id]);
if (ret) {
DRM_LOGE("failed to destroy blob for feature %d, ret = %d", info.prop_id, ret);
return;
}
}
drm_prop_blob_ids_map_[info.prop_id] = blob_id;
value = blob_id;
} else if (info.prop_size == sizeof(uint64_t)) {
value = (reinterpret_cast<uint64_t *> (info.prop_ptr))[0];
} else {
DRM_LOGE("Unsupported property type id = %d size:%d", info.prop_id, info.prop_size);
}
ret = drmModeAtomicAddProperty(req, info.obj_id, prop_id, value);
if (ret < 0) {
DRM_LOGE("failed to add property ret:%d, obj_id:%d prop_id:%x value:%" PRIu64,
ret, info.obj_id, prop_id, value);
}
DLOGI("Commited panel feature [enabled]: %u-%u", info.prop_id, prop_id);
}
} // namespace sde_drm