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