* Copyright (c) 2021-2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "drm_connector.h"
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <cinttypes>
#include <securec.h>
#include "display_log.h"
#include "drm_device.h"
#include "drm_vsync_worker.h"
namespace OHOS {
namespace HDI {
namespace DISPLAY {
void DrmMode::ConvertToHdiMode(DisplayModeInfo &hdiMode)
{
hdiMode.height = mModeInfo.vdisplay;
hdiMode.width = mModeInfo.hdisplay;
hdiMode.freshRate = mModeInfo.vrefresh;
hdiMode.id = mId;
}
DrmConnector::DrmConnector(drmModeConnector c, FdPtr &fd)
: mId(c.connector_id),
mPhyWidth(c.mmWidth),
mPhyHeight(c.mmHeight),
mEncoderId(c.encoder_id),
mConnectState(c.connection),
mDrmFdPtr(fd)
{
DISPLAY_LOGD("encoder_id %{public}d", mEncoderId);
DISPLAY_LOGD("the connect state is %{public}d", mConnectState);
for (int i = 0; i < c.count_encoders; i++) {
mPossibleEncoders.push_back(c.encoders[i]);
DISPLAY_LOGD("add possible encoder id %{public}d", c.encoders[i]);
}
ConvertToHdiType(c.connector_type, mType);
ConvertTypeToName(mType, mName);
InitModes(c);
DISPLAY_LOGD("name %{public}s", mName.c_str());
}
void DrmConnector::InitModes(drmModeConnector c)
{
DISPLAY_LOGD("id %{public}d mode size %{public}d", mId, c.count_modes);
mModes.clear();
mPreferenceId = INVALID_MODE_ID;
for (int i = 0; i < c.count_modes; i++) {
drmModeModeInfoPtr mode = c.modes + i;
DISPLAY_LOGD("mode: hdisplay %{public}d, vdisplay %{public}d vrefresh %{public}d type %{public}d",
mode->hdisplay, mode->vdisplay, mode->vrefresh, mode->type);
if ((mPreferenceId == INVALID_MODE_ID) && (mode->type & DRM_MODE_TYPE_PREFERRED)) {
DISPLAY_LOGD("set it to prefernce id %{public}d", i);
mPreferenceId = i;
}
mModes.emplace(i, DrmMode { *mode, i });
}
DISPLAY_LOGD("mode count %{public}zd", mModes.size());
}
int32_t DrmConnector::Init(DrmDevice &drmDevice)
{
int32_t ret;
DrmProperty prop;
DISPLAY_LOGD();
DISPLAY_CHK_RETURN((mDrmFdPtr == nullptr), DISPLAY_FAILURE, DISPLAY_LOGE("the mDrmFdPtr is nullptr"));
DISPLAY_CHK_RETURN((mDrmFdPtr->GetFd() == -1), DISPLAY_FAILURE, DISPLAY_LOGE("the drm fd is -1"));
ret = drmDevice.GetConnectorProperty(*this, PROP_DPMS, prop);
DISPLAY_CHK_RETURN((ret != DISPLAY_SUCCESS), DISPLAY_FAILURE, DISPLAY_LOGE("can not get mode prop id"));
mPropDpmsId = prop.propId;
mDpmsState = prop.value;
DISPLAY_LOGD("dpms state : %{public}" PRIu64 "", mDpmsState);
ret = drmDevice.GetConnectorProperty(*this, PROP_CRTCID, prop);
DISPLAY_CHK_RETURN((ret != DISPLAY_SUCCESS), DISPLAY_FAILURE, DISPLAY_LOGE("cat not get out fence prop id"));
mPropCrtcId = prop.propId;
DISPLAY_LOGD("encoder_id %{public}d", mEncoderId);
DISPLAY_LOGD("mPropCrtcId %{public}d", mPropCrtcId);
ret = drmDevice.GetConnectorProperty(*this, PROP_BRIGHTNESS, prop);
if (ret == DISPLAY_SUCCESS) {
mPropBrightnessId = prop.propId;
mBrightnessLevel = static_cast<uint32_t>(prop.value);
DISPLAY_LOGD("prop brightness is %{public}d, level is %{public}d", mPropBrightnessId, mBrightnessLevel);
} else {
DISPLAY_LOGW("can not get the brightness prop, can not set the brightness");
}
return DISPLAY_SUCCESS;
}
int32_t DrmConnector::GetBrightness(uint32_t& level)
{
if (mPropBrightnessId == DRM_INVALID_ID) {
DISPLAY_LOGE("the prop id of brightness is invalid");
return DISPLAY_NOT_SUPPORT;
}
level = mBrightnessLevel;
return DISPLAY_SUCCESS;
}
int32_t DrmConnector::SetBrightness(uint32_t level)
{
static int32_t brFd = 0;
const int32_t buffer_size = 10;
char buffer[buffer_size];
DISPLAY_LOGD("set %{public}d", level);
if (mPropBrightnessId == DRM_INVALID_ID) {
DISPLAY_LOGE("the prop id of brightness is invalid");
return DISPLAY_NOT_SUPPORT;
}
if (brFd <= 0) {
brFd = open("/sys/class/backlight/backlight/brightness", O_RDWR);
if (brFd < 0) {
DISPLAY_LOGE("open brightness file failed\n");
return DISPLAY_NOT_SUPPORT;
}
}
errno_t ret = memset_s(buffer, sizeof(buffer), 0, sizeof(buffer));
if (ret != EOK) {
DISPLAY_LOGE("memset_s failed\n");
return DISPLAY_FAILURE;
}
int bytes = sprintf_s(buffer, sizeof(buffer), "%d\n", level);
if (bytes < 0) {
DISPLAY_LOGE("change failed\n");
return DISPLAY_FAILURE;
}
write(brFd, buffer, bytes);
mBrightnessLevel = level;
return DISPLAY_SUCCESS;
}
void DrmConnector::GetDisplayCap(DisplayCapability &cap)
{
cap.phyHeight = mPhyHeight;
cap.phyWidth = mPhyWidth;
cap.type = mType;
memcpy_s(const_cast<char*>(cap.name.c_str()), cap.name.size(), mName.c_str(), mName.size());
if (mName.size() >= sizeof(cap.name)) {
cap.name[sizeof(cap.name) - 1] = 0;
} else {
cap.name[mName.size()] = 0;
}
cap.supportLayers = mSupportLayers;
cap.virtualDispCount = mVirtualDispCount;
cap.supportWriteBack = mSupportWriteBack;
cap.propertyCount = mPropertyCount;
}
void DrmConnector::ConvertTypeToName(uint32_t type, std::string &name)
{
DISPLAY_LOGD("type %{public}d", type);
switch (type) {
case DISP_INTF_VGA:
name = "VGA";
break;
case DISP_INTF_HDMI:
name = "HDMI";
break;
case DISP_INTF_MIPI:
name = "MIPI";
break;
default:
name = "Unknown";
break;
}
}
void DrmConnector::ConvertToHdiType(uint32_t type, InterfaceType &hdiType)
{
switch (type) {
case DRM_MODE_CONNECTOR_VGA:
hdiType = DISP_INTF_VGA;
break;
case DRM_MODE_CONNECTOR_DSI:
hdiType = DISP_INTF_MIPI;
break;
case DRM_MODE_CONNECTOR_HDMIA:
case DRM_MODE_CONNECTOR_HDMIB:
hdiType = DISP_INTF_HDMI;
break;
default:
hdiType = DISP_INTF_BUTT;
break;
}
}
int32_t DrmConnector::TryPickEncoder(IdMapPtr<DrmEncoder> &encoders, uint32_t encoderId, IdMapPtr<DrmCrtc> &crtcs,
uint32_t &crtcId)
{
int ret;
auto encoderIter = encoders.find(encoderId);
if (encoderIter == encoders.end()) {
DISPLAY_LOGW("can not find encoder for id : %{public}d", encoderId);
return DISPLAY_FAILURE;
}
auto &encoder = encoderIter->second;
DISPLAY_LOGD("connector : %{public}d encoder : %{public}d", mId, encoder->GetId());
ret = encoder->PickIdleCrtcId(crtcs, crtcId);
DISPLAY_CHK_RETURN((ret == DISPLAY_SUCCESS), DISPLAY_SUCCESS,
DISPLAY_LOGD("connector : %{public}d pick encoder : %{public}d", mId, encoder->GetId()));
return DISPLAY_FAILURE;
}
int32_t DrmConnector::PickIdleCrtcId(IdMapPtr<DrmEncoder> &encoders, IdMapPtr<DrmCrtc> &crtcs, uint32_t &crtcId)
{
DISPLAY_LOGD();
DISPLAY_LOGD("encoder_id %{public}d", mEncoderId);
int ret = TryPickEncoder(encoders, mEncoderId, crtcs, crtcId);
DISPLAY_CHK_RETURN((ret == DISPLAY_SUCCESS), DISPLAY_SUCCESS,
DISPLAY_LOGD("connector : %{public}d pick endcoder : %{public}d crtcId : %{public}d",
mId, mEncoderId, crtcId));
for (auto encoder : mPossibleEncoders) {
ret = TryPickEncoder(encoders, encoder, crtcs, crtcId);
DISPLAY_CHK_RETURN((ret == DISPLAY_SUCCESS), DISPLAY_SUCCESS,
DISPLAY_LOGD("connector : %{public}d pick endcoder : %{public}d crtcId : %{public}d", mId, mEncoderId,
crtcId));
}
DISPLAY_LOGW("can not pick a crtc for connector");
return DISPLAY_FAILURE;
}
int32_t DrmConnector::UpdateModes()
{
int drmFd = mDrmFdPtr->GetFd();
drmModeConnectorPtr c = drmModeGetConnector(drmFd, mId);
DISPLAY_CHK_RETURN((c == nullptr), DISPLAY_FAILURE, DISPLAY_LOGE("can not get connector"));
mConnectState = c->connection;
InitModes(*c);
drmModeFreeConnector(c);
return DISPLAY_SUCCESS;
}
std::shared_ptr<DrmCrtc> DrmConnector::UpdateCrtcId(IdMapPtr<DrmEncoder> &encoders,
IdMapPtr<DrmCrtc> &crtcs, bool plugIn, drmModeConnectorPtr c, int *crtc_id)
{
std::shared_ptr<DrmCrtc> crtc = nullptr;
int encoderid = c->encoders[0];
auto encoderIter = encoders.find(encoderid);
if (encoderIter == encoders.end()) {
DISPLAY_LOGW("can not find encoder for id : %{public}d", encoderid);
return crtc;
}
auto &encoder = encoderIter->second;
int possibleCrtcs = encoder->GetPossibleCrtcs();
for (auto crtcIter = crtcs.begin(); crtcIter != crtcs.end(); ++crtcIter) {
auto &posCrts = crtcIter->second;
if (possibleCrtcs == (1<<posCrts->GetPipe())) {
DISPLAY_LOGD("find crtc id %{public}d, pipe %{public}d", posCrts->GetId(), posCrts->GetPipe());
crtc = posCrts;
*crtc_id = posCrts->GetId();
}
}
if (plugIn) {
encoder->SetCrtcId(*crtc_id);
mEncoderId = c->encoders[0];
} else if (!plugIn) {
*crtc_id = 0;
mEncoderId = 0;
encoder->SetCrtcId(0);
}
return crtc;
}
bool DrmConnector::HandleHotplug(IdMapPtr<DrmEncoder> &encoders,
IdMapPtr<DrmCrtc> &crtcs, bool plugIn)
{
DISPLAY_LOGD("plug %{public}d", plugIn);
int drmFd = mDrmFdPtr->GetFd();
int ret;
int crtc_id = 0;
std::shared_ptr<DrmCrtc> crtc;
uint32_t blob_id;
drmModeAtomicReq *pset = drmModeAtomicAlloc();
DISPLAY_CHK_RETURN((pset == nullptr), DISPLAY_NULL_PTR,
DISPLAY_LOGE("drm atomic alloc failed errno %{public}d", errno));
drmModeConnectorPtr c = drmModeGetConnector(drmFd, mId);
if (c == nullptr) {
DISPLAY_LOGE("can not get connector");
drmModeAtomicFree(pset);
return false;
}
if (mConnectState == c->connection) {
drmModeAtomicFree(pset);
drmModeFreeConnector(c);
return false;
} else {
crtc = UpdateCrtcId(encoders, crtcs, plugIn, c, &crtc_id);
if (crtc == nullptr) {
return DISPLAY_FAILURE;
}
DISPLAY_LOGD("get crtc id %{public}d ", crtc_id);
DrmVsyncWorker::GetInstance().EnableVsync(plugIn);
drmModeCreatePropertyBlob(drmFd, &c->modes[0],
sizeof(c->modes[0]), &blob_id);
ret = drmModeAtomicAddProperty(pset, crtc->GetId(), crtc->GetActivePropId(), (int)plugIn);
ret |= drmModeAtomicAddProperty(pset, crtc->GetId(), crtc->GetModePropId(), blob_id);
ret |= drmModeAtomicAddProperty(pset, GetId(), GetPropCrtcId(), crtc_id);
DISPLAY_CHK_RETURN((ret < 0), DISPLAY_FAILURE,
DISPLAY_LOGE("can not add the crtc id prop %{public}d", errno));
ret = drmModeAtomicCommit(drmFd, pset, DRM_MODE_ATOMIC_ALLOW_MODESET, nullptr);
DISPLAY_CHK_RETURN((ret < 0), DISPLAY_FAILURE,
DISPLAY_LOGE("can not add the crtc id prop %{public}d", errno));
drmModeAtomicFree(pset);
mConnectState = c->connection;
InitModes(*c);
drmModeFreeConnector(c);
return true;
}
}
int32_t DrmConnector::GetDisplaySupportedModes(uint32_t *num, DisplayModeInfo *modes)
{
DISPLAY_CHK_RETURN((num == nullptr), DISPLAY_NULL_PTR, DISPLAY_LOGE("num is nullptr"));
*num = static_cast<int32_t>(mModes.size());
if (modes != nullptr) {
int i = 0;
for (const auto &modeMap : mModes) {
DrmMode mode = modeMap.second;
mode.ConvertToHdiMode(*(modes + i));
i++;
}
}
return DISPLAY_SUCCESS;
}
int32_t DrmConnector::SetDpmsState(uint64_t dmps)
{
DISPLAY_LOGD("dmps %{public}" PRIu64 "", dmps);
int ret = drmModeConnectorSetProperty(mDrmFdPtr->GetFd(), mId, mPropDpmsId, dmps);
DISPLAY_CHK_RETURN((ret != 0), DISPLAY_FAILURE, DISPLAY_LOGE("can not set dpms"));
mDpmsState = dmps;
return DISPLAY_SUCCESS;
}
bool DrmConnector::IsConnected()
{
return (mConnectState == DRM_MODE_CONNECTED);
}
int32_t DrmConnector::GetModeFromId(int32_t id, DrmMode &mode)
{
DISPLAY_LOGD();
auto iter = mModes.find(id);
if (iter == mModes.end()) {
return DISPLAY_FAILURE;
}
mode = mModes[id];
return DISPLAY_SUCCESS;
}
std::unique_ptr<DrmModeBlock> DrmConnector::GetModeBlockFromId(int32_t id)
{
DISPLAY_LOGD("id %{public}d", id);
auto iter = mModes.find(id);
DISPLAY_CHK_RETURN((iter == mModes.end()), nullptr, DISPLAY_LOGE("can not the mode %{public}d", id));
return std::make_unique<DrmModeBlock>(mModes[id]);
}
DrmModeBlock::DrmModeBlock(DrmMode &mode)
{
DISPLAY_LOGD();
Init(mode);
}
int32_t DrmModeBlock::Init(DrmMode &mode)
{
int ret;
int drmFd = DrmDevice::GetDrmFd();
DISPLAY_CHK_RETURN((drmFd < 0), DISPLAY_FAILURE, DISPLAY_LOGE("the drm fd is invalid"));
drmModeModeInfo modeInfo = *(mode.GetModeInfoPtr());
ret = drmModeCreatePropertyBlob(drmFd, static_cast<void *>(&modeInfo), sizeof(modeInfo), &mBlockId);
DISPLAY_CHK_RETURN((ret != 0), DISPLAY_FAILURE, DISPLAY_LOGE("create property blob failed"));
DISPLAY_LOGD("mBlockId %{public}d", mBlockId);
return DISPLAY_SUCCESS;
}
DrmModeBlock::~DrmModeBlock()
{
DISPLAY_LOGD("mBlockId %{public}d", mBlockId);
int drmFd = DrmDevice::GetDrmFd();
if ((mBlockId != DRM_INVALID_ID) && (drmFd >= 0)) {
int ret = drmModeDestroyPropertyBlob(drmFd, mBlockId);
if (ret != 0) {
DISPLAY_LOGE("destroy property blob failed errno %{public}d", errno);
}
} else {
DISPLAY_LOGE("can not destruct the block id %{public}d drmFd %{public}d", mBlockId, drmFd);
}
}
}
}
}