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

761 lines
21 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 <fcntl.h>
#include "MesonDisplay.h"
#include "V2DComposer.h"
#include "DisplayThread.h"
#include "MultiplanesComposition.h"
#include "DispAdptLog.h"
#include "MesonEventListener.h"
#include "VtAllocSolidColorBuffer.h"
#include "VideoTunnelDev.h"
MesonDisplay::MesonDisplay(uint32_t display)
{
mDisplayId = display;
mDisplayToMainPlane = false;
mEnablePrimaryPlane = true;
mDelayLatency = 0;
mVsyncTimestamp = 0;
mLayerSeq = 0;
mPipe = std::make_shared<MesonDisplayPipe>(display);
mProcessorsManager = std::make_shared<ProcessorsManager>(
(display == 0) ? true : false);
mPipeAdapter = std::make_shared<VideoPipeAdapter>();
}
MesonDisplay::~MesonDisplay()
{
DISPLAY_LOGD("%s[%d]", __func__, __LINE__);
if (mPipe.get())
mPipe->destroyThread();
if (mDisplayThread.get())
mDisplayThread->destroyThread();
if (mVsync.get()) {
mVsync->setEnabled(false);
mVsync->setObserver(NULL);
mVsync.reset();
}
mLayers.clear();
for (auto planeIt = mPlanes.begin(); planeIt != mPlanes.end(); ++planeIt)
(*planeIt)->setPlane(NULL, 0, BLANK_FOR_NO_CONTENT);
mPlanes.clear();
mComposers.clear();
mCompositionStrategy.reset();
mPresentCompositionStg.reset();
mProcessorsManager.reset();
mPipe.reset();
mPipeAdapter.reset();
if (mListener.get())
mListener.reset();
VtAllocSolidColorBuffer::resetInstance();
VideoTunnelDev::resetInstanceForTest();
}
int32_t MesonDisplay::initialize()
{
std::vector<std::shared_ptr<IComposer>> composers;
const char *env = getenv("DISPLAYADAPTER_ENABLE_PRIMARY_PLANE");
if (env && atoi(env) != 1)
mEnablePrimaryPlane = false;
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());
}
}
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);
}
});
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);
}
});
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;
}
if (fd >= 0)
close(fd);
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);
}
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();
}
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);
DISPLAY_LOGD("%s[%d]: layer: %d", __func__, __LINE__, inLayer);
if (layer.get() && !layer->isVtBuffer())
layer->releaseResource();
if (layer.get() && layer->isVtBuffer())
layer->releaseVtResource();
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;
}
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);
if (tunnelId < 128 && tunnelId >= 0)
VtAllocSolidColorBuffer::getInstance().allocBuffer();
auto layerIt = mLayers.find(layer);
if (layerIt != mLayers.end())
return layerIt->second->setTunnelId(tunnelId, std::move(observer));
return -1;
}
int32_t MesonDisplay::setLayerIndex(int32_t layer, int32_t index)
{
std::lock_guard<std::mutex> lock(mMutex);
auto layerIt = mLayers.find(layer);
if (layerIt != mLayers.end()) {
if (index < MAX_LAYER_NUMBER && !(mState & (1 << index)))
layerIt->second->setVisible(false);
return layerIt->second->setIndex(index);
}
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()) {
int32_t displayW = 0;
int32_t displayH = 0;
mPipe->getSize(displayW, displayH);
if (displayW > 0 && displayH > 0) {
float scaleW = (float)displayW / DISPLAY_WIDTH_BASE;
float scaleH = (float)displayH / DISPLAY_HEIGHT_BASE;
frame.left = frame.left * scaleW;
frame.top = frame.top * scaleH;
frame.right = frame.right * scaleW;
frame.bottom = frame.bottom * scaleH;
if (frame.right > displayW) frame.right = displayW;
if (frame.bottom > displayH) frame.bottom = displayH;
}
return layerIt->second->setDisplayFrame(frame);
}
return -1;
}
void MesonDisplay::setVsyncDelayLatency(int32_t latency)
{
mDelayLatency = latency;
}
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());
mDisableValue = std::move(value);
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)
{
if (width == nullptr || height == nullptr) {
DISPLAY_LOGE("Invalid pointer: width or height is null");
return -1;
}
*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;
}
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)
{
if (mPipe.get() && mPipeAdapter.get()) {
int refresh = mPipe->getVrefresh();
int vsync_time = refresh > 0 ? 1000 / refresh : 0;
int drm_delay = 3 * vsync_time;
int video_delay = mPipeAdapter->getVideoDelay(fps, refresh * 1000);
if (video_delay < 0)
return -1;
if (video_delay > std::numeric_limits<int>::max() - drm_delay)
return -1;
return video_delay + drm_delay;
}
return -1;
}
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();
}
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 {
mVsync = std::move(vsync);
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));
}
std::list<DisplayPair> prevDisplayPairs;
std::list<DisplayPair> currDisplayPairs;
mPresentCompositionStg->getDisplayPair(prevDisplayPairs);
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;
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);
}
return ret;
}
int MesonDisplay::presentDisplay(int32_t* outPresentFence)
{
int ret;
std::lock_guard<std::mutex> lock(mMutex);
bool mainThread = !hasVtBuffer();
if (mPresentCompositionStg.get() == NULL)
return MESON_ERROR_BAD_PARAMETER;
if (mProcessorsManager.get())
mProcessorsManager->runVideoProcessors(mainThread);
ret = mPresentCompositionStg->commit();
if (mProcessorsManager.get()) {
mProcessorsManager->setVideoProcessorReleaseFence(ret != 0, mainThread);
mProcessorsManager->restoretProcessorFromFreeToUnused();
}
return ret;
}
bool MesonDisplay::handleDisplayConnection()
{
std::lock_guard<std::mutex> vtLock(mMutex);
std::shared_ptr<MesonLayer> layer;
bool ret = false;
for (auto it = mLayers.begin(); it != mLayers.end(); it++) {
layer = it->second;
if (!layer->isVtBuffer())
layer->updateBuffer();
else
layer->updateVtBuffer();
ret = true;
}
return ret;
}
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;
}