1/*
2 * Copyright (c) 2021-2022 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#include "drm_driver.h"
16#include <cstdio>
17#include <unistd.h>
18#include "log/log.h"
19#include "securec.h"
20#include "ui_rotation.h"
21#include "updater_ui_const.h"
22
23namespace Updater {
24void DrmDriver::Flip(const uint8_t *buf)
25{
26    if (buff_.vaddr != MAP_FAILED) {
27        UiRotation::GetInstance().RotateBuffer(buf, buff_.vaddr, buff_.size);
28    }
29}
30
31void DrmDriver::Blank(bool blank)
32{
33    LOG(INFO) << "drm blank";
34}
35
36void DrmDriver::Exit(void)
37{
38    ModesetDestroyFb(&buff_);
39}
40
41void DrmDriver::GetGrSurface(GrSurface &surface)
42{
43    surface.width = static_cast<int>(buff_.width);
44    surface.height = static_cast<int>(buff_.height);
45    surface.rowBytes = 0;
46    surface.pixelBytes = 0;
47}
48
49int DrmDriver::ModesetCreateFb(struct BufferObject *bo)
50{
51    struct drm_mode_create_dumb create = {};
52    struct drm_mode_map_dumb map = {};
53    const int offsetNumber = 4;
54    uint32_t handles[offsetNumber] = {0};
55    uint32_t pitches[offsetNumber] = {0};
56    uint32_t offsets[offsetNumber] = {0};
57
58    /* create a dumb-buffer, the pixel format is XRGB888 */
59    const int pixelDepth = 32;
60    create.width = bo->width;
61    create.height = bo->height;
62    create.bpp = pixelDepth;
63    drmIoctl(fd_, DRM_IOCTL_MODE_CREATE_DUMB, &create);
64
65    /* bind the dumb-buffer to an FB object */
66    bo->pitch = create.pitch;
67    bo->size = create.size;
68    bo->handle = create.handle;
69
70    handles[0] = bo->handle;
71    pitches[0] = bo->pitch;
72    offsets[0] = 0;
73    int ret = drmModeAddFB2(fd_, bo->width, bo->height, DRM_FORMAT_ARGB8888, handles, pitches, offsets, &bo->fbId, 0);
74    if (ret) {
75        LOG(ERROR) << "[fbtest]failed to add fb (" << bo->width << "x" << bo->height << "): " << strerror(errno);
76        return -1;
77    }
78
79    /* map the dumb-buffer to userspace */
80    map.handle = create.handle;
81    drmIoctl(fd_, DRM_IOCTL_MODE_MAP_DUMB, &map);
82    bo->vaddr = static_cast<uint8_t *>(mmap(0, create.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, map.offset));
83    if (bo->vaddr == MAP_FAILED) {
84        LOG(ERROR) << "failed to mmap framebuffer";
85        return -1;
86    }
87    const uint32_t newColor = 0xff000000;
88    uint32_t i = 0;
89    uint32_t color = newColor;
90    while (i < bo->size) {
91        if (memcpy_s(&bo->vaddr[i], bo->size, &color, sizeof(color)) != EOK) {
92            return -1;
93        }
94        i += sizeof(color);
95    }
96    return 0;
97}
98
99drmModeCrtc *DrmDriver::GetCrtc(const drmModeRes &res, const int fd, const drmModeConnector &conn) const
100{
101    // if connector has one encoder, use it
102    drmModeEncoder *encoder = nullptr;
103    if (conn.encoder_id != 0) {
104        encoder = drmModeGetEncoder(fd, conn.encoder_id);
105    }
106    if (encoder != nullptr && encoder->crtc_id != 0) {
107        uint32_t crtcId = encoder->crtc_id;
108        drmModeFreeEncoder(encoder);
109        return drmModeGetCrtc(fd, crtcId);
110    }
111
112    if (encoder != nullptr) {
113        drmModeFreeEncoder(encoder);
114    }
115
116    // try get a vaild encoder and crtc
117    for (int i = 0; i < conn.count_encoders; i++) {
118        encoder = drmModeGetEncoder(fd, conn.encoders[i]);
119        if (encoder == nullptr) {
120            continue;
121        }
122
123        for (int j = 0; j < res.count_crtcs; j++) {
124            if ((encoder->possible_crtcs & (1u << static_cast<uint32_t>(j))) != 0) {
125                drmModeFreeEncoder(encoder);
126                return drmModeGetCrtc(fd, res.crtcs[j]);
127            }
128        }
129        drmModeFreeEncoder(encoder);
130    }
131    return nullptr;
132}
133
134drmModeConnector *DrmDriver::GetFirstConnector(const drmModeRes &res, const int fd) const
135{
136    // get connected connector
137    for (int i = 0; i < res.count_connectors; i++) {
138        drmModeConnector *conn = drmModeGetConnector(fd, res.connectors[i]);
139        if (conn == nullptr) {
140            continue;
141        }
142        if (conn->count_modes > 0 &&
143            conn->connection == DRM_MODE_CONNECTED) {
144            return conn;
145        }
146        drmModeFreeConnector(conn);
147    }
148    return nullptr;
149}
150
151drmModeConnector *DrmDriver::GetConnectorByType(const drmModeRes &res, const int fd, const uint32_t type) const
152{
153    // get connected connector
154    for (int i = 0; i < res.count_connectors; i++) {
155        drmModeConnector *conn = drmModeGetConnector(fd, res.connectors[i]);
156        if (conn == nullptr) {
157            continue;
158        }
159        if (conn->connector_type == type &&
160            conn->count_modes > 0 &&
161            conn->connection == DRM_MODE_CONNECTED) {
162            return conn;
163        }
164        drmModeFreeConnector(conn);
165    }
166    return nullptr;
167}
168
169
170drmModeConnector *DrmDriver::GetConnector(const drmModeRes &res, const int fd, uint32_t &modeId) const
171{
172    // get main connector : lvds edp and dsi
173    uint32_t mainConnector[] = {
174        DRM_MODE_CONNECTOR_LVDS,
175        DRM_MODE_CONNECTOR_eDP,
176        DRM_MODE_CONNECTOR_DSI,
177    };
178
179    drmModeConnector *conn = nullptr;
180    for (uint32_t i = 0; i < sizeof(mainConnector) / sizeof(mainConnector[0]); i++) {
181        conn = GetConnectorByType(res, fd, mainConnector[i]);
182        if (conn != nullptr) {
183            break;
184        }
185    }
186
187    if (conn == nullptr) {
188        conn = GetFirstConnector(res, fd);
189    }
190
191    if (conn == nullptr) {
192        LOG(ERROR) << "DrmDriver cannot get vaild connector";
193        return nullptr;
194    }
195
196    // get preferred mode index
197    modeId = 0;
198    for (int i = 0; i < conn->count_modes; i++) {
199        if ((conn->modes[i].type & DRM_MODE_TYPE_PREFERRED) != 0) {
200            modeId = static_cast<uint32_t>(i);
201            break;
202        }
203    }
204
205    return conn;
206}
207
208drmModeRes *DrmDriver::GetResources(int &fd) const
209{
210    // 1: open drm resource
211    drmModeRes *res = nullptr;
212    for (int i = 0; i < DRM_MAX_MINOR; i++) {
213        res = GetOneResources(i, fd);
214        if (res != nullptr) {
215            break;
216        }
217    }
218    return res;
219}
220
221drmModeRes *DrmDriver::GetOneResources(const int devIndex, int &fd) const
222{
223    // 1: open drm device
224    fd = -1;
225    std::string devName = std::string("/dev/dri/card") + std::to_string(devIndex);
226    int tmpFd = open(devName.c_str(), O_RDWR | O_CLOEXEC);
227    if (tmpFd < 0) {
228        LOG(ERROR) << "open failed " << devName;
229        return nullptr;
230    }
231    // 2: check drm capacity
232    uint64_t cap = 0;
233    int ret = drmGetCap(tmpFd, DRM_CAP_DUMB_BUFFER, &cap);
234    if (ret != 0 || cap == 0) {
235        LOG(ERROR) << "drmGetCap failed";
236        close(tmpFd);
237        return nullptr;
238    }
239
240    // 3: get drm resources
241    drmModeRes *res = drmModeGetResources(tmpFd);
242    if (res == nullptr) {
243        LOG(ERROR) << "drmModeGetResources failed";
244        close(tmpFd);
245        return nullptr;
246    }
247
248    // 4: check it has connected connector and crtc
249    if (res->count_crtcs > 0 && res->count_connectors > 0 && res->count_encoders > 0) {
250        drmModeConnector *conn = GetFirstConnector(*res, tmpFd);
251        if (conn != nullptr) {
252            // don't close fd
253            LOG(INFO) << "drm dev:" << devName;
254            drmModeFreeConnector(conn);
255            fd = tmpFd;
256            return res;
257        }
258    }
259    close(tmpFd);
260    drmModeFreeResources(res);
261    return nullptr;
262}
263
264int DrmDriver::DrmInit(void)
265{
266    // 1: open drm resource
267    res_ = GetResources(fd_);
268    if (fd_ < 0 || res_ == nullptr) {
269        LOG(ERROR) << "DrmInit: GetResources failed";
270        return -1;
271    }
272
273    // 2 : get connected connector
274    uint32_t modeId;
275    conn_ = GetConnector(*res_, fd_, modeId);
276    if (conn_ == nullptr) {
277        LOG(ERROR) << "DrmInit: GetConnector failed";
278        return -1;
279    }
280
281    // 3: get vaild encoder and crtc
282    crtc_ = GetCrtc(*res_, fd_, *conn_);
283    if (crtc_ == nullptr) {
284        LOG(ERROR) << "DrmInit: GetCrtc failed";
285        return -1;
286    }
287
288    // 4: create userspace buffer
289    buff_.width = conn_->modes[modeId].hdisplay;
290    buff_.height = conn_->modes[modeId].vdisplay;
291    ModesetCreateFb(&buff_);
292
293    // 5: bind ctrc and connector
294    drmModeSetCrtc(fd_, crtc_->crtc_id, buff_.fbId, 0, 0, &conn_->connector_id, 1, &conn_->modes[modeId]);
295    LOG(INFO) << "DrmInit: buff_.width:" << buff_.width << " buff_.height:" << buff_.height;
296    LOG(INFO) << "DrmInit: crtc_id:" << crtc_->crtc_id << " connector_id:" << conn_->connector_id;
297    LOG(INFO) << " drm init success.";
298    return 0;
299}
300
301bool DrmDriver::Init()
302{
303    // this static variable can guarantee Init be called only once
304    static bool res = [this] () {
305        if (DrmInit() == -1) {
306            LOG(ERROR) << "load drm driver fail";
307            return false;
308        }
309        return true;
310    } ();
311    return res;
312}
313
314void DrmDriver::ModesetDestroyFb(struct BufferObject *bo)
315{
316    if (fd_ > 0 && bo->fbId != 0) {
317        drmModeRmFB(fd_, bo->fbId);
318    }
319    if (bo->vaddr != MAP_FAILED) {
320        munmap(bo->vaddr, bo->size);
321    }
322    if (fd_ > 0) {
323        struct drm_mode_destroy_dumb destroy = {};
324        destroy.handle = bo->handle;
325        drmIoctl(fd_, DRM_IOCTL_GEM_CLOSE, &destroy);
326    }
327    if (crtc_ != nullptr) {
328        drmModeFreeCrtc(crtc_);
329    }
330    if (conn_ != nullptr) {
331        drmModeFreeConnector(conn_);
332    }
333    if (res_ != nullptr) {
334        drmModeFreeResources(res_);
335    }
336    if (fd_ > 0) {
337        close(fd_);
338        fd_ = -1;
339    }
340}
341
342DrmDriver::~DrmDriver()
343{
344    ModesetDestroyFb(&buff_);
345}
346} // namespace Updater
347