Files
BPI-CM9-Linux/aml-comp/vendor/amlogic/meson_videoserver/display/MesonDisplay.cpp
T

1472 lines
41 KiB
C++
Raw Normal View History

2026-08-13 11:28:40 +08:00
/*
* 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 <fcntl.h>
#include "MesonDisplay.h"
#include "V2DComposer.h"
#include "DisplayThread.h"
#include "MultiplanesComposition.h"
#include "DispAdptLog.h"
#include "MesonEventListener.h"
#include "VtAllocSolidColorBuffer.h"
2026-08-25 10:02:36 +08:00
#include "VideoTunnelDev.h"
2026-08-13 11:28:40 +08:00
2026-11-11 08:50:43 +08:00
#ifdef VSC_ENABLE
#include "VscalerEvent.h"
#endif
2026-08-13 11:28:40 +08:00
MesonDisplay::MesonDisplay(uint32_t display)
{
mDisplayId = display;
mDisplayToMainPlane = false;
2026-08-25 10:02:36 +08:00
mEnablePrimaryPlane = true;
2026-08-13 11:28:40 +08:00
mDelayLatency = 0;
2026-08-25 10:02:36 +08:00
mVsyncTimestamp = 0;
mLayerSeq = 0;
2026-08-13 11:28:40 +08:00
mPipe = std::make_shared<MesonDisplayPipe>(display);
mProcessorsManager = std::make_shared<ProcessorsManager>(
(display == 0) ? true : false);
2026-08-25 10:02:36 +08:00
mPipeAdapter = std::make_shared<VideoPipeAdapter>();
2026-08-13 11:28:40 +08:00
}
MesonDisplay::~MesonDisplay()
{
2026-08-25 10:02:36 +08:00
DISPLAY_LOGD("%s[%d]", __func__, __LINE__);
if (mPipe.get())
mPipe->destroyThread();
2026-08-13 11:28:40 +08:00
if (mDisplayThread.get())
2026-08-25 10:02:36 +08:00
mDisplayThread->destroyThread();
2026-08-13 11:28:40 +08:00
if (mVsync.get()) {
mVsync->setEnabled(false);
mVsync->setObserver(NULL);
mVsync.reset();
}
mLayers.clear();
2026-08-25 10:02:36 +08:00
for (auto planeIt = mPlanes.begin(); planeIt != mPlanes.end(); ++planeIt)
(*planeIt)->setPlane(NULL, 0, BLANK_FOR_NO_CONTENT);
2026-08-13 11:28:40 +08:00
mPlanes.clear();
mComposers.clear();
mCompositionStrategy.reset();
mPresentCompositionStg.reset();
2026-08-25 10:02:36 +08:00
mProcessorsManager.reset();
2026-08-13 11:28:40 +08:00
mPipe.reset();
2026-08-25 10:02:36 +08:00
mPipeAdapter.reset();
2026-08-13 11:28:40 +08:00
if (mListener.get())
mListener.reset();
2026-08-25 10:02:36 +08:00
VtAllocSolidColorBuffer::resetInstance();
VideoTunnelDev::resetInstanceForTest();
2026-08-13 11:28:40 +08:00
}
int32_t MesonDisplay::initialize()
{
std::vector<std::shared_ptr<IComposer>> composers;
const char *env = getenv("DISPLAYADAPTER_ENABLE_PRIMARY_PLANE");
2026-08-25 10:02:36 +08:00
if (env && atoi(env) != 1)
mEnablePrimaryPlane = false;
2026-08-13 11:28:40 +08:00
getComposers(composers);
for (auto it = composers.begin(); it != composers.end(); it++) {
mComposers.emplace(2, std::move(*it));
}
if (mPipe.get()) {
if (mEnablePrimaryPlane)
mPipe->enablePrimaryPlane();
mPipe->getCrtc(mCrtc);
mPipe->getConnector(mConnector);
mPipe->getPlanes(mPlanes);
for (auto & plane : mPlanes) {
plane->setCrtcId(mCrtc->getId());
}
2026-11-11 08:50:43 +08:00
#ifdef VSC_ENABLE
if (mCrtc.get() && mConnector.get())
mCrtc->setConnectorType(mConnector->getType());
#endif
2026-08-13 11:28:40 +08:00
}
mListener = std::make_shared<MesonEventListener>(this, ADPT_LISTENER_DIR);
mListener->registerHandler(IN_CLOSE_WRITE | IN_CREATE | IN_DELETE);
mListener->addWatch(ADPT_VIDEO, [this](const std::string& path, int mask) {
char data[STR_LEN] = { 0 };
int state = 0;
if (mask & IN_CLOSE_WRITE) {
DispAdptReadFile(path.c_str(), data, true, STR_LEN);
if (sscanf(data, "%d", &state) == 1)
this->setVideoPlaneState(state);
}
});
2026-08-25 10:02:36 +08:00
mListener->addWatch(DEBUG_LOG, [this](const std::string& path, int mask) {
char data[STR_LEN] = { 0 };
if (mask & IN_CLOSE_WRITE) {
DispAdptReadFile(path.c_str(), data, true, STR_LEN);
DispAdptDebugSetting(data);
}
});
2026-08-13 11:28:40 +08:00
mCompositionStrategy = std::make_shared<MultiplanesComposition>();
std::shared_ptr<HwDisplayCrtcBase> crtc =
std::static_pointer_cast<HwDisplayCrtcBase>(mCrtc);
std::shared_ptr<HwcVsyncBase> mesonVsync =
std::make_shared<HwcVsyncBase>(DISPLAY_DEFAULT);
mesonVsync->setVsyncMode(VSYNC_MODE_HW, crtc);
setVsync(mesonVsync);
if (!mDisplayThread.get()) {
mDisplayThread = std::make_shared<DisplayThread>(this);
}
mesonVsync->setEnabled(true);
initLayerIdGenerator();
return 0;
}
int32_t MesonDisplay::createV2DComposer(int idx,
std::shared_ptr<IComposer> & composer) {
int ret = 0;
int fd = -1;
char path[64] = { 0 };
snprintf(path, 64, "/dev/v2d.%u", idx);
fd = open(path, O_RDWR, 0);
if (fd >= 0) {
DISPLAY_LOGD("create V2DComposer for %s succeed", path);
composer = std::make_shared<V2DComposer>(mDisplayId, fd);
} else {
DISPLAY_LOGE("create V2DComposer for %s failed: %s", path, strerror(errno));
ret = -1;
}
2026-08-25 10:02:36 +08:00
if (fd >= 0)
close(fd);
2026-08-13 11:28:40 +08:00
return ret;
}
void MesonDisplay::getComposers(std::vector<std::shared_ptr<IComposer>> & composers)
{
int idx = 0;
int ret;
std::shared_ptr<IComposer> composer;
while (true) {
char path[64];
snprintf(path, 64, "/dev/v2d.%u", idx);
if (access(path, R_OK | W_OK) == 0)
idx ++;
else
break;
}
int id = mEnablePrimaryPlane ? 0 : 1;
composers.clear();
for (auto i = id; i < idx; i++) {
ret = createV2DComposer(i, composer);
if (ret)
return;
else
composers.push_back(std::move(composer));
}
}
/*
LayerId is 16bits.
Higher 8 Bits: now is 256 layer slot, the higher 8 bits is the slot index.
Lower 8 Bits: a sequence no, used to distinguish layers have same slot index (
which may happened a layer destroyed and at the same time a new layer created.)
*/
#define MAX_MESON_LAYERS (256)
#define LAYER_SLOT_BITS (8)
void MesonDisplay::initLayerIdGenerator()
{
mLayersBitmap = std::make_shared<BitsMap>(MAX_MESON_LAYERS);
mLayerSeq = 0;
}
uint32_t MesonDisplay::createLayerId()
{
uint32_t layerId = 0;
int idx = mLayersBitmap->getZeroBit();
mLayersBitmap->setBit(idx);
mLayerSeq++;
mLayerSeq %= MAX_MESON_LAYERS;
layerId = ((idx & (MAX_MESON_LAYERS - 1)) << LAYER_SLOT_BITS) | mLayerSeq;
return layerId;
}
void MesonDisplay::destroyLayerId(uint32_t id)
{
int slotIdx = id >> LAYER_SLOT_BITS;
mLayersBitmap->clearBit(slotIdx);
}
2026-08-25 10:02:36 +08:00
void MesonDisplay::registerCallback(int32_t layerId, void *context, meson_callback_t *cb)
{
std::lock_guard<std::mutex> lock(mMutex);
auto layerIt = mLayers.find(layerId);
if (layerIt != mLayers.end())
layerIt->second->registerCallback(context, cb);
}
void MesonDisplay::registerPageFlipCallBack(int32_t layerId, void *context, adpt_page_flip_callback *cb)
{
std::lock_guard<std::mutex> lock(mMutex);
auto layerIt = mLayers.find(layerId);
if (layerIt != mLayers.end())
layerIt->second->registerPageFlipCallBack(context, cb);
if (mPipe.get())
mPipe->drmPageFlipInit();
}
2026-08-13 11:28:40 +08:00
int MesonDisplay::createLayer(int32_t &outLayer)
{
std::lock_guard<std::mutex> lock(mMutex);
std::shared_ptr<MesonLayer> layer = std::make_shared<MesonLayer>(mDisplayId);
uint32_t idx = createLayerId();
outLayer = idx;
layer->setUniqueId(outLayer);
mLayers.emplace(outLayer, layer);
return MESON_ERROR_NONE;
}
int MesonDisplay::destroyLayer(int32_t inLayer)
{
std::lock_guard<std::mutex> lock(mMutex);
auto layerit = mLayers.find(inLayer);
if (layerit == mLayers.end())
return MESON_ERROR_BAD_LAYER;
std::shared_ptr<MesonLayer> layer = layerit->second;
mLayers.erase(inLayer);
destroyLayerId(inLayer);
2026-08-25 10:02:36 +08:00
DISPLAY_LOGD("%s[%d]: layer: %d", __func__, __LINE__, inLayer);
2026-08-13 11:28:40 +08:00
if (layer.get() && !layer->isVtBuffer())
layer->releaseResource();
if (layer.get() && layer->isVtBuffer())
layer->releaseVtResource();
2026-08-25 10:02:36 +08:00
if (mPipe.get())
mPipe->destroyPageFlip(inLayer);
if (layer->keepFrame()) {
for (auto planeIt = mPlanes.begin(); planeIt != mPlanes.end(); ++planeIt)
(*planeIt)->setKeepFrame();
return MESON_ERROR_HAS_CHANGES;
}
2026-08-13 11:28:40 +08:00
return MESON_ERROR_NONE;
}
int32_t MesonDisplay::setTunnelId(int32_t layer, int32_t tunnelId)
{
std::lock_guard<std::mutex> lock(mMutex);
std::shared_ptr<VtDisplayObserver> observer = std::make_shared<VtDisplayObserver>(this);
2026-08-25 10:02:36 +08:00
if (tunnelId < 128 && tunnelId >= 0)
VtAllocSolidColorBuffer::getInstance().allocBuffer();
2026-08-13 11:28:40 +08:00
auto layerIt = mLayers.find(layer);
if (layerIt != mLayers.end())
2026-08-25 10:02:36 +08:00
return layerIt->second->setTunnelId(tunnelId, std::move(observer));
2026-08-13 11:28:40 +08:00
return -1;
}
int32_t MesonDisplay::setLayerIndex(int32_t layer, int32_t index)
{
std::lock_guard<std::mutex> lock(mMutex);
auto layerIt = mLayers.find(layer);
2026-08-25 10:02:36 +08:00
if (layerIt != mLayers.end()) {
if (index < MAX_LAYER_NUMBER && !(mState & (1 << index)))
layerIt->second->setVisible(false);
2026-08-13 11:28:40 +08:00
return layerIt->second->setIndex(index);
2026-08-25 10:02:36 +08:00
}
2026-08-13 11:28:40 +08:00
return -1;
}
int32_t MesonDisplay::setLayerZorder(int32_t layer, int32_t zorder)
{
std::lock_guard<std::mutex> lock(mMutex);
auto layerIt = mLayers.find(layer);
if (layerIt != mLayers.end())
return layerIt->second->setZorder(zorder);
return -1;
}
int32_t MesonDisplay::setLayerSourceCrop(int32_t layerId, struct rect crop)
{
std::lock_guard<std::mutex> lock(mMutex);
drm_rect_t layer_crop;
auto layerIt = mLayers.find(layerId);
layer_crop.left = crop.x;
layer_crop.top = crop.y;
layer_crop.right = crop.x + crop.w;
layer_crop.bottom = crop.y + crop.h;
if (layerIt != mLayers.end())
return layerIt->second->setSourceCrop(layer_crop);
return -1;
}
int32_t MesonDisplay::setLayerDispFrame(int32_t layer, drm_rect_t frame)
{
std::lock_guard<std::mutex> lock(mMutex);
auto layerIt = mLayers.find(layer);
if (layerIt != mLayers.end()) {
return layerIt->second->setDisplayFrame(frame);
}
return -1;
}
void MesonDisplay::setVsyncDelayLatency(int32_t latency)
{
2026-11-11 08:50:43 +08:00
#ifdef VSC_ENABLE
int delayTime = getVideoDelay(DEFAULT_FPS);
if (mDelayLatency != delayTime) {
mDelayLatency = delayTime;
uint8_t dat[3] = {0};
uint16_t time = delayTime;
VscEventType vEventType = { 0 };
VscalerEvent *vEvent = &(VscalerEvent::getInstance());
dat[1] = time & 0xff;
dat[2] = (time >> 8) & 0xff;
vEventType = vEvent->makeEventData(mDisplayId,
V4L2_CID_EXT_VSC_SUBSCRIBE_VIDEO_DELAY,
dat, 3);
vEvent->addEventToQueue(vEventType);
}
#else
2026-08-13 11:28:40 +08:00
mDelayLatency = latency;
2026-11-11 08:50:43 +08:00
#endif
2026-08-13 11:28:40 +08:00
}
int32_t MesonDisplay::getVsyncDelayLatency(int32_t layer)
{
return mDelayLatency;
}
void MesonDisplay::setVideoPlaneState(int state)
{
std::lock_guard<std::mutex> lock(mMutex);
std::string value = VIDEO_ENABLE;
bool visible = true;
int index = -1;
int change = mState != -1 ? mState ^ state : 0xf;
if (change != 0) {
if (state == 0) {
value = VIDEO_DISABLE;
}
if (mDisableValue != value)
DispAdptWriteFile(DISABLE_VIDEO, value.c_str());
2026-08-25 10:02:36 +08:00
mDisableValue = std::move(value);
2026-08-13 11:28:40 +08:00
for (auto it = mLayers.begin(); it != mLayers.end(); it++) {
index = it->second->getIndex();
if (index < 0 || index >= MAX_LAYER_NUMBER)
continue;
if ((change & (1U << BASE_SHIFT_LEFT(index))) == 0)
continue;
visible = false;
if (state & (1U << BASE_SHIFT_LEFT(index)))
visible = true;
it->second->setVisible(visible);
}
}
mState = state;
DISPLAY_LOGV("%s[%d]: state: 0x%x, change: 0x%x", __func__, __LINE__, state, change);
}
int32_t MesonDisplay::getUiSize(int32_t* width, int32_t* height)
{
2026-08-25 10:02:36 +08:00
if (width == nullptr || height == nullptr) {
DISPLAY_LOGE("Invalid pointer: width or height is null");
2026-08-13 11:28:40 +08:00
return -1;
}
2026-08-25 10:02:36 +08:00
2026-08-13 11:28:40 +08:00
*width = DISPLAY_WIDTH_BASE;
*height = DISPLAY_HEIGHT_BASE;
return 0;
}
int32_t MesonDisplay::setVideoDisplaytoMainplane()
{
char path_select[MESON_MAX_STR_LEN] = { 0 };
int vd1, vd2, vd3;
int ret;
if (mEnablePrimaryPlane)
return 0;
ret = DispAdptReadFile(VIDEO_PATH_SELECT, path_select, true, MESON_MAX_STR_LEN);
if (ret) {
DISPLAY_LOGE("read file(%s) fail", VIDEO_PATH_SELECT);
return ret;
}
if (sscanf(path_select, "vd1: %d vd2: %d, vd3: %d", &vd1, &vd2, &vd3) == 3) {
memset(path_select, 0, sizeof(path_select));
sprintf(path_select, "%d %d %d", vd2, vd1, vd3);
DispAdptWriteFile(VIDEO_PATH_SELECT, path_select);
DispAdptWriteFile(DISABLE_VIDEO, "0");
DispAdptWriteFile(VIDEO_TEST_PATTERN, "0 1 0xff");
DispAdptWriteFile(VIDEO_TEST_PATTERN, "0 0 0xff");
}
return 0;
}
2026-08-25 10:02:36 +08:00
int32_t MesonDisplay::setLayerTransform(int32_t layer, int32_t transform)
{
std::lock_guard<std::mutex> lock(mMutex);
auto layerIt = mLayers.find(layer);
if (layerIt != mLayers.end()) {
switch (transform) {
case ROTATION_90:
transform = TRANSFORM_ROT_90;
break;
case ROTATION_270:
transform = TRANSFORM_ROT_270;
break;
case ROTATION_180:
transform = TRANSFORM_ROT_180;
break;
default:
transform = TRANSFORM_NONE;
break;
}
return layerIt->second->setTransform(transform);
}
return -1;
}
int32_t MesonDisplay::getVideoDelay(int fps)
{
2026-11-11 08:50:43 +08:00
int refresh = 0;
int video_delay = 0;
int vsync_time = 0;
int drm_delay = 0;
2026-08-25 10:02:36 +08:00
2026-11-11 08:50:43 +08:00
if (mPipe.get())
refresh = mPipe->getVrefresh();
2026-08-25 10:02:36 +08:00
2026-11-11 08:50:43 +08:00
vsync_time = refresh > 0 ? 1000 / refresh : 0;
drm_delay = 3 * vsync_time;
2026-08-25 10:02:36 +08:00
2026-11-11 08:50:43 +08:00
if (mPipeAdapter.get())
video_delay = mPipeAdapter->getVideoDelay(fps, refresh * 1000);
if (video_delay < 0)
video_delay = 0;
if (video_delay > std::numeric_limits<int>::max() - drm_delay)
video_delay = 0;
return video_delay + drm_delay;
2026-08-25 10:02:36 +08:00
}
int32_t MesonDisplay::waitVBlank()
{
if (!mCrtc.get()) {
return -EFAULT;
}
std::shared_ptr<DrmCrtc> localCrtc = mCrtc;
nsecs_t vsyncTime = 0;
int ret = localCrtc->waitVBlank(vsyncTime);
localCrtc.reset();
return ret;
}
int MesonDisplay::getLayerTotal()
{
std::lock_guard<std::mutex> lock(mMutex);
return mLayers.size();
}
2026-08-13 11:28:40 +08:00
int32_t MesonDisplay::setVsync(std::shared_ptr<HwcVsyncBase> vsync)
{
std::lock_guard<std::mutex> lock(mMutex);
int vsyncType = vsync->getVsyncType();
switch (vsyncType) {
case DISPLAY_DEFAULT:
if (mVsync != vsync) {
if (mVsync.get()) {
mVsync->setEnabled(false);
mVsync->setObserver(NULL);
} else {
2026-08-25 10:02:36 +08:00
mVsync = std::move(vsync);
2026-08-13 11:28:40 +08:00
mVsync->setObserver(this);
}
}
break;
default:
DISPLAY_LOGE("Get invalid vsync");
break;
}
return 0;
}
void MesonDisplay::onVsync(int64_t timestamp, uint32_t vsyncPeriodNanos, int vsyncType)
{
switch (vsyncType) {
case DISPLAY_DEFAULT:
mVsyncTimestamp = timestamp;
if (mDisplayThread.get()) {
mDisplayThread->onVsync(timestamp, vsyncPeriodNanos);
} else {
DISPLAY_LOGE("MesonDisplay (%p) mDisplayThread is NULL", this);
}
break;
default:
DISPLAY_LOGE("onVsync get invalid vsync");
break;
}
}
void MesonDisplay::setLayersPresentTime()
{
std::lock_guard<std::mutex> vtLock(mMutex);
nsecs_t period = 1e9 / 60;
nsecs_t expectPresentedTime = mVsyncTimestamp + period;
for (auto it = mLayers.begin(); it != mLayers.end(); it++) {
// expectPresent time is current vsync timestamp + 1 vsyncPeriod
it->second->setPresentTime(expectPresentedTime);
}
}
int MesonDisplay::collectLayersForPresent() {
/*
* 1) add reference to Layers to keep it alive during display.
* 2) for special layer, update its composition type.
* 3) sort Layers by zorder for composition.
*/
mPresentLayers.reserve(10);
for (auto it = mLayers.begin(); it != mLayers.end(); it++) {
mPresentLayers.push_back(it->second);
mFbs.insert(make_pair(it->second->getCurZorder(), it->second));
}
return MESON_ERROR_NONE;
}
int MesonDisplay::collectPlanesForPresent() {
mPresentPlanes = mPlanes;
return MESON_ERROR_NONE;
}
int MesonDisplay::collectComposersForPresent() {
for (auto it = mComposers.begin(); it != mComposers.end(); it++) {
mPresentComposers.push_back(it->second);
}
return MESON_ERROR_NONE;
}
int MesonDisplay::collectCompositionStgForPresent() {
if (!mCompositionStrategy.get())
return MESON_ERROR_BAD_PARAMETER;
mPresentCompositionStg = mCompositionStrategy;
return MESON_ERROR_NONE;
}
int MesonDisplay::validateDisplay(uint32_t* outNumTypes,
uint32_t* outNumRequests)
{
std::lock_guard<std::mutex> lock(mMutex);
bool updateZ = true;
mPresentLayers.clear();
mPresentComposers.clear();
mPresentPlanes.clear();
mPresentCompositionStg.reset();
mFbs.clear();
int ret = collectLayersForPresent();
if (ret != MESON_ERROR_NONE) {
return ret;
}
ret = collectComposersForPresent();
if (ret != MESON_ERROR_NONE) {
return ret;
}
ret = collectPlanesForPresent();
if (ret != MESON_ERROR_NONE) {
return ret;
}
ret = collectCompositionStgForPresent();
if (ret != MESON_ERROR_NONE) {
return ret;
}
if (mFbs.size() != mLayers.size())
updateZ = false;
/*setup composition strategy.*/
std::vector<std::shared_ptr<DrmFramebufferBase>> fbs;
auto fbIt = mPresentLayers.begin();
for (; fbIt != mPresentLayers.end(); fbIt++) {
if (updateZ)
(*fbIt)->updateZorder();
fbs.push_back(std::static_pointer_cast<DrmFramebufferBase>(*fbIt));
}
std::vector<std::shared_ptr<HwDisplayPlaneBase>> planes;
auto planeIt = mPresentPlanes.begin();
for (; planeIt != mPresentPlanes.end(); planeIt++) {
if ((*planeIt)->getPrimary() || !mDisplayToMainPlane)
planes.push_back(std::static_pointer_cast<HwDisplayPlaneBase>(*planeIt));
}
2026-08-25 10:02:36 +08:00
std::list<DisplayPair> prevDisplayPairs;
std::list<DisplayPair> currDisplayPairs;
mPresentCompositionStg->getDisplayPair(prevDisplayPairs);
2026-08-13 11:28:40 +08:00
std::shared_ptr<HwDisplayCrtcBase> crtc =
std::static_pointer_cast<HwDisplayCrtcBase>(mCrtc);
mPresentCompositionStg->setup(fbs, mPresentComposers,
planes, crtc, 0, 0, { 0 });
if (mPresentCompositionStg->decideComposition() < 0)
return MESON_ERROR_NO_RESOURCES;
2026-08-25 10:02:36 +08:00
mPresentCompositionStg->getDisplayPair(currDisplayPairs);
if (currDisplayPairs != prevDisplayPairs) {
for (auto displayIt = currDisplayPairs.begin(); displayIt != currDisplayPairs.end(); ++displayIt) {
std::shared_ptr<MesonLayer> fb =
std::static_pointer_cast<MesonLayer>(*(displayIt->fbs.begin()));
fb->froceRefresh(true);
}
}
if (mProcessorsManager.get()) {
std::list<DisplayPair> displayPairs;
mPresentCompositionStg->getDisplayPair(displayPairs);
mProcessorsManager->setup(std::move(displayPairs), 0);
}
2026-08-13 11:28:40 +08:00
return ret;
}
int MesonDisplay::presentDisplay(int32_t* outPresentFence)
{
int ret;
2026-08-25 10:02:36 +08:00
std::lock_guard<std::mutex> lock(mMutex);
2026-08-13 11:28:40 +08:00
bool mainThread = !hasVtBuffer();
if (mPresentCompositionStg.get() == NULL)
return MESON_ERROR_BAD_PARAMETER;
if (mProcessorsManager.get())
mProcessorsManager->runVideoProcessors(mainThread);
ret = mPresentCompositionStg->commit();
2026-08-25 10:02:36 +08:00
if (mProcessorsManager.get()) {
2026-08-13 11:28:40 +08:00
mProcessorsManager->setVideoProcessorReleaseFence(ret != 0, mainThread);
2026-08-25 10:02:36 +08:00
mProcessorsManager->restoretProcessorFromFreeToUnused();
}
2026-08-13 11:28:40 +08:00
return ret;
}
bool MesonDisplay::handleDisplayConnection()
{
std::lock_guard<std::mutex> vtLock(mMutex);
std::shared_ptr<MesonLayer> layer;
2026-08-25 10:02:36 +08:00
bool ret = false;
2026-08-13 11:28:40 +08:00
for (auto it = mLayers.begin(); it != mLayers.end(); it++) {
layer = it->second;
if (!layer->isVtBuffer())
layer->updateBuffer();
else
layer->updateVtBuffer();
2026-08-25 10:02:36 +08:00
ret = true;
2026-08-13 11:28:40 +08:00
}
2026-08-25 10:02:36 +08:00
return ret;
2026-08-13 11:28:40 +08:00
}
void MesonDisplay::releaseLayers()
{
std::lock_guard<std::mutex> vtLock(mMutex);
int ret;
std::shared_ptr<MesonLayer> layer;
for (auto it = mLayers.begin(); it != mLayers.end(); it++) {
layer = it->second;
if (!layer->isVtBuffer()) {
ret = layer->releaseBuffer();
} else {
ret = layer->releaseVtBuffer();
}
if (ret != 0 && ret != -EAGAIN) {
DISPLAY_LOGE("%s, release layer id=%" PRIu64 " failed, ret=%d",
__func__, layer->getUniqueId(), ret);
}
}
}
/*
* whether the videotunnel layer has new game buffer
*/
bool MesonDisplay::newGameBuffer() {
std::lock_guard<std::mutex> vtLock(mMutex);
for (auto it = mLayers.begin(); it != mLayers.end(); it++) {
auto layer = it->second;
if (layer && layer->newGameBuffer())
return true;
}
return false;
}
void MesonDisplay::onFrameAvailable() {
if (mDisplayThread.get())
mDisplayThread->onFrameAvailableForGameMode();
}
bool MesonDisplay::hasVtBuffer() {
for (auto it = mLayers.begin(); it != mLayers.end(); it++) {
auto layer = it->second;
if (layer && layer->isVtBuffer())
return true;
}
return false;
}
2026-11-11 08:50:43 +08:00
#ifdef VSC_ENABLE
std::shared_ptr<MesonLayer> MesonDisplay::getLayerById(int id)
{
std::unordered_map<int, std::shared_ptr<MesonLayer>>::iterator it =
mLayers.find(id);
if (it != mLayers.end())
return it->second;
return NULL;
}
int32_t MesonDisplay::getLayerWinRegion(int32_t layerId,
struct v4l2_ext_vsc_win_region &win)
{
std::lock_guard<std::mutex> lock(mMutex);
std::shared_ptr<MesonLayer> layer = getLayerById(layerId);
if (!layer)
return MESON_ERROR_BAD_LAYER;
drm_rect_t res = layer->getSourceResolution();
drm_rect_t crop = layer->getSourceCrop();
drm_rect_t frame = layer->getDisplayFrame();
drm_rect_t punch = layer->getSourcePunch();
int rotation = 0;
switch (layer->getTransform()) {
case TRANSFORM_ROT_90:
rotation = ROTATION_90;
break;
case TRANSFORM_ROT_270:
rotation = ROTATION_270;
break;
case TRANSFORM_ROT_180:
rotation = ROTATION_180;
break;
default:
rotation = ROTATION_0;
break;
}
win.in.res.x = res.left;
win.in.res.y = res.top;
win.in.res.w = res.right - res.left;
win.in.res.h = res.bottom - res.top;
win.in.crop.x = crop.left;
win.in.crop.y = crop.top;
win.in.crop.w = crop.right - crop.left;
win.in.crop.h = crop.bottom - crop.top;
win.out.x = frame.left;
win.out.y = frame.top;
win.out.w = frame.right - frame.left;
win.out.h = frame.bottom - frame.top;
win.punch.x = punch.left;
win.punch.y = punch.top;
win.punch.w = punch.right - punch.left;
win.punch.h = punch.bottom - punch.top;
win.rotation = (enum v4l2_ext_vsc_rotation)rotation;
return 0;
}
int32_t MesonDisplay::setLayerOriginalResolution(int32_t layerId,
meson_rect_t rect, meson_rect_t win_punch)
{
std::lock_guard<std::mutex> lock(mMutex);
drm_rect_t res;
drm_rect_t punch;
std::shared_ptr<MesonLayer> layer = getLayerById(layerId);
if (!layer)
return MESON_ERROR_BAD_LAYER;
res.left = rect.left;
res.top = rect.top;
res.right = rect.right;
res.bottom = rect.bottom;
punch.left = win_punch.left;
punch.top = win_punch.top;
punch.right = win_punch.right;
punch.bottom = win_punch.bottom;
layer->setOriginalResolution(res, punch);
return 0;
}
int32_t MesonDisplay::getLayerZorder(int32_t layerId, struct v4l2_ext_vsc_zorder &z)
{
std::lock_guard<std::mutex> lock(mMutex);
std::shared_ptr<MesonLayer> layer = getLayerById(layerId);
if (!layer)
return MESON_ERROR_BAD_LAYER;
z.zorder = layer->getZorder();
z.alpha = layer->getPlaneAlpha() * 255;
return 0;
}
int32_t MesonDisplay::setLayerPlaneAlpha(int32_t layerId, float alpha)
{
std::lock_guard<std::mutex> lock(mMutex);
std::shared_ptr<MesonLayer> layer = getLayerById(layerId);
if (!layer)
return MESON_ERROR_BAD_LAYER;
return layer->setPlaneAlpha(alpha);
}
int32_t MesonDisplay::getLayerConnectInfo(int32_t layerId,
struct v4l2_ext_vsc_connect_info &info)
{
std::lock_guard<std::mutex> lock(mMutex);
std::shared_ptr<MesonLayer> layer = getLayerById(layerId);
if (!layer)
return MESON_ERROR_BAD_LAYER;
connect_info_t c_info = layer->getConnectInfo();
info.in.src = (enum v4l2_ext_vsc_input_src)c_info.src;
info.in.index = c_info.index;
info.in.attr = c_info.attr;
info.out = (enum v4l2_ext_vsc_dest)c_info.out;
return 0;
}
int32_t MesonDisplay::setLayerConnectInfo(int32_t layerId, int32_t src,
int32_t index, int32_t attr, int32_t out)
{
std::lock_guard<std::mutex> lock(mMutex);
connect_info_t c_info = {
.src = src,
.index = index,
.attr = attr,
.out = out,
};
std::shared_ptr<MesonLayer> layer = getLayerById(layerId);
if (!layer)
return MESON_ERROR_BAD_LAYER;
layer->setConnectInfo(c_info);
return 0;
}
int32_t MesonDisplay::getLayerWinProp(int32_t layerId,
struct v4l2_ext_vsc_win_prop &prop)
{
std::lock_guard<std::mutex> lock(mMutex);
std::shared_ptr<MesonLayer> layer = getLayerById(layerId);
if (!layer)
return MESON_ERROR_BAD_LAYER;
win_prop_t wprop = layer->getWinProp();
prop.win_mode = (enum v4l2_ext_vsc_win_mode)wprop.win_mode;
prop.mirror_mode = (enum v4l2_ext_vsc_mirror_mode)wprop.mirror_mode;
prop.mem_type = (enum v4l2_ext_vsc_memory_type)wprop.mem_type;
return 0;
}
int32_t MesonDisplay::setLayerWinProp(int32_t layerId, int32_t winmode,
int32_t mirrormode, int32_t memtype)
{
std::lock_guard<std::mutex> lock(mMutex);
std::shared_ptr<MesonLayer> layer = getLayerById(layerId);
if (!layer)
return MESON_ERROR_BAD_LAYER;
win_prop_t prop = {
.win_mode = winmode,
.mirror_mode = mirrormode,
.mem_type = memtype,
};
layer->setWinProp(prop);
return 0;
}
int32_t MesonDisplay::setAspectratio(int32_t layerId,
struct v4l2_ext_vsc_aspectratio_policy policy)
{
std::lock_guard<std::mutex> lock(mMutex);
std::shared_ptr<MesonLayer> layer = getLayerById(layerId);
if (!layer)
return MESON_ERROR_BAD_LAYER;
int wss_afd = 0;
struct v4l2_ext_video_rect source_rect = { 0 };
struct v4l2_ext_video_rect input_rect = { 0 };
struct v4l2_ext_video_rect output_rect = { 0 };
int w, h;
int id = layer->getTunnelId();
int ret = -1;
if (id != MESON_USE_TVIN_TUNNEL && id != MESON_USE_TVIN_EXT_TUNNEL)
wss_afd = 1;
drm_rect_t res = layer->getSourceResolution();
source_rect.x = res.left;
source_rect.y = res.top;
source_rect.w = res.right - res.left;
source_rect.h = res.bottom - res.top;
if (mPipe.get()) {
mPipe->getSize(w, h);
ret = mPipe->getAspectratio((void *)&source_rect, wss_afd,
w, h,
(void *)&policy,
(void *)&input_rect,
(void *)&output_rect);
if (!ret) {
drm_rect_t rect = {
.left = input_rect.x,
.top = input_rect.y,
.right = input_rect.x + input_rect.w,
.bottom = input_rect.y + input_rect.h,
};
drm_rect_t frame = {
.left = output_rect.x,
.top = output_rect.y,
.right = output_rect.x + output_rect.w,
.bottom = output_rect.y + output_rect.h,
};
layer->setSourceCrop(rect);
layer->setDisplayFrame(frame);
}
}
return ret;
}
int32_t MesonDisplay::setLayerOrbitWindow(int32_t layerId,
struct v4l2_ext_vsc_orbit_window &info)
{
std::lock_guard<std::mutex> lock(mMutex);
std::shared_ptr<MesonLayer> layer = getLayerById(layerId);
if (!layer)
return MESON_ERROR_BAD_LAYER;
int w = 0;
int h = 0;
drm_rect_t osd_input = {
.left = info.osd_input.x,
.top = info.osd_input.y,
.right = info.osd_input.w + info.osd_input.x,
.bottom = info.osd_input.h + info.osd_input.y
};
drm_rect_t video_input = {
.left = info.video_input.x,
.top = info.video_input.y,
.right = info.video_input.w + info.video_input.x,
.bottom = info.video_input.h + info.video_input.y
};
drm_rect_t osd_output = {
.left = info.osd_output.x,
.top = info.osd_output.y,
.right = info.osd_output.w + info.osd_output.x,
.bottom = info.osd_output.h + info.osd_output.y
};
drm_rect_t video_output = {
.left = info.video_output.x,
.top = info.video_output.y,
.right = info.video_output.w + info.video_output.x,
.bottom = info.video_output.h + info.video_output.y
};
if (mPipe.get())
mPipe->getSize(w, h);
return layer->setOrbitWindow(osd_input,
video_input,
osd_output,
video_output,
w, h);
}
int32_t MesonDisplay::setLayerOrbitMove(int32_t layerId,
struct v4l2_ext_vsc_orbit_move &info)
{
std::lock_guard<std::mutex> lock(mMutex);
std::shared_ptr<MesonLayer> layer = getLayerById(layerId);
if (!layer)
return MESON_ERROR_BAD_LAYER;
int w = 0;
int h = 0;
drm_rect_t osd_input = {
.left = info.osd_input.x,
.top = info.osd_input.y,
.right = info.osd_input.w + info.osd_input.x,
.bottom = info.osd_input.h + info.osd_input.y
};
drm_rect_t video_input = {
.left = info.video_input.x,
.top = info.video_input.y,
.right = info.video_input.w + info.video_input.x,
.bottom = info.video_input.h + info.video_input.y
};
drm_rect_t osd_output = {
.left = info.osd_output.x,
.top = info.osd_output.y,
.right = info.osd_output.w + info.osd_output.x,
.bottom = info.osd_output.h + info.osd_output.y
};
drm_rect_t video_output = {
.left = info.video_output.x,
.top = info.video_output.y,
.right = info.video_output.w + info.video_output.x,
.bottom = info.video_output.h + info.video_output.y
};
if (mPipe.get())
mPipe->getSize(w, h);
return layer->setOrbitMove(osd_input,
video_input,
osd_output,
video_output,
w, h);
}
int32_t MesonDisplay::getHDR()
{
std::lock_guard<std::mutex> lock(mMutex);
if (mConnector.get())
return mConnector->getHDR();
return -1;
}
int32_t MesonDisplay::setHDR(int value)
{
std::lock_guard<std::mutex> lock(mMutex);
if (mConnector.get())
return mConnector->setHDR(value);
return -1;
}
int32_t MesonDisplay::getBgColor(int *value)
{
std::lock_guard<std::mutex> lock(mMutex);
if (mCrtc.get())
*value = mCrtc->getBgColor();
return 0;
}
int32_t MesonDisplay::setBgColor(int value)
{
std::lock_guard<std::mutex> lock(mMutex);
if (mCrtc.get())
return mCrtc->setBgColor(value);
return -1;
}
int32_t MesonDisplay::getLayerFreeze(int32_t layerId, int *freeze)
{
std::lock_guard<std::mutex> lock(mMutex);
std::shared_ptr<MesonLayer> layer = getLayerById(layerId);
if (!layer)
return MESON_ERROR_BAD_LAYER;
*freeze = layer->getFreeze();
return 0;
}
int32_t MesonDisplay::setLayerFreeze(int32_t layerId, int freeze)
{
std::lock_guard<std::mutex> lock(mMutex);
std::shared_ptr<MesonLayer> layer = getLayerById(layerId);
if (!layer)
return MESON_ERROR_BAD_LAYER;
layer->setFreeze(freeze);
return 0;
}
int32_t MesonDisplay::getLayerFreezeFrameBuffer(int32_t layerId, int *freeze)
{
std::lock_guard<std::mutex> lock(mMutex);
std::shared_ptr<MesonLayer> layer = getLayerById(layerId);
if (!layer)
return MESON_ERROR_BAD_LAYER;
*freeze = layer->getFreezeBuffer();
return 0;
}
int32_t MesonDisplay::setLayerFreezeFrameBuffer(int32_t layerId, int freeze)
{
std::lock_guard<std::mutex> lock(mMutex);
std::shared_ptr<MesonLayer> layer = getLayerById(layerId);
if (!layer)
return MESON_ERROR_BAD_LAYER;
layer->setFreezeBuffer(freeze);
return 0;
}
/* hdmitx function begin */
int32_t MesonDisplay::getHdmitxConnection(int &state)
{
std::lock_guard<std::mutex> lock(mMutex);
state = 0;
if (mConnector.get())
return mConnector->getHdmitxConnection(state);
return -1;
}
int32_t MesonDisplay::getHdmitxHdrType(int &value)
{
std::lock_guard<std::mutex> lock(mMutex);
if (mConnector.get())
value = mConnector->getHDR();
return 0;
}
int32_t MesonDisplay::getHdmitxHdcpVersion(int &value)
{
std::lock_guard<std::mutex> lock(mMutex);
if (mConnector.get())
return mConnector->getHdmitxHdcpVersion(value);
return 0;
}
int32_t MesonDisplay::getHdmitxHdrPolicy(int &value)
{
std::lock_guard<std::mutex> lock(mMutex);
if (mCrtc.get())
return mCrtc->getHdmitxHdrPolicy(value);
return -1;
}
int32_t MesonDisplay::setHdmitxHdrPolicy(int value)
{
std::lock_guard<std::mutex> lock(mMutex);
if (mCrtc.get())
return mCrtc->setHdmitxHdrPolicy((ENUM_MESON_HDR_POLICY)value);
return 0;
}
int32_t MesonDisplay::setHdmitxAvMute(enum v4l2_ext_hdmitx_avmute value)
{
std::lock_guard<std::mutex> lock(mMutex);
if (mCrtc.get())
return mCrtc->setHdmitxAvMute(value);
return 0;
}
int32_t MesonDisplay::setHdmitxHdcpAuth(enum v4l2_ext_hdmitx_hdcp_auth value)
{
std::lock_guard<std::mutex> lock(mMutex);
if (mCrtc.get())
return mCrtc->setHdmitxHdcpAuth(value);
return 0;
}
int32_t MesonDisplay::getHdmitxHdcpStatus(enum v4l2_ext_hdmitx_hdcp_status &value)
{
std::lock_guard<std::mutex> lock(mMutex);
if (mConnector.get())
value = (v4l2_ext_hdmitx_hdcp_status)mConnector->getHdmitxHdcpStatus();
return 0;
}
int32_t MesonDisplay::getHdmiTiming(struct v4l2_ext_hdmitx_timing_info &info)
{
std::lock_guard<std::mutex> lock(mMutex);
int ret = -1;
if (mCrtc.get()) {
DisplayMode mode = { 0 };
ret = mCrtc->getHdmiTxMode(mode);
if (!ret) {
info.width = mode.w;
info.height = mode.h;
info.framerate = mode.vrefresh;
info.scan_type = !mode.interlace;
}
}
return ret;
}
int32_t MesonDisplay::setHdmiTiming(struct v4l2_ext_hdmitx_timing_info &info)
{
std::lock_guard<std::mutex> lock(mMutex);
if (mCrtc.get()) {
DisplayMode mode = { 0 };
mode.w = info.width;
mode.h = info.height;
mode.vrefresh = info.framerate;
mode.interlace = !info.scan_type;
DISPLAY_LOGV("setHdmiTxMode: w: %d, h: %d, f: %d, t: %d\n",
info.width, info.height, info.framerate, info.scan_type);
return mCrtc->setHdmiTxMode(mode);
}
return 0;
}
int32_t MesonDisplay::getHdmitxColor(struct v4l2_ext_hdmitx_color_attr &attr)
{
std::lock_guard<std::mutex> lock(mMutex);
int ret = -1;
if (mConnector.get()) {
ENUM_MESON_COLOR_SPACE space;
int depth;
ret = mConnector->getHdmitxColor(space, depth);
if (!ret) {
attr.pixel_encoding = (enum v4l2_ext_hdmitx_avi_csc)space;
if (depth == 8) {
attr.color_depth = V4L2_EXT_HDMITX_COLOR_DEPTH_8BIT;
} else if (depth == 10) {
attr.color_depth = V4L2_EXT_HDMITX_COLOR_DEPTH_10BIT;
} else if (depth == 12) {
attr.color_depth = V4L2_EXT_HDMITX_COLOR_DEPTH_12BIT;
} else if (depth == 16) {
attr.color_depth = V4L2_EXT_HDMITX_COLOR_DEPTH_16BIT;
} else {
attr.color_depth = V4L2_EXT_HDMITX_COLOR_DEPTH_RESERVED;
}
}
}
return ret;
}
int32_t MesonDisplay::setHdmitxColor(struct v4l2_ext_hdmitx_color_attr attr)
{
std::lock_guard<std::mutex> lock(mMutex);
if (mConnector.get()) {
int depth = 0;
ENUM_MESON_COLOR_SPACE space;
switch (attr.color_depth) {
case V4L2_EXT_HDMITX_COLOR_DEPTH_8BIT:
depth = 8;
break;
case V4L2_EXT_HDMITX_COLOR_DEPTH_10BIT:
depth = 10;
break;
case V4L2_EXT_HDMITX_COLOR_DEPTH_12BIT:
depth = 12;
break;
case V4L2_EXT_HDMITX_COLOR_DEPTH_16BIT:
depth = 16;
break;
default:
depth = 0;
break;
}
space = (ENUM_MESON_COLOR_SPACE)attr.pixel_encoding;
return mConnector->setHdmitxColor(space, depth);
}
return -1;
}
int32_t MesonDisplay::getHdmitxRawEdid(char **dat, int &len)
{
std::lock_guard<std::mutex> lock(mMutex);
int ret = -1;
if (!dat)
return -1;
if (mConnector.get())
ret = mConnector->getHdmitxRawEdid(dat, len);
return ret;
}
/* hdmitx function end */
void MesonDisplay::createGpscPlane(int fd)
{
std::lock_guard<std::mutex> lock(mMutex);
if (fd < 0)
return;
mGpscPlane = std::make_shared<GpscPlane>(fd);
if (mCompositionStrategy.get()) {
std::shared_ptr<MultiplanesComposition> strategy =
std::static_pointer_cast<MultiplanesComposition>(mCompositionStrategy);
strategy->setGpscPlane(mGpscPlane);
}
}
void MesonDisplay::setGpscConnectId(int id)
{
std::lock_guard<std::mutex> lock(mMutex);
if (mGpscPlane.get())
mGpscPlane->setConnectTunnel(id);
}
void MesonDisplay::clearPlane()
{
std::lock_guard<std::mutex> lock(mMutex);
for (auto planeIt = mPlanes.begin(); planeIt != mPlanes.end(); ++planeIt)
(*planeIt)->setPlane(NULL, 0, BLANK_FOR_NO_CONTENT);
if (mProcessorsManager)
mProcessorsManager->tearDownAllProcessors();
}
int MesonDisplay::setDrmLeaseFd(int fd)
{
std::lock_guard<std::mutex> lock(mMutex);
if (mConnector.get())
mConnector->setDrmLeaseFd(fd);
if (mCrtc.get())
mCrtc->setDrmLeaseFd(fd);
return 0;
}
int MesonDisplay::setOrbitPolicy(int32_t layerId,
struct v4l2_ext_vsc_orbit_policy policy)
{
int ret = -1;
struct v4l2_ext_vsc_orbit_position position = { 0 };
{
std::lock_guard<std::mutex> lock(mMutex);
std::shared_ptr<MesonLayer> layer = getLayerById(layerId);
if (!layer)
return MESON_ERROR_BAD_LAYER;
struct v4l2_ext_video_rect source_rect = { 0 };
struct v4l2_ext_video_rect input_rect = { 0 };
struct v4l2_ext_video_rect output_rect = { 0 };
layer->setOrbitPolicy(policy);
drm_rect_t res = layer->getSourceResolution();
source_rect.x = res.left;
source_rect.y = res.top;
source_rect.w = res.right - res.left;
source_rect.h = res.bottom - res.top;
drm_rect_t crop = layer->getSourceCrop();
input_rect.x = crop.left;
input_rect.y = crop.top;
input_rect.w = crop.right - crop.left;
input_rect.h = crop.bottom - crop.top;
drm_rect_t frame = layer->getDisplayFrame();
output_rect.x = frame.left;
output_rect.y = frame.top;
output_rect.w = frame.right - frame.left;
output_rect.h = frame.bottom - frame.top;
if (mPipe.get())
ret = mPipe->getOrbitPosition((void *)&source_rect,
(void *)&input_rect,
(void *)&output_rect,
(void *)&policy,
(void *)&position);
}
if (!ret) {
ret = setLayerOrbitWindow(layerId, position.orbit_window);
ret |= setLayerOrbitMove(layerId, position.orbit_move);
}
return ret;
}
int MesonDisplay::getOrbitPolicy(int32_t layerId,
struct v4l2_ext_vsc_orbit_policy *policy)
{
std::lock_guard<std::mutex> lock(mMutex);
std::shared_ptr<MesonLayer> layer = getLayerById(layerId);
if (!layer)
return MESON_ERROR_BAD_LAYER;
*policy = layer->getOrbitPolicy();
return 0;
}
int MesonDisplay::setSeamlessInfo(int32_t layerId,
struct v4l2_ext_vsc_seamless_info info)
{
std::lock_guard<std::mutex> lock(mMutex);
std::shared_ptr<MesonLayer> layer = getLayerById(layerId);
if (!layer)
return MESON_ERROR_BAD_LAYER;
layer->setSeamlessInfo(info);
return 0;
}
int MesonDisplay::getSeamlessInfo(int32_t layerId,
struct v4l2_ext_vsc_seamless_info *info)
{
std::lock_guard<std::mutex> lock(mMutex);
std::shared_ptr<MesonLayer> layer = getLayerById(layerId);
if (!layer)
return MESON_ERROR_BAD_LAYER;
*info = layer->getSeamlessInfo();
return 0;
}
#endif