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