Files

428 lines
13 KiB
C++

/*
* Copyright (C) 2024 Amlogic Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <cstring>
#include <sys/ioctl.h>
#include <error.h>
#include <cinttypes>
#include <cerrno>
#include <vector>
#include <stdlib.h>
#include <drm_fourcc.h>
#include <BasicTypes.h>
#include <MesonLayer.h>
#include "V2DComposer.h"
#include "DispAdptLog.h"
V2DComposer::V2DComposer(int displayId, int devFd)
: IComposer() {
mEnable = false;
mDisplayID = displayId;
mDevFd = dup(devFd);
memset(&mVideoFramesInfo, 0, sizeof(mVideoFramesInfo));
mInputFbs.clear();
mOutputFb.reset();
}
V2DComposer::~V2DComposer() {
mInputFbs.clear();
mOutputFb.reset();
if (mDevFd >= 0)
close(mDevFd);
}
bool V2DComposer::isFbsSupport(
std::vector<std::shared_ptr<DrmFramebufferBase>> & fbs __unused,
std::vector<std::shared_ptr<DrmFramebufferBase>> & overlayfbs __unused,
int composeIdx __unused) {
return true;
}
int32_t V2DComposer::prepare() {
mOverlayFbs.clear();
return 0;
}
int32_t V2DComposer::addInputs(
std::vector<std::shared_ptr<DrmFramebufferBase>> & fbs,
std::vector<std::shared_ptr<DrmFramebufferBase>> & overlayfbs,
int composeIdx) {
mInputFbs = fbs;
mOverlayFbs = overlayfbs;
if (mInputFbs.empty())
return 0;
auto it = mInputFbs.begin();
if ((*it)->isRotated() || mInputFbs.size() > 1 || composeIdx > 0) {
if (mInputFbs.size() == 1) {
int32_t transform = (*it)->getTransform();
if (transform == TRANSFORM_FLIP_H || transform == TRANSFORM_FLIP_V ||
transform == TRANSFORM_ROT_180) {
DISPLAY_LOGV("V2DComposer(%d) %s transform %d VideoDisplay pass-through for 180 rotation or flip",
mDevFd, __func__, transform);
mInputFbs.clear();
return 0;
}
}
/* input fbs need do composition */
for (; it != mInputFbs.end(); it++) {
(*it)->setCompositionType(getType());
}
enable(true);
} else {
mInputFbs.clear();
}
return 0;
}
int32_t V2DComposer::getOverlyFbs(
std::vector<std::shared_ptr<DrmFramebufferBase>> & overlays) {
overlays = mOverlayFbs;
return 0;
}
int32_t V2DComposer::setOutput(std::shared_ptr<DrmFramebufferBase> & fb,
int composeIdx) {
UNUSED(composeIdx);
UNUSED(fb);
return 0;
}
int32_t V2DComposer::start(int composeIdx) {
UNUSED(composeIdx);
int i = 0;
int ret = -1;
float alpha;
drm_rect_t crtcRect;
drm_rect_t srcRect;
frame_info_t * vFrameInfo;
int inputFbSize = 0;
MesonLayer* fb = nullptr;
int updateFrameCount = 0;
if (mInputFbs.empty()) {
DISPLAY_LOGV("V2DComposer(%d), input buffers is empty", mDevFd);
return 0;
}
if (needSkipRotationInComposer()) {
mOutputFb = std::static_pointer_cast<MesonLayer>(mInputFbs[0]);
DISPLAY_LOGV("V2DComposer(%d) %s DPSS pass-through, skip V2D composer, layerId %" PRIu64,
mDevFd, __func__, mOutputFb->getUniqueId());
return 0;
}
auto baseFb = mInputFbs[0];
if (baseFb->getCompositionType() != getType()) {
/* mInputFbs have only one element and it not rotated */
mOutputFb = std::static_pointer_cast<MesonLayer>(baseFb);
return 0;
}
if (!mEnable) {
DISPLAY_LOGE("V2DComposer(%d), composer not enable", mDevFd);
return 0;
}
if (!mOutputFb.get()) {
DISPLAY_LOGE("V2DComposer(%d), not create output buffer", mDevFd);
return 0;
}
if (mOutputFb->getFbType() != DRM_FB_VIDEO_COMPOSE_OUTPUT) {
mOutputFb = std::make_shared<MesonLayer>();
mOutputFb->setFbType(DRM_FB_VIDEO_COMPOSE_OUTPUT);
}
memset(&mVideoFramesInfo, 0, sizeof(mVideoFramesInfo));
mVideoFramesInfo.out_buffer.fd = -1;
mVideoFramesInfo.out_buffer.fence_fd = -1;
inputFbSize = mInputFbs.size();
for (i = 0; i < inputFbSize; i++) {
uint32_t width;
uint32_t height;
uint32_t format;
uint32_t stride;
MesonLayer* fb = (MesonLayer*) (mInputFbs[i].get());
if (mVideoFramesInfo.frame_count >= MAX_LAYER_COUNT) {
DISPLAY_LOGW("V2DComposer(%d) start, More than Max composition video "
"layers, skip %" PRIu64 ", !!!", mDevFd, fb->getUniqueId());
continue;
}
if (fb->getBufferFd() < 0) {
DISPLAY_LOGD("V2DComposer(%d) setframes layerId(%" PRIu64 ") type(%s) invalid fd",
mDevFd, fb->getUniqueId(), drmFbTypeToString(fb->getFbType()));
continue;
}
if (!fb->getVisible()) {
DISPLAY_LOGV("layer(%" PRIu64 ") invisible, don't composer", fb->getUniqueId());
continue;
}
if (fb->isFbUpdated())
updateFrameCount++;
vFrameInfo = &mVideoFramesInfo.input_buffer[mVideoFramesInfo.frame_count];
mVideoFramesInfo.frame_count++;
crtcRect = fb->getDisplayFrame();
srcRect = fb->getSourceCrop();
alpha = fb->getPlaneAlpha();
fb->getBufferInfo(width, height, stride, format);
vFrameInfo->fd = fb->getBufferFd();
vFrameInfo->fence_fd = fb->getAcquireFenceFd();
vFrameInfo->buffer_w = width;
vFrameInfo->buffer_h = height;
vFrameInfo->align_w = stride;
vFrameInfo->align_h = 0;
vFrameInfo->buffer_format = format;
vFrameInfo->dst_x = crtcRect.left;
vFrameInfo->dst_y = crtcRect.top;
vFrameInfo->dst_w = crtcRect.right - crtcRect.left;
vFrameInfo->dst_h = crtcRect.bottom - crtcRect.top;
vFrameInfo->crop_x = srcRect.left;
vFrameInfo->crop_y = srcRect.top;
vFrameInfo->crop_w = srcRect.right - srcRect.left;
vFrameInfo->crop_h = srcRect.bottom - srcRect.top;
vFrameInfo->zorder = fb->getZorder();
vFrameInfo->transform = fb->getTransform();
vFrameInfo->alpha = int32_t(alpha * (MAX_ALPHA_VALUE - MIN_ALPHA_VALUE) + MIN_ALPHA_VALUE);
DISPLAY_LOGV("V2DComposer(%d) setframes layerId(%" PRIu64 ") z(%d) type(%s) "
"fd(%d) wxh(%dx%d) alignW(%d) f(%d) t(%d) axis(%s) crop(%s)",
mDevFd, fb->getUniqueId(), vFrameInfo->zorder, drmFbTypeToString(fb->getFbType()),
vFrameInfo->fd, width, height, stride, format, vFrameInfo->transform,
crtcRect.toString().c_str(), srcRect.toString().c_str());
}
if (mVideoFramesInfo.frame_count == 0) {
DISPLAY_LOGV("V2DComposer(%d) has no frame count", mDevFd);
mOutputFb->setBufferInfo(-1, -1);
mOutputFb->setVisible(false);
return -EINVAL;
}
if (updateFrameCount == 0) {
DISPLAY_LOGV("V2DComposer(%d) has no frame update", mDevFd);
mOutputFb->froceRefresh(false);
return -EINVAL;
}
ret = ioctl(mDevFd, V2D_IOCTL_SET_FRAMES, &mVideoFramesInfo);
for (int i = 0; i < mVideoFramesInfo.frame_count; i++) {
vFrameInfo = &mVideoFramesInfo.input_buffer[i];
if (vFrameInfo->fence_fd >= 0) {
close(vFrameInfo->fence_fd);
vFrameInfo->fence_fd = -1;
}
}
if (ret != 0) {
DISPLAY_LOGE("v2d composer ioctl error, %s(%d), mDevFd = %d",
strerror(errno), errno, mDevFd);
mOutputFb->setVisible(true);
return -1;
}
fillOutputFrameBuffer(mVideoFramesInfo.out_buffer);
mOutputFb->clearFlipData();
for (i = 0; i < (int)mInputFbs.size(); i++) {
fb = (MesonLayer *) (mInputFbs[i].get());
fb->setDiProcessorFd(-1);
std::map<uint64_t, meson_page_flip_data> fb_data = fb->getPageFlipData();
for (const auto& pair : fb_data) {
mOutputFb->setPageFlipData(
pair.first,
pair.second.context,
pair.second.callback,
fb->getBufferFd());
}
}
return 0;
}
std::shared_ptr<DrmFramebufferBase> V2DComposer::getOutput(int composeIdx) {
UNUSED(composeIdx);
return mOutputFb;
}
int32_t V2DComposer::fillOutputFrameBuffer(frame_info_t & buffer) {
drm_rect_t crtcRect;
drm_rect_t srcRect;
crtcRect.left = buffer.dst_x;
crtcRect.top = buffer.dst_y;
crtcRect.right = buffer.dst_x + buffer.dst_w;
crtcRect.bottom = buffer.dst_y + buffer.dst_h;
srcRect.left = buffer.crop_x;
srcRect.top = buffer.crop_y;
srcRect.right = buffer.crop_x + buffer.crop_w;
srcRect.bottom = buffer.crop_y + buffer.crop_h;
mOutputFb->setCompositionType(getType());
mOutputFb->setUniqueId(0);
mOutputFb->setBufferInfo(buffer.fd, buffer.fence_fd);
mOutputFb->setBufferInfo(buffer.buffer_w,
buffer.buffer_h,
buffer.align_w,
DRM_FORMAT_NV21);
mOutputFb->setSourceCrop(srcRect);
mOutputFb->setDisplayFrame(crtcRect);
mOutputFb->setPlaneAlpha(1);
mOutputFb->setZorder(1);
mOutputFb->updateZorder();
mOutputFb->setVisible(true);
DISPLAY_LOGV("V2DComposer(%d) output buffer: fd:%d, fence_Fd:%d, "
"w/h:(%dx%d), alignW:%d, crop(%s) displayFrame(%s)",
mDevFd, buffer.fd, buffer.fence_fd, buffer.buffer_w, buffer.buffer_h,
buffer.align_w, srcRect.toString().c_str(), crtcRect.toString().c_str());
return 0;
}
int32_t V2DComposer::setReleaseFence() {
int fence = -1;
int32_t ret = 0;
if (needSkipRotationInComposer())
return ret;
if (!mOutputFb.get() || !mOutputFb->isFbUpdated() || (mOutputFb->getBufferFd() < 0))
return ret;
/* this fence from drm video plane */
fence = mOutputFb->getCurReleaseFence();
for (auto it = mInputFbs.begin(); it != mInputFbs.end(); it++) {
if ((*it)->getBufferFd() < 0)
continue;
int releaseFence = (fence >= 0 ? dup(fence) : -1);
DISPLAY_LOGV("V2DComposer(%d) set release fence %d for layerId %" PRIu64,
mDevFd, releaseFence, (*it)->getUniqueId());
(*it)->setCurReleaseFence(releaseFence);
}
if (fence < 0) {
mOutputFb->mUpdated = false;
return ret;
}
release_info_t info = {
.fd = mOutputFb->getBufferFd(),
.fence_fd = fence,
};
if (ioctl(mDevFd, V2D_IOCTL_SET_FENCE, &info) != 0) {
DISPLAY_LOGE("V2DComposer(%d) error, set release fence error, %s(%d)",
mDevFd, strerror(errno), errno);
ret = errno;
} else {
DISPLAY_LOGV("V2DComposer(%d) set release fence %d for fd %d to V2D",
mDevFd, info.fence_fd, info.fd);
}
if (fence >= 0)
close(fence);
mOutputFb->mUpdated = false;
return ret;
}
int32_t V2DComposer::enable(bool enable) {
if (enable && needSkipRotationInComposer()) {
if (mEnable) {
int32_t val = 0;
DISPLAY_LOGD("V2DComposer(%d) disable", mDevFd);
if (ioctl(mDevFd, V2D_IOCTL_SET_ENABLE, &val) != 0) {
DISPLAY_LOGE("%s V2DComposer(%d) error, %s(%d)",
__func__, mDevFd, strerror(errno), errno);
return -errno;
}
mEnable = false;
}
DISPLAY_LOGV("V2DComposer(%d) %s DPSS pass-through, skip V2D hardware enable.",
mDevFd, __func__);
return 1;
}
if (mEnable != enable) {
int32_t val = enable ? 1 : 0;
DISPLAY_LOGD("V2DComposer(%d) %s", mDevFd, enable ? "enable" : "disable");
if (ioctl(mDevFd, V2D_IOCTL_SET_ENABLE, &val) != 0) {
DISPLAY_LOGE("%s V2DComposer(%d) error, %s(%d)",
enable ? "enable" : "disable", mDevFd, strerror(errno), errno);
return -errno;
}
mEnable = enable;
if (mEnable) {
mOutputFb = std::make_shared<MesonLayer>();
mOutputFb->setFbType(DRM_FB_VIDEO_COMPOSE_OUTPUT);
}
}
if (!enable) {
/* Any disable() clears composer-held refs. Covers:
* (1) DPSS pass-through: first enable(true) returns early and mEnable stays false;
* later enable(false) does not enter mEnable!=enable, but must still clear
* mInputFbs / mOutputFb (output may alias input).
* (2) Normal path: true->false after ioctl also hits this block.
*/
mInputFbs.clear();
mOutputFb.reset();
}
return 1;
}
bool V2DComposer::needSkipRotationInComposer() {
if (mInputFbs.size() != 1)
return false;
if ((*mInputFbs.begin())->isRotated()) {
if ((*mInputFbs.begin())->isDpssTransformFlagSet()) {
(*mInputFbs.begin())->setCompositionType(MESON_COMPOSITION_PLANE_HWCVIDEO);
return true;
} else {
(*mInputFbs.begin())->setCompositionType(getType());
}
}
return false;
}