blob: 3da99ed378ded581fe85442b1470999b72b860b0 [file] [log] [blame]
fei.dengf7a0cd32023-08-29 09:36:37 +00001/*
2 * Copyright (C) 2021 Amlogic Corporation.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16#include <errno.h>
17#include <sys/mman.h>
18#include <xf86drm.h>
19#include <xf86drmMode.h>
20#include <drm_fourcc.h>
21#include <linux/videodev2.h>
22#include <meson_drm.h>
23#include "drm_display.h"
24#include "Logger.h"
25#include "drm_framepost.h"
26#include "drm_framerecycle.h"
fei.dengb9a1a572023-09-13 01:33:57 +000027#include "ErrorCode.h"
fei.dengf7a0cd32023-08-29 09:36:37 +000028
29using namespace Tls;
30
31#define TAG "rlib:drm_display"
32
33#define BLACK_FRAME_WIDTH 64
34#define BLACK_FRAME_HEIGHT 64
35
fei.dengb9a1a572023-09-13 01:33:57 +000036DrmDisplay::DrmDisplay(DrmPlugin *plugin, int logcategory)
37 : mPlugin(plugin),
38 mLogCategory(logcategory)
fei.dengf7a0cd32023-08-29 09:36:37 +000039{
40 mDrmHandle = NULL;
41 mBlackFrame = 0;
42 mIsPip = false;
43 mDrmFramePost = NULL;
44 mDrmFrameRecycle = NULL;
45 mVideoFormat = VIDEO_FORMAT_UNKNOWN;
46 mWinRect.x = 0;
47 mWinRect.y = 0;
48 mWinRect.w = 0;
49 mWinRect.h = 0;
50 mFrameWidth = 0;
51 mFrameHeight = 0;
52 mBlackFrame = NULL;
53 mBlackFrameAddr = NULL;
54 mHideVideo = false;
55 mIsDropFrames = false;
56 mKeepLastFrame = false;
fei.dengb9a1a572023-09-13 01:33:57 +000057 mDrmMesonLib = drmMesonLoadLib(logcategory);
fei.dengf7a0cd32023-08-29 09:36:37 +000058}
59
60DrmDisplay::~DrmDisplay()
61{
62 if (mDrmHandle) {
63 if (mDrmMesonLib) {
64 mDrmMesonLib->libDrmDisplayDestroy(mDrmHandle);
65 mDrmHandle = NULL;
66 }
67 }
68 //free dlopen resources
69 if (mDrmMesonLib) {
fei.dengb9a1a572023-09-13 01:33:57 +000070 drmMesonUnloadLib(mLogCategory, mDrmMesonLib);
fei.dengf7a0cd32023-08-29 09:36:37 +000071 mDrmMesonLib = NULL;
72 }
73}
74
75bool DrmDisplay::start(bool pip)
76{
77 mIsPip = pip;
78
fei.dengb9a1a572023-09-13 01:33:57 +000079 DEBUG(mLogCategory, "start pip:%d",pip);
fei.dengf7a0cd32023-08-29 09:36:37 +000080 if (mDrmMesonLib) {
81 mDrmHandle = mDrmMesonLib->libDrmDisplayInit();
82 }
83
84 if (!mDrmHandle) {
fei.dengb9a1a572023-09-13 01:33:57 +000085 ERROR(mLogCategory, "drm display init failed");
fei.dengf7a0cd32023-08-29 09:36:37 +000086 return false;
87 }
88#ifdef SUPPORT_DRM_FREEZEN
89 //set keep last frame if needed;
90 if (mKeepLastFrame) {
91 mDrmHandle->freeze = 1;
92 } else {
93 mDrmHandle->freeze = 0;
94 }
fei.dengb9a1a572023-09-13 01:33:57 +000095 INFO(mLogCategory, "set keep last frame %d", mDrmHandle->freeze);
fei.dengf7a0cd32023-08-29 09:36:37 +000096#endif
97
98 if (!mDrmFramePost) {
fei.dengb9a1a572023-09-13 01:33:57 +000099 mDrmFramePost = new DrmFramePost(this, mLogCategory);
fei.dengf7a0cd32023-08-29 09:36:37 +0000100 mDrmFramePost->start();
101 /*if window size set, we set to frame post,this needed
102 when yts resize case,window size must effect when post
103 buffer to drm
104 */
105 if (mWinRect.w > 0 && mWinRect.h > 0) {
106 mDrmFramePost->setWindowSize(mWinRect.x, mWinRect.y, mWinRect.w, mWinRect.h);
107 }
108 }
109
110 if (!mDrmFrameRecycle) {
fei.dengb9a1a572023-09-13 01:33:57 +0000111 mDrmFrameRecycle = new DrmFrameRecycle(this,mLogCategory);
fei.dengf7a0cd32023-08-29 09:36:37 +0000112 mDrmFrameRecycle->start();
113 }
114 return true;
115}
116
117bool DrmDisplay::stop()
118{
119 if (mDrmFramePost) {
fei.dengb9a1a572023-09-13 01:33:57 +0000120 DEBUG(mLogCategory, "stop frame post thread");
fei.dengf7a0cd32023-08-29 09:36:37 +0000121 mDrmFramePost->stop();
122 delete mDrmFramePost;
123 mDrmFramePost = NULL;
124 }
125 if (mDrmFrameRecycle) {
fei.dengb9a1a572023-09-13 01:33:57 +0000126 DEBUG(mLogCategory, "stop frame recycle thread");
fei.dengf7a0cd32023-08-29 09:36:37 +0000127 mDrmFrameRecycle->stop();
128 delete mDrmFrameRecycle;
129 mDrmFrameRecycle = NULL;
130 }
131 if (mBlackFrameAddr) {
132 munmap (mBlackFrameAddr, mBlackFrame->width * mBlackFrame->height * 2);
133 mBlackFrameAddr = NULL;
134 }
135 if (mBlackFrame) {
136 if (mDrmMesonLib) {
137 mDrmMesonLib->libDrmFreeBuf(mBlackFrame);
138 }
139 mBlackFrame = NULL;
140 }
141 if (mDrmHandle) {
142 if (mDrmMesonLib) {
143 mDrmMesonLib->libDrmDisplayDestroy(mDrmHandle);
144 mDrmHandle = NULL;
145 }
146 }
147 return true;
148}
149
150bool DrmDisplay::displayFrame(RenderBuffer *buf, int64_t displayTime)
151{
152 int ret;
153
154 FrameEntity *frameEntity = createFrameEntity(buf, displayTime);
155 if (frameEntity) {
156 if (mDrmFramePost) {
157 //if hided video and commit hide frame to drm,drop all frame
158 if (mIsDropFrames) {
159 handleDropedFrameEntity(frameEntity);
160 handleReleaseFrameEntity(frameEntity);
161 } else {
162 mDrmFramePost->readyPostFrame(frameEntity);
163 }
164 if (mHideVideo) {
165 mIsDropFrames = true;
166 }
167 } else {
fei.dengb9a1a572023-09-13 01:33:57 +0000168 WARNING(mLogCategory,"no frame post service");
fei.dengf7a0cd32023-08-29 09:36:37 +0000169 handleDropedFrameEntity(frameEntity);
170 handleReleaseFrameEntity(frameEntity);
171 }
172 } else { //drop and release render buffer
173 if (mPlugin) {
174 mPlugin->handleFrameDropped(buf);
175 mPlugin->handleBufferRelease(buf);
176 }
177 }
178
179 return true;
180}
181
182void DrmDisplay::flush()
183{
fei.dengb9a1a572023-09-13 01:33:57 +0000184 DEBUG(mLogCategory,"flush");
185 Tls::Mutex::Autolock _l(mMutex);
fei.dengf7a0cd32023-08-29 09:36:37 +0000186 if (mDrmFramePost) {
187 mDrmFramePost->flush();
188 }
189}
190
191void DrmDisplay::pause()
192{
193 mDrmFramePost->pause();
194}
195
196void DrmDisplay::resume()
197{
198 mDrmFramePost->resume();
199}
200
201void DrmDisplay::setVideoFormat(RenderVideoFormat videoFormat)
202{
203 mVideoFormat = videoFormat;
fei.dengb9a1a572023-09-13 01:33:57 +0000204 DEBUG(mLogCategory,"video format:%d",mVideoFormat);
fei.dengf7a0cd32023-08-29 09:36:37 +0000205}
206
207void DrmDisplay::setWindowSize(int x, int y, int w, int h)
208{
209 mWinRect.x = x;
210 mWinRect.y = y;
211 mWinRect.w = w;
212 mWinRect.h = h;
fei.dengb9a1a572023-09-13 01:33:57 +0000213 DEBUG(mLogCategory,"window size:(%dx%dx%dx%d)",x, y,w,h);
fei.dengf7a0cd32023-08-29 09:36:37 +0000214 if (mDrmFramePost) {
215 mDrmFramePost->setWindowSize(x, y, w, h);
216 }
217}
218
219void DrmDisplay::setFrameSize(int width, int height)
220{
221 mFrameWidth = width;
222 mFrameHeight = height;
fei.dengb9a1a572023-09-13 01:33:57 +0000223 DEBUG(mLogCategory,"frame size:%dx%d",width, height);
fei.dengf7a0cd32023-08-29 09:36:37 +0000224}
225
226void DrmDisplay::showBlackFrame()
227{
228 int rc;
229 struct drm_buf_metadata info;
230
231 memset(&info, 0 , sizeof(struct drm_buf_metadata));
232
233 /* use single planar for black frame */
234 info.width = BLACK_FRAME_WIDTH;
235 info.height = BLACK_FRAME_HEIGHT;
236 info.flags = 0;
237 info.fourcc = DRM_FORMAT_YUYV;
238
239 if (!mIsPip)
240 info.flags |= MESON_USE_VD1;
241 else
242 info.flags |= MESON_USE_VD2;
243
fei.deng942dff62023-10-18 04:21:10 +0000244 if (mDrmHandle && mDrmMesonLib && !mBlackFrame) {
fei.dengf7a0cd32023-08-29 09:36:37 +0000245 mBlackFrame = mDrmMesonLib->libDrmAllocBuf(mDrmHandle, &info);
246 }
247
248 if (!mBlackFrame) {
fei.dengb9a1a572023-09-13 01:33:57 +0000249 ERROR(mLogCategory,"Unable to alloc drm buf");
fei.dengf7a0cd32023-08-29 09:36:37 +0000250 goto tag_error;
251 }
252
fei.deng942dff62023-10-18 04:21:10 +0000253 if (!mBlackFrameAddr) {
254 mBlackFrameAddr = mmap (NULL, info.width * info.height * 2, PROT_WRITE,
255 MAP_SHARED, mBlackFrame->fd[0], mBlackFrame->offsets[0]);
256 if (mBlackFrameAddr == MAP_FAILED) {
257 ERROR(mLogCategory,"mmap fail %d", errno);
258 mBlackFrameAddr = NULL;
259 goto tag_error;
260 }
fei.dengf7a0cd32023-08-29 09:36:37 +0000261 }
262
263 /* full screen black frame */
264 memset (mBlackFrameAddr, 0, info.width * info.height * 2);
265 mBlackFrame->crtc_x = 0;
266 mBlackFrame->crtc_y = 0;
267 mBlackFrame->crtc_w = -1;
268 mBlackFrame->crtc_h = -1;
269
fei.deng942dff62023-10-18 04:21:10 +0000270 //disable plane,only this flag can take effect for black screen
fei.dengb9a1a572023-09-13 01:33:57 +0000271 mBlackFrame->disable_plane = 1;
272
fei.dengf7a0cd32023-08-29 09:36:37 +0000273 //post black frame buf to drm
274 if (mDrmHandle && mDrmMesonLib) {
275 rc = mDrmMesonLib->libDrmPostBuf(mDrmHandle, mBlackFrame);
276 }
277 if (rc) {
fei.dengb9a1a572023-09-13 01:33:57 +0000278 ERROR(mLogCategory, "post black frame to drm failed");
fei.dengf7a0cd32023-08-29 09:36:37 +0000279 goto tag_error;
280 }
281
282 return;
283tag_error:
284 if (mBlackFrameAddr) {
285 munmap (mBlackFrameAddr, mBlackFrame->width * mBlackFrame->height * 2);
286 mBlackFrameAddr = NULL;
287 }
288 if (mBlackFrame) {
289 if (mDrmMesonLib) {
290 mDrmMesonLib->libDrmFreeBuf(mBlackFrame);
291 }
fei.dengf7a0cd32023-08-29 09:36:37 +0000292 mBlackFrame = NULL;
293 }
294 return;
295}
296
297void DrmDisplay::setImmediatelyOutout(bool on)
298{
299 if (mDrmFramePost) {
300 mDrmFramePost->setImmediatelyOutout(on);
301 }
302}
303
304void DrmDisplay::setHideVideo(bool hide) {
fei.dengb9a1a572023-09-13 01:33:57 +0000305 INFO(mLogCategory,"hide video:%d",hide);
fei.dengf7a0cd32023-08-29 09:36:37 +0000306 mHideVideo = hide;
307 mIsDropFrames = false;
fei.deng942dff62023-10-18 04:21:10 +0000308 //if set hide video after paused,we should send a black frame to enable black screen
309 if (hide && mPaused) {
310 INFO(mLogCategory, "show black frame to drm");
311 showBlackFrame();
312 }
fei.dengf7a0cd32023-08-29 09:36:37 +0000313}
314
315void DrmDisplay::setKeepLastFrame(bool keep)
316{
317 int kp = keep? 1: 0;
318 mKeepLastFrame = keep;
319
320#ifdef SUPPORT_DRM_FREEZEN
321 if (mDrmHandle) {
322 mDrmHandle->freeze = kp;
323 }
fei.dengb9a1a572023-09-13 01:33:57 +0000324 INFO(mLogCategory, "set keep last frame %d(%d)", keep,mDrmHandle->freeze);
325 if (!kp) {
326 INFO(mLogCategory, "set keep last frame %d, show black frame to drm", kp);
327 showBlackFrame();
328 }
fei.dengf7a0cd32023-08-29 09:36:37 +0000329#endif
330}
331
332FrameEntity *DrmDisplay::createFrameEntity(RenderBuffer *buf, int64_t displayTime)
333{
334 struct drm_buf_import info;
335 FrameEntity* frame = NULL;
336 struct drm_buf * drmBuf = NULL;
fei.deng942dff62023-10-18 04:21:10 +0000337 int displayWidth = 0, displayHeight = 0;
fei.dengf7a0cd32023-08-29 09:36:37 +0000338
339 frame = (FrameEntity*)calloc(1, sizeof(FrameEntity));
340 if (!frame) {
fei.dengb9a1a572023-09-13 01:33:57 +0000341 ERROR(mLogCategory,"oom calloc FrameEntity mem failed");
fei.dengf7a0cd32023-08-29 09:36:37 +0000342 goto tag_error;
343 }
344
345 frame->displayTime = displayTime;
346 frame->renderBuf = buf;
347
348 memset(&info, 0 , sizeof(struct drm_buf_import));
349
350 info.width = buf->dma.width;
351 info.height = buf->dma.height;
352 info.flags = 0;
353 switch (mVideoFormat)
354 {
355 case VIDEO_FORMAT_NV21: {
356 info.fourcc = DRM_FORMAT_NV21;
357 } break;
358 case VIDEO_FORMAT_NV12: {
359 info.fourcc = DRM_FORMAT_NV12;
360 } break;
361
362 default: {
363 info.fourcc = DRM_FORMAT_YUYV;
fei.dengb9a1a572023-09-13 01:33:57 +0000364 WARNING(mLogCategory,"unknown video format, set to default YUYV format");
fei.dengf7a0cd32023-08-29 09:36:37 +0000365 }
366 }
367
368 if (!mIsPip) {
369 info.flags |= MESON_USE_VD1;
370 } else {
371 info.flags |= MESON_USE_VD2;
372 }
373
374 /*warning: must dup fd, because drm_free_buf will close buf fd
375 if not dup buf fd, fd will be double free*/
376 for (int i = 0; i < buf->dma.planeCnt; i++) {
377 info.fd[i] = dup(buf->dma.fd[i]);
fei.dengb9a1a572023-09-13 01:33:57 +0000378 TRACE(mLogCategory,"dup fd[%d]:%d->%d",i,buf->dma.fd[i],info.fd[i]);
fei.dengf7a0cd32023-08-29 09:36:37 +0000379 }
380
381 if (mDrmHandle && mDrmMesonLib) {
382 drmBuf = mDrmMesonLib->libDrmImportBuf(mDrmHandle, &info);
383 }
384
385 if (!drmBuf) {
fei.dengb9a1a572023-09-13 01:33:57 +0000386 ERROR(mLogCategory, "unable drm_import_buf");
fei.dengf7a0cd32023-08-29 09:36:37 +0000387 goto tag_error;
388 }
389
390 //set source content's size
391 drmBuf->src_x = 0;
392 drmBuf->src_y = 0;
393 drmBuf->src_w = buf->dma.width;
394 drmBuf->src_h = buf->dma.height;
395
396 /*default set window size to full screen
397 *if window size set,the crtc size will be reset
398 *before post drm buffer
399 */
fei.deng942dff62023-10-18 04:21:10 +0000400 //get current display mode
401 if (mDrmHandle && mDrmMesonLib) {
402 DisplayMode displayMode;
403 mDrmMesonLib->libDrmGetModeInfo(mDrmHandle->drm_fd, MESON_CONNECTOR_RESERVED, &displayMode);
404 displayWidth = displayMode.w;
405 displayHeight = displayMode.h;
406 ERROR(mLogCategory, "displayWidth:%d,displayHeight:%d",displayWidth,displayHeight);
407 }
fei.dengf7a0cd32023-08-29 09:36:37 +0000408 drmBuf->crtc_x = 0;
409 drmBuf->crtc_y = 0;
fei.deng942dff62023-10-18 04:21:10 +0000410 if (displayWidth > 0 && displayHeight > 0) {
411 drmBuf->crtc_w = displayWidth;
412 drmBuf->crtc_h = displayHeight;
413 } else {
414 drmBuf->crtc_w = mDrmHandle->width;
415 drmBuf->crtc_h = mDrmHandle->height;
416 }
fei.dengf7a0cd32023-08-29 09:36:37 +0000417#ifdef SUPPORT_DRM_FREEZEN
418 if (mHideVideo) {
419 drmBuf->disable_plane = 1;
420 } else {
421 drmBuf->disable_plane = 0;
422 }
423#endif
424
425 frame->drmBuf = drmBuf;
fei.dengb9a1a572023-09-13 01:33:57 +0000426 TRACE(mLogCategory,"crtc(%d,%d,%d,%d),src(%d,%d,%d,%d)",drmBuf->crtc_x,drmBuf->crtc_y,drmBuf->crtc_w,drmBuf->crtc_h,
fei.dengf7a0cd32023-08-29 09:36:37 +0000427 drmBuf->src_x,drmBuf->src_y,drmBuf->src_w,drmBuf->src_h);
428
429 return frame;
430tag_error:
431 for (int i = 0; i < buf->dma.planeCnt; i++) {
432 if (info.fd[i] > 0) {
433 close(info.fd[i]);
434 info.fd[i] = -1;
435 }
436 }
437 if (frame) {
438 free(frame);
439 }
440 return NULL;
441}
442
443void DrmDisplay::destroyFrameEntity(FrameEntity * frameEntity)
444{
445 int rc;
446
447 if (!frameEntity) {
448 return;
449 }
450 if (frameEntity->drmBuf) {
451 if (mDrmMesonLib) {
452 rc = mDrmMesonLib->libDrmFreeBuf(frameEntity->drmBuf);
453 }
454
455 if (rc) {
fei.dengb9a1a572023-09-13 01:33:57 +0000456 WARNING(mLogCategory, "drm_free_buf free %p failed",frameEntity->drmBuf);
fei.dengf7a0cd32023-08-29 09:36:37 +0000457 }
fei.dengb9a1a572023-09-13 01:33:57 +0000458 TRACE(mLogCategory,"drm_free_buf displaytime:%lld(pts:%lld ms)",frameEntity->displayTime,frameEntity->renderBuf->pts/1000000);
fei.dengf7a0cd32023-08-29 09:36:37 +0000459 }
460 free(frameEntity);
461}
462
463void DrmDisplay::handlePostedFrameEntity(FrameEntity * frameEntity)
464{
465 if (mDrmFrameRecycle) {
466 mDrmFrameRecycle->recycleFrame(frameEntity);
467 }
468}
469
470void DrmDisplay::handleDropedFrameEntity(FrameEntity * frameEntity)
471{
472 if (mPlugin) {
473 mPlugin->handleFrameDropped(frameEntity->renderBuf);
474 }
475}
476
477void DrmDisplay::handleDisplayedFrameEntity(FrameEntity * frameEntity)
478{
479 if (mPlugin) {
480 mPlugin->handleFrameDisplayed(frameEntity->renderBuf);
481 }
482}
483
484void DrmDisplay::handleReleaseFrameEntity(FrameEntity * frameEntity)
485{
486 if (mPlugin) {
487 mPlugin->handleBufferRelease(frameEntity->renderBuf);
488 }
489 destroyFrameEntity(frameEntity);
490}
491
492void DrmDisplay::handleMsg(int type, void *detail)
493{
494 if (mPlugin) {
495 mPlugin->handleMsgNotify(type, detail);
496 }
497}