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