/* * 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 #include #include #include "MesonDisplayPipe.h" #include "DispAdptLog.h" #define MESON_DRM_DRIVER_NAME "meson" #define MAX_PLANE_LIMIT 4 static std::mutex ghandlerMutex; MesonDisplayPipe::MesonDisplayPipe(int dispId) { mPipeId = dispId; mDrmFd = -1; mEnablePrimaryPlane = false; mThread = 0; needRefresh = false; init(); } MesonDisplayPipe::~MesonDisplayPipe() { if (mFlipThread) pthread_join(mThread, NULL); if (mDrmFd >= 0) close(mDrmFd); mDrmFd = -1; mPageFlip.clear(); } int32_t MesonDisplayPipe::init() { int ret; ret = openDrmDevice(); DISPLAY_ASSERT(ret == 0, "Open drm device %s failed.", MESON_DRM_DRIVER_NAME); drmDropMaster(mDrmFd); loadDrmResources(); return 0; } int32_t MesonDisplayPipe::openDrmDevice() { if (mDrmFd >= 0) return 0; mDrmFd = drmOpen(MESON_DRM_DRIVER_NAME, NULL); DISPLAY_ASSERT(mDrmFd >= 0, "Open drm device %s failed.", MESON_DRM_DRIVER_NAME); return 0; } int32_t MesonDisplayPipe::loadDrmResources() { int ret = drmSetClientCap(mDrmFd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); DISPLAY_ASSERT(ret == 0, "DRM_CLIENT_CAP_UNIVERSAL_PLANES failed(%d).", ret); ret = drmSetClientCap(mDrmFd, DRM_CLIENT_CAP_ATOMIC, 1); DISPLAY_ASSERT(ret == 0, "DRM_CLIENT_CAP_ATOMIC failed (%d).", ret); /* load crtc & connector & encoder. */ drmModeResPtr drmRes = drmModeGetResources(mDrmFd); DISPLAY_ASSERT(drmRes != 0, "drmModeGetResources failed()."); /* Crtc */ for (int i = 0; i < drmRes->count_crtcs; i++) { drmModeCrtcPtr metadata = drmModeGetCrtc( mDrmFd, drmRes->crtcs[i]); std::shared_ptr crtc = std::make_shared(mDrmFd, metadata, i); mCrtcs.emplace(crtc->getId(), std::move(crtc)); drmModeFreeCrtc(metadata); } /* Encoder */ for (int i = 0; i < drmRes->count_encoders; i++) { drmModeEncoderPtr metadata = drmModeGetEncoder( mDrmFd, drmRes->encoders[i]); std::shared_ptr encoder = std::make_shared(mDrmFd, metadata); mEncoders.emplace(encoder->getId(), std::move(encoder)); drmModeFreeEncoder(metadata); } /*Connectors*/ for (int i = 0; i < drmRes->count_connectors; i ++) { drmModeConnectorPtr metadata = drmModeGetConnector( mDrmFd,drmRes->connectors[i]); std::shared_ptr connector = std::make_shared(mDrmFd, metadata); mConnectors.emplace(connector->getId(), std::move(connector)); drmModeFreeConnector(metadata); } drmModeFreeResources(drmRes); /* load Drm planes */ std::map> planes; bool primary = true; drmModePlaneResPtr planeRes = drmModeGetPlaneResources(mDrmFd); for (uint32_t i = 0; i < planeRes->count_planes; i ++) { drmModePlanePtr metadata = drmModeGetPlane( mDrmFd, planeRes->planes[i]); for (uint32_t pfi= 0; pfi < metadata->count_formats; ++pfi ) { if (DRM_FORMAT_NV21 == metadata->formats[pfi]) { DISPLAY_LOGI("Load a drm video plane %d", pfi); std::shared_ptr plane = std::make_shared(mDrmFd, metadata); plane->setPrimary(primary); if (primary) primary = false; planes.emplace(plane->getId(), std::move(plane)); break; } } drmModeFreePlane(metadata); } drmModeFreePlaneResources(planeRes); std::shared_ptr plane; for (auto planeIt = planes.begin(); planeIt != planes.end(); planeIt++) { plane = planeIt->second; if (plane->getPossibleCrtcs() == (uint32_t)(1 << mPipeId)) { mPlanesForPipe.insert(std::make_pair(mPipeId, plane)); } } return 0; } void MesonDisplayPipe::enablePrimaryPlane() { mEnablePrimaryPlane = true; } int32_t MesonDisplayPipe::getCrtc(std::shared_ptr & crtc) { for (const auto & it : mCrtcs) { if (it.second->getPipe() == (uint32_t)mPipeId) mCrtc = it.second; } crtc = mCrtc; return 0; } void MesonDisplayPipe::getSize(int32_t &width, int32_t &height) { std::shared_ptr crtc; width = 0; height = 0; getCrtc(crtc); if (crtc.get()) { drmModeCrtcPtr metadata = drmModeGetCrtc(mDrmFd, crtc->getId()); if (metadata) { width = metadata->mode.hdisplay; height = metadata->mode.vdisplay; drmModeFreeCrtc(metadata); } } } int32_t MesonDisplayPipe::getPlanes(std::vector> & planes) { planes.clear(); auto plane_rage = mPlanesForPipe.equal_range(mPipeId); for (auto it = plane_rage.first; it != plane_rage.second; ++it) { if (it->second->getPrimary() && mEnablePrimaryPlane) { planes.push_back(it->second); break; } } for (auto it = plane_rage.first; it != plane_rage.second; ++it) { if (!it->second->getPrimary()) planes.push_back(it->second); } return 0; } int32_t MesonDisplayPipe::getConnector(std::shared_ptr & connector) { if (mConnectors.size() > 0) { auto it = mConnectors.begin(); mConnector = it->second; } connector = mConnector; return 0; } int32_t MesonDisplayPipe::getVrefresh() { std::shared_ptr crtc; int refresh = 0; getCrtc(crtc); if (crtc.get()) { drmModeCrtcPtr metadata = drmModeGetCrtc( mDrmFd, crtc->getId()); refresh = metadata->mode.vrefresh; drmModeFreeCrtc(metadata); } return refresh; } void MesonDisplayPipe::drmPageFlipInit() { std::unique_lock stateLock(mMutex); mThreadExit = false; if (mFlipThread) return; mEvctx.version = 3; mEvctx.page_flip_handler2 = pageFlipHandler; int ret = pthread_create(&mThread, NULL, PageFlipThread, (void *)this); if (!ret) { mFlipThread = true; std::vector> planes; getPlanes(planes); for (auto & plane : planes) { std::shared_ptr flip = std::make_shared(); plane->setFlipUserData((void *)flip.get()); plane->setObserver(this); mPageFlip.push_back(std::move(flip)); } } } void MesonDisplayPipe::destroyPageFlip(uint64_t id) { std::unique_lock stateLock(mMutex); for (auto & flip : mPageFlip) flip->removeData(id); } void MesonDisplayPipe::onPoll() { { std::unique_lock stateLock(mMutex); needRefresh = true; } mCondition.notify_all(); } void* MesonDisplayPipe::PageFlipThread(void *data) { MesonDisplayPipe* pThis = (MesonDisplayPipe*)data; prctl(PR_SET_NAME, "pageFlipThread"); // set thread to SCHED_FIFO to minimize jitter struct sched_param param = {0}; param.sched_priority = 2; if (sched_setscheduler(0, SCHED_FIFO, ¶m) != 0) DISPLAY_LOGE("%s, Couldn't set SCHED_FIFO for thread", __func__); while (true) { bool isExit = false; { std::unique_lock stateLock(pThis->mMutex); isExit = pThis->mThreadExit; } if (isExit) { DISPLAY_LOGD("%s, PageFlipThread exit", __func__); pthread_exit(0); return NULL; } pThis->handleEvent(); } } bool MesonDisplayPipe::updateRefresh() { for (auto & flip : mPageFlip) { if (flip->getIndex() != 0) return false; } return true; } void MesonDisplayPipe::handleEvent() { struct pollfd pfd; int rc = -1; bool isExit = false; pfd.fd = mDrmFd; pfd.events = POLLIN; pfd.revents = 0; { std::unique_lock stateLock(mMutex); if (updateRefresh() && needRefresh) needRefresh = false; while (!needRefresh) { mCondition.wait(stateLock); } needRefresh = false; isExit = mThreadExit; } if (isExit) { DISPLAY_LOGD("%s, PageFlipThread exit", __func__); return; } rc = poll(&pfd, 1, 1000); /* timeout:msec */ if ((rc == -1) && ((errno == EINTR) || (errno == EAGAIN))) { DISPLAY_LOGE("%s %d poll fail: fd %d errno %d\n", __FUNCTION__, __LINE__, mDrmFd, errno); } else if (rc == 0) { errno = ETIME; DISPLAY_LOGE("%s %d poll timeout: fd %d, errno %d\n", __FUNCTION__, __LINE__, mDrmFd, errno); } else if(pfd.revents & (POLLNVAL | POLLERR)) { DISPLAY_LOGE("%s %d poll revents error, fd %d\n", __FUNCTION__, __LINE__, mDrmFd); } else { DISPLAY_LOGD("%s[%d]: page flip event", __func__, __LINE__); drmHandleEvent(mDrmFd, &mEvctx); } } void MesonDisplayPipe::destroyThread() { { std::unique_lock stateLock(mMutex); needRefresh = true; mThreadExit = true; } mCondition.notify_all(); } void MesonDisplayPipe::pageFlipHandler(int fd, uint32_t frame, uint32_t sec, uint32_t usec, uint32_t crtc_id, void *data) { MesonPageFlip *flip = (MesonPageFlip *)data; meson_page_flip_info info = { 0 }; if (flip == NULL) return; std::lock_guard lock(flip->mMutex); info.sec = sec; info.usec = usec; for (const auto& pair : flip->getData()) { info.fd = pair.fd; if (pair.callback) pair.callback(pair.context, &info); } flip->clearData(); flip->decIndex(); }