/* * 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 #include "MesonDisplay.h" #include "V2DComposer.h" #include "DisplayThread.h" #include "MultiplanesComposition.h" #include "DispAdptLog.h" #include "MesonEventListener.h" #include "VtAllocSolidColorBuffer.h" #include "VideoTunnelDev.h" #ifdef VSC_ENABLE #include "VscalerEvent.h" #endif MesonDisplay::MesonDisplay(uint32_t display) { mDisplayId = display; mDisplayToMainPlane = false; mEnablePrimaryPlane = true; mDelayLatency = 0; mVsyncTimestamp = 0; mLayerSeq = 0; mPipe = std::make_shared(display); mProcessorsManager = std::make_shared( (display == 0) ? true : false); mPipeAdapter = std::make_shared(); } 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> 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()); } #ifdef VSC_ENABLE if (mCrtc.get() && mConnector.get()) mCrtc->setConnectorType(mConnector->getType()); #endif } mListener = std::make_shared(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(); std::shared_ptr crtc = std::static_pointer_cast(mCrtc); std::shared_ptr mesonVsync = std::make_shared(DISPLAY_DEFAULT); mesonVsync->setVsyncMode(VSYNC_MODE_HW, crtc); setVsync(mesonVsync); if (!mDisplayThread.get()) { mDisplayThread = std::make_shared(this); } mesonVsync->setEnabled(true); initLayerIdGenerator(); return 0; } int32_t MesonDisplay::createV2DComposer(int idx, std::shared_ptr & 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(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> & composers) { int idx = 0; int ret; std::shared_ptr 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(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 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 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 lock(mMutex); std::shared_ptr layer = std::make_shared(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 lock(mMutex); auto layerit = mLayers.find(inLayer); if (layerit == mLayers.end()) return MESON_ERROR_BAD_LAYER; std::shared_ptr 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 lock(mMutex); std::shared_ptr observer = std::make_shared(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 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 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 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 lock(mMutex); auto layerIt = mLayers.find(layer); if (layerIt != mLayers.end()) { return layerIt->second->setDisplayFrame(frame); } return -1; } void MesonDisplay::setVsyncDelayLatency(int32_t latency) { #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 mDelayLatency = latency; #endif } int32_t MesonDisplay::getVsyncDelayLatency(int32_t layer) { return mDelayLatency; } void MesonDisplay::setVideoPlaneState(int state) { std::lock_guard 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 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) { int refresh = 0; int video_delay = 0; int vsync_time = 0; int drm_delay = 0; if (mPipe.get()) refresh = mPipe->getVrefresh(); vsync_time = refresh > 0 ? 1000 / refresh : 0; drm_delay = 3 * vsync_time; if (mPipeAdapter.get()) video_delay = mPipeAdapter->getVideoDelay(fps, refresh * 1000); if (video_delay < 0) video_delay = 0; if (video_delay > std::numeric_limits::max() - drm_delay) video_delay = 0; return video_delay + drm_delay; } int32_t MesonDisplay::waitVBlank() { if (!mCrtc.get()) { return -EFAULT; } std::shared_ptr localCrtc = mCrtc; nsecs_t vsyncTime = 0; int ret = localCrtc->waitVBlank(vsyncTime); localCrtc.reset(); return ret; } int MesonDisplay::getLayerTotal() { std::lock_guard lock(mMutex); return mLayers.size(); } int32_t MesonDisplay::setVsync(std::shared_ptr vsync) { std::lock_guard 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 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 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> fbs; auto fbIt = mPresentLayers.begin(); for (; fbIt != mPresentLayers.end(); fbIt++) { if (updateZ) (*fbIt)->updateZorder(); fbs.push_back(std::static_pointer_cast(*fbIt)); } std::vector> planes; auto planeIt = mPresentPlanes.begin(); for (; planeIt != mPresentPlanes.end(); planeIt++) { if ((*planeIt)->getPrimary() || !mDisplayToMainPlane) planes.push_back(std::static_pointer_cast(*planeIt)); } std::list prevDisplayPairs; std::list currDisplayPairs; mPresentCompositionStg->getDisplayPair(prevDisplayPairs); std::shared_ptr crtc = std::static_pointer_cast(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 fb = std::static_pointer_cast(*(displayIt->fbs.begin())); fb->froceRefresh(true); } } if (mProcessorsManager.get()) { std::list displayPairs; mPresentCompositionStg->getDisplayPair(displayPairs); mProcessorsManager->setup(std::move(displayPairs), 0); } return ret; } int MesonDisplay::presentDisplay(int32_t* outPresentFence) { int ret; std::lock_guard 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 vtLock(mMutex); std::shared_ptr 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 vtLock(mMutex); int ret; std::shared_ptr 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 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; } #ifdef VSC_ENABLE std::shared_ptr MesonDisplay::getLayerById(int id) { std::unordered_map>::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 lock(mMutex); std::shared_ptr 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 lock(mMutex); drm_rect_t res; drm_rect_t punch; std::shared_ptr 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 lock(mMutex); std::shared_ptr 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 lock(mMutex); std::shared_ptr 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 lock(mMutex); std::shared_ptr 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 lock(mMutex); connect_info_t c_info = { .src = src, .index = index, .attr = attr, .out = out, }; std::shared_ptr 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 lock(mMutex); std::shared_ptr 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 lock(mMutex); std::shared_ptr 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 lock(mMutex); std::shared_ptr 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 lock(mMutex); std::shared_ptr 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 lock(mMutex); std::shared_ptr 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 lock(mMutex); if (mConnector.get()) return mConnector->getHDR(); return -1; } int32_t MesonDisplay::setHDR(int value) { std::lock_guard lock(mMutex); if (mConnector.get()) return mConnector->setHDR(value); return -1; } int32_t MesonDisplay::getBgColor(int *value) { std::lock_guard lock(mMutex); if (mCrtc.get()) *value = mCrtc->getBgColor(); return 0; } int32_t MesonDisplay::setBgColor(int value) { std::lock_guard lock(mMutex); if (mCrtc.get()) return mCrtc->setBgColor(value); return -1; } int32_t MesonDisplay::getLayerFreeze(int32_t layerId, int *freeze) { std::lock_guard lock(mMutex); std::shared_ptr 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 lock(mMutex); std::shared_ptr 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 lock(mMutex); std::shared_ptr 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 lock(mMutex); std::shared_ptr 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 lock(mMutex); state = 0; if (mConnector.get()) return mConnector->getHdmitxConnection(state); return -1; } int32_t MesonDisplay::getHdmitxHdrType(int &value) { std::lock_guard lock(mMutex); if (mConnector.get()) value = mConnector->getHDR(); return 0; } int32_t MesonDisplay::getHdmitxHdcpVersion(int &value) { std::lock_guard lock(mMutex); if (mConnector.get()) return mConnector->getHdmitxHdcpVersion(value); return 0; } int32_t MesonDisplay::getHdmitxHdrPolicy(int &value) { std::lock_guard lock(mMutex); if (mCrtc.get()) return mCrtc->getHdmitxHdrPolicy(value); return -1; } int32_t MesonDisplay::setHdmitxHdrPolicy(int value) { std::lock_guard 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 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 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 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 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 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 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 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 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 lock(mMutex); if (fd < 0) return; mGpscPlane = std::make_shared(fd); if (mCompositionStrategy.get()) { std::shared_ptr strategy = std::static_pointer_cast(mCompositionStrategy); strategy->setGpscPlane(mGpscPlane); } } void MesonDisplay::setGpscConnectId(int id) { std::lock_guard lock(mMutex); if (mGpscPlane.get()) mGpscPlane->setConnectTunnel(id); } void MesonDisplay::clearPlane() { std::lock_guard 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 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 lock(mMutex); std::shared_ptr 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 lock(mMutex); std::shared_ptr 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 lock(mMutex); std::shared_ptr 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 lock(mMutex); std::shared_ptr layer = getLayerById(layerId); if (!layer) return MESON_ERROR_BAD_LAYER; *info = layer->getSeamlessInfo(); return 0; } #endif