1/*
2 * Copyright (c) 2021-2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "drm_connector.h"
17#include <xf86drm.h>
18#include <xf86drmMode.h>
19#include <cinttypes>
20#include <securec.h>
21#include "display_log.h"
22#include "drm_device.h"
23#include "drm_vsync_worker.h"
24
25namespace OHOS {
26namespace HDI {
27namespace DISPLAY {
28void DrmMode::ConvertToHdiMode(DisplayModeInfo &hdiMode)
29{
30    hdiMode.height = mModeInfo.vdisplay;
31    hdiMode.width = mModeInfo.hdisplay;
32    hdiMode.freshRate = mModeInfo.vrefresh;
33    hdiMode.id = mId;
34}
35
36DrmConnector::DrmConnector(drmModeConnector c, FdPtr &fd)
37    : mId(c.connector_id),
38      mPhyWidth(c.mmWidth),
39      mPhyHeight(c.mmHeight),
40      mEncoderId(c.encoder_id),
41      mConnectState(c.connection),
42      mDrmFdPtr(fd)
43{
44    DISPLAY_LOGD("encoder_id %{public}d", mEncoderId);
45    DISPLAY_LOGD("the connect state is %{public}d", mConnectState);
46
47    for (int i = 0; i < c.count_encoders; i++) {
48        mPossibleEncoders.push_back(c.encoders[i]);
49        DISPLAY_LOGD("add possible encoder id %{public}d", c.encoders[i]);
50    }
51
52    ConvertToHdiType(c.connector_type, mType);
53    ConvertTypeToName(mType, mName);
54    InitModes(c);
55    DISPLAY_LOGD("name %{public}s", mName.c_str());
56}
57
58void DrmConnector::InitModes(drmModeConnector c)
59{
60    DISPLAY_LOGD("id %{public}d  mode size %{public}d", mId, c.count_modes);
61    mModes.clear();
62    mPreferenceId = INVALID_MODE_ID;
63    for (int i = 0; i < c.count_modes; i++) {
64        drmModeModeInfoPtr mode = c.modes + i;
65        DISPLAY_LOGD("mode: hdisplay %{public}d, vdisplay %{public}d vrefresh %{public}d type %{public}d",
66            mode->hdisplay, mode->vdisplay, mode->vrefresh, mode->type);
67        if ((mPreferenceId == INVALID_MODE_ID) && (mode->type & DRM_MODE_TYPE_PREFERRED)) {
68            DISPLAY_LOGD("set it to prefernce id %{public}d", i);
69            mPreferenceId = i;
70        }
71        mModes.emplace(i, DrmMode { *mode, i });
72    }
73    DISPLAY_LOGD("mode count %{public}zd", mModes.size());
74}
75
76int32_t DrmConnector::Init(DrmDevice &drmDevice)
77{
78    int32_t ret;
79    DrmProperty prop;
80    DISPLAY_LOGD();
81    DISPLAY_CHK_RETURN((mDrmFdPtr == nullptr), DISPLAY_FAILURE, DISPLAY_LOGE("the mDrmFdPtr is nullptr"));
82    DISPLAY_CHK_RETURN((mDrmFdPtr->GetFd() == -1), DISPLAY_FAILURE, DISPLAY_LOGE("the drm fd is -1"));
83    // find dpms prop
84    ret = drmDevice.GetConnectorProperty(*this, PROP_DPMS, prop);
85    DISPLAY_CHK_RETURN((ret != DISPLAY_SUCCESS), DISPLAY_FAILURE, DISPLAY_LOGE("can not get mode prop id"));
86    mPropDpmsId = prop.propId;
87    mDpmsState = prop.value;
88    DISPLAY_LOGD("dpms state : %{public}" PRIu64 "", mDpmsState);
89    // find the crtcid
90    ret = drmDevice.GetConnectorProperty(*this, PROP_CRTCID, prop);
91    DISPLAY_CHK_RETURN((ret != DISPLAY_SUCCESS), DISPLAY_FAILURE, DISPLAY_LOGE("cat not get out fence prop id"));
92    mPropCrtcId = prop.propId;
93    DISPLAY_LOGD("encoder_id %{public}d", mEncoderId);
94    DISPLAY_LOGD("mPropCrtcId %{public}d", mPropCrtcId);
95    // find the brightness
96    ret = drmDevice.GetConnectorProperty(*this, PROP_BRIGHTNESS, prop);
97    if (ret == DISPLAY_SUCCESS) {
98        mPropBrightnessId =  prop.propId;
99        mBrightnessLevel = static_cast<uint32_t>(prop.value);
100        DISPLAY_LOGD("prop brightness is %{public}d, level is %{public}d", mPropBrightnessId, mBrightnessLevel);
101    } else {
102        DISPLAY_LOGW("can not get the brightness prop, can not set the brightness");
103    }
104    return DISPLAY_SUCCESS;
105}
106
107int32_t DrmConnector::GetBrightness(uint32_t& level)
108{
109    if (mPropBrightnessId == DRM_INVALID_ID) {
110        DISPLAY_LOGE("the prop id of brightness is invalid");
111        return DISPLAY_NOT_SUPPORT;
112    }
113    level = mBrightnessLevel;
114    return DISPLAY_SUCCESS;
115}
116
117int32_t DrmConnector::SetBrightness(uint32_t level)
118{
119    static int32_t brFd = 0;
120    const int32_t buffer_size = 10; /* buffer size */
121    char buffer[buffer_size];
122
123    DISPLAY_LOGD("set %{public}d", level);
124    if (mPropBrightnessId == DRM_INVALID_ID) {
125        DISPLAY_LOGE("the prop id of brightness is invalid");
126        return DISPLAY_NOT_SUPPORT;
127    }
128    if (brFd <= 0) {
129        brFd = open("/sys/class/backlight/backlight/brightness", O_RDWR);
130        if (brFd < 0) {
131            DISPLAY_LOGE("open brightness file failed\n");
132            return DISPLAY_NOT_SUPPORT;
133        }
134    }
135    errno_t ret = memset_s(buffer, sizeof(buffer), 0, sizeof(buffer));
136    if (ret != EOK) {
137        DISPLAY_LOGE("memset_s failed\n");
138        return DISPLAY_FAILURE;
139    }
140    int bytes = sprintf_s(buffer, sizeof(buffer), "%d\n", level);
141    if (bytes < 0) {
142        DISPLAY_LOGE("change failed\n");
143        return DISPLAY_FAILURE;
144    }
145    write(brFd, buffer, bytes);
146    mBrightnessLevel = level;
147    return DISPLAY_SUCCESS;
148}
149
150void DrmConnector::GetDisplayCap(DisplayCapability &cap)
151{
152    cap.phyHeight = mPhyHeight;
153    cap.phyWidth = mPhyWidth;
154    cap.type = mType;
155    memcpy_s(const_cast<char*>(cap.name.c_str()), cap.name.size(), mName.c_str(), mName.size());
156    if (mName.size() >= sizeof(cap.name)) {
157        cap.name[sizeof(cap.name) - 1] = 0;
158    } else {
159        cap.name[mName.size()] = 0;
160    }
161    cap.supportLayers = mSupportLayers;
162    cap.virtualDispCount = mVirtualDispCount;
163    cap.supportWriteBack = mSupportWriteBack;
164    cap.propertyCount = mPropertyCount;
165}
166
167void DrmConnector::ConvertTypeToName(uint32_t type, std::string &name)
168{
169    DISPLAY_LOGD("type %{public}d", type);
170    switch (type) {
171        case DISP_INTF_VGA:
172            name = "VGA";
173            break;
174        case DISP_INTF_HDMI:
175            name = "HDMI";
176            break;
177        case DISP_INTF_MIPI:
178            name = "MIPI";
179            break;
180        default:
181            name = "Unknown";
182            break;
183    }
184}
185
186void DrmConnector::ConvertToHdiType(uint32_t type, InterfaceType &hdiType)
187{
188    switch (type) {
189        case DRM_MODE_CONNECTOR_VGA:
190            hdiType = DISP_INTF_VGA;
191            break;
192        case DRM_MODE_CONNECTOR_DSI:
193            hdiType = DISP_INTF_MIPI;
194            break;
195        case DRM_MODE_CONNECTOR_HDMIA:
196        case DRM_MODE_CONNECTOR_HDMIB:
197            hdiType = DISP_INTF_HDMI;
198            break;
199        default:
200            hdiType = DISP_INTF_BUTT;
201            break;
202    }
203}
204int32_t DrmConnector::TryPickEncoder(IdMapPtr<DrmEncoder> &encoders, uint32_t encoderId, IdMapPtr<DrmCrtc> &crtcs,
205    uint32_t &crtcId)
206{
207    int ret;
208    auto encoderIter = encoders.find(encoderId);
209    if (encoderIter == encoders.end()) {
210        DISPLAY_LOGW("can not find encoder for id : %{public}d", encoderId);
211        return DISPLAY_FAILURE;
212    }
213
214    auto &encoder = encoderIter->second;
215    DISPLAY_LOGD("connector : %{public}d encoder : %{public}d", mId, encoder->GetId());
216    ret = encoder->PickIdleCrtcId(crtcs, crtcId);
217    DISPLAY_CHK_RETURN((ret == DISPLAY_SUCCESS), DISPLAY_SUCCESS,
218        DISPLAY_LOGD("connector : %{public}d pick encoder : %{public}d", mId, encoder->GetId()));
219    return DISPLAY_FAILURE;
220}
221
222int32_t DrmConnector::PickIdleCrtcId(IdMapPtr<DrmEncoder> &encoders, IdMapPtr<DrmCrtc> &crtcs, uint32_t &crtcId)
223{
224    DISPLAY_LOGD();
225    DISPLAY_LOGD("encoder_id %{public}d", mEncoderId);
226    int ret = TryPickEncoder(encoders, mEncoderId, crtcs, crtcId);
227    DISPLAY_CHK_RETURN((ret == DISPLAY_SUCCESS), DISPLAY_SUCCESS,
228        DISPLAY_LOGD("connector : %{public}d pick endcoder : %{public}d crtcId : %{public}d",
229        mId, mEncoderId, crtcId));
230
231    for (auto encoder : mPossibleEncoders) {
232        ret = TryPickEncoder(encoders, encoder, crtcs, crtcId);
233        DISPLAY_CHK_RETURN((ret == DISPLAY_SUCCESS), DISPLAY_SUCCESS,
234            DISPLAY_LOGD("connector : %{public}d pick endcoder : %{public}d crtcId : %{public}d", mId, mEncoderId,
235            crtcId));
236    }
237
238    DISPLAY_LOGW("can not pick a crtc for connector");
239    return DISPLAY_FAILURE;
240}
241
242int32_t DrmConnector::UpdateModes()
243{
244    int drmFd = mDrmFdPtr->GetFd();
245    drmModeConnectorPtr c = drmModeGetConnector(drmFd, mId);
246    DISPLAY_CHK_RETURN((c == nullptr), DISPLAY_FAILURE, DISPLAY_LOGE("can not get connector"));
247    mConnectState = c->connection;
248    // init the modes
249    InitModes(*c);
250    drmModeFreeConnector(c);
251    return DISPLAY_SUCCESS;
252}
253
254std::shared_ptr<DrmCrtc> DrmConnector::UpdateCrtcId(IdMapPtr<DrmEncoder> &encoders,
255    IdMapPtr<DrmCrtc> &crtcs, bool plugIn, drmModeConnectorPtr c, int *crtc_id)
256{
257    std::shared_ptr<DrmCrtc> crtc = nullptr;
258    int  encoderid = c->encoders[0];
259    auto encoderIter = encoders.find(encoderid);
260    if (encoderIter == encoders.end()) {
261        DISPLAY_LOGW("can not find encoder for id : %{public}d", encoderid);
262        return crtc;
263    }
264
265    auto &encoder = encoderIter->second;
266    int possibleCrtcs = encoder->GetPossibleCrtcs();
267
268    for (auto crtcIter = crtcs.begin(); crtcIter != crtcs.end(); ++crtcIter) {
269        auto &posCrts = crtcIter->second;
270        if (possibleCrtcs == (1<<posCrts->GetPipe())) {
271            DISPLAY_LOGD("find crtc id %{public}d, pipe %{public}d", posCrts->GetId(), posCrts->GetPipe());
272            crtc = posCrts;
273            *crtc_id = posCrts->GetId();
274        }
275    }
276    if (plugIn) {
277        encoder->SetCrtcId(*crtc_id);
278        mEncoderId = c->encoders[0];
279    } else if (!plugIn) {
280        *crtc_id = 0;
281        mEncoderId = 0;
282        encoder->SetCrtcId(0);
283    }
284    return crtc;
285}
286
287bool DrmConnector::HandleHotplug(IdMapPtr<DrmEncoder> &encoders,
288    IdMapPtr<DrmCrtc> &crtcs, bool plugIn)
289{
290    DISPLAY_LOGD("plug %{public}d", plugIn);
291    int drmFd = mDrmFdPtr->GetFd();
292    int ret;
293    int crtc_id = 0;
294    std::shared_ptr<DrmCrtc> crtc;
295    uint32_t blob_id;
296    drmModeAtomicReq *pset = drmModeAtomicAlloc();
297    DISPLAY_CHK_RETURN((pset == nullptr), DISPLAY_NULL_PTR,
298        DISPLAY_LOGE("drm atomic alloc failed errno %{public}d", errno));
299
300    drmModeConnectorPtr c = drmModeGetConnector(drmFd, mId);
301    DISPLAY_CHK_RETURN((c == nullptr), false, DISPLAY_LOGE("can not get connector"));
302    if (mConnectState == c->connection) {
303        drmModeFreeConnector(c);
304        return false;
305    } else {
306        crtc = UpdateCrtcId(encoders, crtcs, plugIn, c, &crtc_id);
307        if (crtc == nullptr) {
308            return DISPLAY_FAILURE;
309        }
310        DISPLAY_LOGD("get crtc id %{public}d ", crtc_id);
311
312        DrmVsyncWorker::GetInstance().EnableVsync(plugIn);
313        drmModeCreatePropertyBlob(drmFd, &c->modes[0],
314            sizeof(c->modes[0]), &blob_id);
315        ret = drmModeAtomicAddProperty(pset, crtc->GetId(), crtc->GetActivePropId(), (int)plugIn);
316        ret |= drmModeAtomicAddProperty(pset, crtc->GetId(), crtc->GetModePropId(), blob_id);
317        ret |= drmModeAtomicAddProperty(pset, GetId(), GetPropCrtcId(), crtc_id);
318        DISPLAY_CHK_RETURN((ret < 0), DISPLAY_FAILURE,
319            DISPLAY_LOGE("can not add the crtc id prop %{public}d", errno));
320
321        ret = drmModeAtomicCommit(drmFd, pset, DRM_MODE_ATOMIC_ALLOW_MODESET, nullptr);
322        DISPLAY_CHK_RETURN((ret < 0), DISPLAY_FAILURE,
323            DISPLAY_LOGE("can not add the crtc id prop %{public}d", errno));
324        drmModeAtomicFree(pset);
325
326        mConnectState = c->connection;
327        InitModes(*c);
328        drmModeFreeConnector(c);
329        return true;
330    }
331}
332
333int32_t DrmConnector::GetDisplaySupportedModes(uint32_t *num, DisplayModeInfo *modes)
334{
335    DISPLAY_CHK_RETURN((num == nullptr), DISPLAY_NULL_PTR, DISPLAY_LOGE("num is nullptr"));
336    *num = static_cast<int32_t>(mModes.size());
337    if (modes != nullptr) {
338        int i = 0;
339        for (const auto &modeMap : mModes) {
340            DrmMode mode = modeMap.second;
341            mode.ConvertToHdiMode(*(modes + i));
342            i++;
343        }
344    }
345    return DISPLAY_SUCCESS;
346}
347
348int32_t DrmConnector::SetDpmsState(uint64_t dmps)
349{
350    DISPLAY_LOGD("dmps %{public}" PRIu64 "", dmps);
351    int ret = drmModeConnectorSetProperty(mDrmFdPtr->GetFd(), mId, mPropDpmsId, dmps);
352    DISPLAY_CHK_RETURN((ret != 0), DISPLAY_FAILURE, DISPLAY_LOGE("can not set dpms"));
353    mDpmsState = dmps;
354    return DISPLAY_SUCCESS;
355}
356
357bool DrmConnector::IsConnected()
358{
359    return (mConnectState == DRM_MODE_CONNECTED);
360}
361
362int32_t DrmConnector::GetModeFromId(int32_t id, DrmMode &mode)
363{
364    DISPLAY_LOGD();
365    auto iter = mModes.find(id);
366    if (iter == mModes.end()) {
367        return DISPLAY_FAILURE;
368    }
369    mode = mModes[id];
370    return DISPLAY_SUCCESS;
371}
372
373std::unique_ptr<DrmModeBlock> DrmConnector::GetModeBlockFromId(int32_t id)
374{
375    DISPLAY_LOGD("id %{public}d", id);
376    auto iter = mModes.find(id);
377    DISPLAY_CHK_RETURN((iter == mModes.end()), nullptr, DISPLAY_LOGE("can not the mode %{public}d", id));
378    return std::make_unique<DrmModeBlock>(mModes[id]);
379}
380
381DrmModeBlock::DrmModeBlock(DrmMode &mode)
382{
383    DISPLAY_LOGD();
384    Init(mode);
385}
386
387int32_t DrmModeBlock::Init(DrmMode &mode)
388{
389    int ret;
390    int drmFd = DrmDevice::GetDrmFd();
391    DISPLAY_CHK_RETURN((drmFd < 0), DISPLAY_FAILURE, DISPLAY_LOGE("the drm fd is invalid"));
392    drmModeModeInfo modeInfo = *(mode.GetModeInfoPtr());
393    ret = drmModeCreatePropertyBlob(drmFd, static_cast<void *>(&modeInfo), sizeof(modeInfo), &mBlockId);
394    DISPLAY_CHK_RETURN((ret != 0), DISPLAY_FAILURE, DISPLAY_LOGE("create property blob failed"));
395    DISPLAY_LOGD("mBlockId %{public}d", mBlockId);
396    return DISPLAY_SUCCESS;
397}
398
399DrmModeBlock::~DrmModeBlock()
400{
401    DISPLAY_LOGD("mBlockId %{public}d", mBlockId);
402    int drmFd = DrmDevice::GetDrmFd();
403    if ((mBlockId != DRM_INVALID_ID) && (drmFd >= 0)) {
404        int ret = drmModeDestroyPropertyBlob(drmFd, mBlockId);
405        if (ret != 0) {
406            DISPLAY_LOGE("destroy property blob failed errno %{public}d", errno);
407        }
408    } else {
409        DISPLAY_LOGE("can not destruct the block id %{public}d drmFd %{public}d", mBlockId, drmFd);
410    }
411}
412} // namespace OHOS
413} // namespace HDI
414} // namespace DISPLAY
415