|  | /* | 
|  | * Copyright 2021 The Android Open Source Project | 
|  | * | 
|  | * 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 "DrmPresenter.h" | 
|  |  | 
|  | #include <cros_gralloc_handle.h> | 
|  | #include <linux/netlink.h> | 
|  | #include <sys/socket.h> | 
|  |  | 
|  | using android::base::guest::AutoReadLock; | 
|  | using android::base::guest::AutoWriteLock; | 
|  | using android::base::guest::ReadWriteLock; | 
|  |  | 
|  | namespace android { | 
|  |  | 
|  | bool DrmPresenter::init(const HotplugCallback& cb) { | 
|  | DEBUG_LOG("%s", __FUNCTION__); | 
|  |  | 
|  | mHotplugCallback = cb; | 
|  | mFd = android::base::unique_fd(open("/dev/dri/card0", O_RDWR | O_CLOEXEC)); | 
|  | if (mFd < 0) { | 
|  | ALOGE("%s HWC2::Error opening DrmPresenter device: %d", __FUNCTION__, | 
|  | errno); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | int univRet = drmSetClientCap(mFd.get(), DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); | 
|  | if (univRet) { | 
|  | ALOGE("%s: fail to set universal plane %d\n", __FUNCTION__, univRet); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | int atomicRet = drmSetClientCap(mFd.get(), DRM_CLIENT_CAP_ATOMIC, 1); | 
|  | if (atomicRet) { | 
|  | ALOGE("%s: fail to set atomic operation %d, %d\n", __FUNCTION__, atomicRet, | 
|  | errno); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | { | 
|  | AutoWriteLock lock(mStateMutex); | 
|  | bool initDrmRet = initDrmElementsLocked(); | 
|  | if (initDrmRet) { | 
|  | ALOGD("%s: Successfully initialized DRM backend", __FUNCTION__); | 
|  | } else { | 
|  | ALOGE("%s: Failed to initialize DRM backend", __FUNCTION__); | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | mDrmEventListener = sp<DrmEventListener>::make(*this); | 
|  | if (mDrmEventListener->init()) { | 
|  | ALOGD("%s: Successfully initialized DRM event listener", __FUNCTION__); | 
|  | } else { | 
|  | ALOGE("%s: Failed to initialize DRM event listener", __FUNCTION__); | 
|  | } | 
|  | mDrmEventListener->run("", ANDROID_PRIORITY_URGENT_DISPLAY); | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool DrmPresenter::initDrmElementsLocked() { | 
|  | drmModeRes* res; | 
|  | static const int32_t kUmPerInch = 25400; | 
|  |  | 
|  | res = drmModeGetResources(mFd.get()); | 
|  | if (res == nullptr) { | 
|  | ALOGE("%s HWC2::Error reading drm resources: %d", __FUNCTION__, errno); | 
|  | mFd.reset(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | ALOGD( | 
|  | "drmModeRes count fbs %d crtc %d connector %d encoder %d min w %d max w " | 
|  | "%d min h %d max h %d", | 
|  | res->count_fbs, res->count_crtcs, res->count_connectors, | 
|  | res->count_encoders, res->min_width, res->max_width, res->min_height, | 
|  | res->max_height); | 
|  |  | 
|  | for (uint32_t i = 0; i < res->count_crtcs; i++) { | 
|  | DrmCrtc crtc = {}; | 
|  |  | 
|  | drmModeCrtcPtr c = drmModeGetCrtc(mFd.get(), res->crtcs[i]); | 
|  | crtc.mId = c->crtc_id; | 
|  |  | 
|  | drmModeObjectPropertiesPtr crtcProps = | 
|  | drmModeObjectGetProperties(mFd.get(), c->crtc_id, DRM_MODE_OBJECT_CRTC); | 
|  |  | 
|  | for (uint32_t crtcPropsIndex = 0; crtcPropsIndex < crtcProps->count_props; | 
|  | crtcPropsIndex++) { | 
|  | drmModePropertyPtr crtcProp = | 
|  | drmModeGetProperty(mFd.get(), crtcProps->props[crtcPropsIndex]); | 
|  |  | 
|  | if (!strcmp(crtcProp->name, "OUT_FENCE_PTR")) { | 
|  | crtc.mOutFencePtrPropertyId = crtcProp->prop_id; | 
|  | } else if (!strcmp(crtcProp->name, "ACTIVE")) { | 
|  | crtc.mActivePropertyId = crtcProp->prop_id; | 
|  | } else if (!strcmp(crtcProp->name, "MODE_ID")) { | 
|  | crtc.mModePropertyId = crtcProp->prop_id; | 
|  | } | 
|  |  | 
|  | drmModeFreeProperty(crtcProp); | 
|  | } | 
|  |  | 
|  | drmModeFreeObjectProperties(crtcProps); | 
|  |  | 
|  | mCrtcs.push_back(crtc); | 
|  | } | 
|  |  | 
|  | drmModePlaneResPtr planeRes = drmModeGetPlaneResources(mFd.get()); | 
|  | for (uint32_t i = 0; i < planeRes->count_planes; ++i) { | 
|  | DrmPlane plane = {}; | 
|  |  | 
|  | drmModePlanePtr p = drmModeGetPlane(mFd.get(), planeRes->planes[i]); | 
|  | plane.mId = p->plane_id; | 
|  |  | 
|  | ALOGD( | 
|  | "%s: plane id: %u crtcid %u fbid %u crtc xy %d %d xy %d %d " | 
|  | "possible ctrcs 0x%x", | 
|  | __FUNCTION__, p->plane_id, p->crtc_id, p->fb_id, p->crtc_x, p->crtc_y, | 
|  | p->x, p->y, p->possible_crtcs); | 
|  |  | 
|  | drmModeObjectPropertiesPtr planeProps = | 
|  | drmModeObjectGetProperties(mFd.get(), plane.mId, DRM_MODE_OBJECT_PLANE); | 
|  |  | 
|  | for (uint32_t planePropIndex = 0; planePropIndex < planeProps->count_props; | 
|  | ++planePropIndex) { | 
|  | drmModePropertyPtr planeProp = | 
|  | drmModeGetProperty(mFd.get(), planeProps->props[planePropIndex]); | 
|  |  | 
|  | if (!strcmp(planeProp->name, "CRTC_ID")) { | 
|  | plane.mCrtcPropertyId = planeProp->prop_id; | 
|  | } else if (!strcmp(planeProp->name, "IN_FENCE_FD")) { | 
|  | plane.mInFenceFdPropertyId = planeProp->prop_id; | 
|  | } else if (!strcmp(planeProp->name, "FB_ID")) { | 
|  | plane.mFbPropertyId = planeProp->prop_id; | 
|  | } else if (!strcmp(planeProp->name, "CRTC_X")) { | 
|  | plane.mCrtcXPropertyId = planeProp->prop_id; | 
|  | } else if (!strcmp(planeProp->name, "CRTC_Y")) { | 
|  | plane.mCrtcYPropertyId = planeProp->prop_id; | 
|  | } else if (!strcmp(planeProp->name, "CRTC_W")) { | 
|  | plane.mCrtcWPropertyId = planeProp->prop_id; | 
|  | } else if (!strcmp(planeProp->name, "CRTC_H")) { | 
|  | plane.mCrtcHPropertyId = planeProp->prop_id; | 
|  | } else if (!strcmp(planeProp->name, "SRC_X")) { | 
|  | plane.mSrcXPropertyId = planeProp->prop_id; | 
|  | } else if (!strcmp(planeProp->name, "SRC_Y")) { | 
|  | plane.mSrcYPropertyId = planeProp->prop_id; | 
|  | } else if (!strcmp(planeProp->name, "SRC_W")) { | 
|  | plane.mSrcWPropertyId = planeProp->prop_id; | 
|  | } else if (!strcmp(planeProp->name, "SRC_H")) { | 
|  | plane.mSrcHPropertyId = planeProp->prop_id; | 
|  | } else if (!strcmp(planeProp->name, "type")) { | 
|  | plane.mTypePropertyId = planeProp->prop_id; | 
|  | uint64_t type = planeProp->values[0]; | 
|  | switch (type) { | 
|  | case DRM_PLANE_TYPE_OVERLAY: | 
|  | plane.mType = type; | 
|  | ALOGD("%s: plane %" PRIu32 " is DRM_PLANE_TYPE_OVERLAY", | 
|  | __FUNCTION__, plane.mId); | 
|  | break; | 
|  | case DRM_PLANE_TYPE_PRIMARY: | 
|  | plane.mType = type; | 
|  | ALOGD("%s: plane %" PRIu32 " is DRM_PLANE_TYPE_PRIMARY", | 
|  | __FUNCTION__, plane.mId); | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | drmModeFreeProperty(planeProp); | 
|  | } | 
|  |  | 
|  | drmModeFreeObjectProperties(planeProps); | 
|  |  | 
|  | bool isPrimaryOrOverlay = plane.mType == DRM_PLANE_TYPE_OVERLAY || | 
|  | plane.mType == DRM_PLANE_TYPE_PRIMARY; | 
|  | if (isPrimaryOrOverlay) { | 
|  | for (uint32_t j = 0; j < mCrtcs.size(); j++) { | 
|  | if ((0x1 << j) & p->possible_crtcs) { | 
|  | ALOGD("%s: plane %" PRIu32 " compatible with crtc mask %" PRIu32, | 
|  | __FUNCTION__, plane.mId, p->possible_crtcs); | 
|  | if (mCrtcs[j].mPlaneId == -1) { | 
|  | mCrtcs[j].mPlaneId = plane.mId; | 
|  | ALOGD("%s: plane %" PRIu32 " associated with crtc %" PRIu32, | 
|  | __FUNCTION__, plane.mId, j); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | drmModeFreePlane(p); | 
|  | mPlanes[plane.mId] = plane; | 
|  | } | 
|  | drmModeFreePlaneResources(planeRes); | 
|  |  | 
|  | for (uint32_t i = 0; i < res->count_connectors; ++i) { | 
|  | DrmConnector connector = {}; | 
|  | connector.mId = res->connectors[i]; | 
|  |  | 
|  | { | 
|  | drmModeObjectPropertiesPtr connectorProps = drmModeObjectGetProperties( | 
|  | mFd.get(), connector.mId, DRM_MODE_OBJECT_CONNECTOR); | 
|  |  | 
|  | for (uint32_t connectorPropIndex = 0; | 
|  | connectorPropIndex < connectorProps->count_props; | 
|  | ++connectorPropIndex) { | 
|  | drmModePropertyPtr connectorProp = drmModeGetProperty( | 
|  | mFd.get(), connectorProps->props[connectorPropIndex]); | 
|  | if (!strcmp(connectorProp->name, "CRTC_ID")) { | 
|  | connector.mCrtcPropertyId = connectorProp->prop_id; | 
|  | } else if (!strcmp(connectorProp->name, "EDID")) { | 
|  | connector.mEdidBlobId = | 
|  | connectorProps->prop_values[connectorPropIndex]; | 
|  | } | 
|  | drmModeFreeProperty(connectorProp); | 
|  | } | 
|  |  | 
|  | drmModeFreeObjectProperties(connectorProps); | 
|  | } | 
|  | { | 
|  | drmModeConnector* c = drmModeGetConnector(mFd.get(), connector.mId); | 
|  | if (c == nullptr) { | 
|  | ALOGE("%s: Failed to get connector %" PRIu32 ": %d", __FUNCTION__, | 
|  | connector.mId, errno); | 
|  | return false; | 
|  | } | 
|  | connector.connection = c->connection; | 
|  | if (c->count_modes > 0) { | 
|  | memcpy(&connector.mMode, &c->modes[0], sizeof(drmModeModeInfo)); | 
|  | drmModeCreatePropertyBlob(mFd.get(), &connector.mMode, | 
|  | sizeof(connector.mMode), | 
|  | &connector.mModeBlobId); | 
|  |  | 
|  | // Dots per 1000 inches | 
|  | connector.dpiX = | 
|  | c->mmWidth ? (c->modes[0].hdisplay * kUmPerInch) / (c->mmWidth) | 
|  | : -1; | 
|  | // Dots per 1000 inches | 
|  | connector.dpiY = | 
|  | c->mmHeight ? (c->modes[0].vdisplay * kUmPerInch) / (c->mmHeight) | 
|  | : -1; | 
|  | } | 
|  | ALOGD("%s connector %" PRIu32 " dpiX %" PRIi32 " dpiY %" PRIi32 | 
|  | " connection %d", | 
|  | __FUNCTION__, connector.mId, connector.dpiX, connector.dpiY, | 
|  | connector.connection); | 
|  |  | 
|  | drmModeFreeConnector(c); | 
|  |  | 
|  | connector.mRefreshRateAsFloat = | 
|  | 1000.0f * connector.mMode.clock / | 
|  | ((float)connector.mMode.vtotal * (float)connector.mMode.htotal); | 
|  | connector.mRefreshRateAsInteger = | 
|  | (uint32_t)(connector.mRefreshRateAsFloat + 0.5f); | 
|  | } | 
|  |  | 
|  | mConnectors.push_back(connector); | 
|  | } | 
|  |  | 
|  | drmModeFreeResources(res); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void DrmPresenter::resetDrmElementsLocked() { | 
|  | for (auto& c : mConnectors) { | 
|  | if (c.mModeBlobId) { | 
|  | if (drmModeDestroyPropertyBlob(mFd.get(), c.mModeBlobId)) { | 
|  | ALOGE("%s: Error destroy PropertyBlob %" PRIu32, __func__, | 
|  | c.mModeBlobId); | 
|  | } | 
|  | } | 
|  | } | 
|  | mConnectors.clear(); | 
|  | mCrtcs.clear(); | 
|  | mPlanes.clear(); | 
|  | } | 
|  |  | 
|  | int DrmPresenter::getDrmFB(hwc_drm_bo_t& bo) { | 
|  | int ret = drmPrimeFDToHandle(mFd.get(), bo.prime_fds[0], &bo.gem_handles[0]); | 
|  | if (ret) { | 
|  | ALOGE("%s: drmPrimeFDToHandle failed: %s (errno %d)", __FUNCTION__, | 
|  | strerror(errno), errno); | 
|  | return -1; | 
|  | } | 
|  | ret = drmModeAddFB2(mFd.get(), bo.width, bo.height, bo.format, bo.gem_handles, | 
|  | bo.pitches, bo.offsets, &bo.fb_id, 0); | 
|  | if (ret) { | 
|  | ALOGE("%s: drmModeAddFB2 failed: %s (errno %d)", __FUNCTION__, | 
|  | strerror(errno), errno); | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int DrmPresenter::clearDrmFB(hwc_drm_bo_t& bo) { | 
|  | int ret = 0; | 
|  | if (bo.fb_id) { | 
|  | if (drmModeRmFB(mFd.get(), bo.fb_id)) { | 
|  | ALOGE("%s: drmModeRmFB failed: %s (errno %d)", __FUNCTION__, | 
|  | strerror(errno), errno); | 
|  | } | 
|  | ret = -1; | 
|  | } | 
|  | if (bo.gem_handles[0]) { | 
|  | struct drm_gem_close gem_close = {}; | 
|  | gem_close.handle = bo.gem_handles[0]; | 
|  | if (drmIoctl(mFd.get(), DRM_IOCTL_GEM_CLOSE, &gem_close)) { | 
|  | ALOGE("%s: DRM_IOCTL_GEM_CLOSE failed: %s (errno %d)", __FUNCTION__, | 
|  | strerror(errno), errno); | 
|  | } | 
|  | ret = -1; | 
|  | } | 
|  | ALOGV("%s: drm FB %d", __FUNCTION__, bo.fb_id); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | bool DrmPresenter::handleHotplug() { | 
|  | std::vector<DrmConnector> oldConnectors(mConnectors); | 
|  | { | 
|  | AutoReadLock lock(mStateMutex); | 
|  | oldConnectors.assign(mConnectors.begin(), mConnectors.end()); | 
|  | } | 
|  | { | 
|  | AutoWriteLock lock(mStateMutex); | 
|  | resetDrmElementsLocked(); | 
|  | if (!initDrmElementsLocked()) { | 
|  | ALOGE( | 
|  | "%s: failed to initialize drm elements during hotplug. Displays may " | 
|  | "not function correctly!", | 
|  | __FUNCTION__); | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | AutoReadLock lock(mStateMutex); | 
|  | for (int i = 0; i < mConnectors.size(); i++) { | 
|  | bool changed = | 
|  | oldConnectors[i].dpiX != mConnectors[i].dpiX || | 
|  | oldConnectors[i].dpiY != mConnectors[i].dpiY || | 
|  | oldConnectors[i].connection != mConnectors[i].connection || | 
|  | oldConnectors[i].mMode.hdisplay != mConnectors[i].mMode.hdisplay || | 
|  | oldConnectors[i].mMode.vdisplay != mConnectors[i].mMode.vdisplay; | 
|  | if (changed) { | 
|  | if (i == 0) { | 
|  | ALOGE( | 
|  | "%s: Ignoring changes to display:0 which is not configurable by " | 
|  | "multi-display interface.", | 
|  | __FUNCTION__); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | bool connected = | 
|  | mConnectors[i].connection == DRM_MODE_CONNECTED ? true : false; | 
|  | if (mHotplugCallback) { | 
|  | mHotplugCallback(connected, i, mConnectors[i].mMode.hdisplay, | 
|  | mConnectors[i].mMode.vdisplay, mConnectors[i].dpiX, | 
|  | mConnectors[i].dpiY, | 
|  | mConnectors[i].mRefreshRateAsInteger); | 
|  | } | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | std::tuple<HWC2::Error, base::unique_fd> DrmPresenter::flushToDisplay( | 
|  | int display, hwc_drm_bo_t& bo, base::borrowed_fd inSyncFd) { | 
|  | AutoReadLock lock(mStateMutex); | 
|  |  | 
|  | DrmConnector& connector = mConnectors[display]; | 
|  | DrmCrtc& crtc = mCrtcs[display]; | 
|  |  | 
|  | HWC2::Error error = HWC2::Error::None; | 
|  |  | 
|  | drmModeAtomicReqPtr pset = drmModeAtomicAlloc(); | 
|  |  | 
|  | int ret; | 
|  |  | 
|  | if (!crtc.mDidSetCrtc) { | 
|  | DEBUG_LOG("%s: Setting crtc.\n", __FUNCTION__); | 
|  | ret = drmModeAtomicAddProperty(pset, crtc.mId, crtc.mActivePropertyId, 1); | 
|  | if (ret < 0) { | 
|  | ALOGE("%s:%d: failed %d errno %d\n", __FUNCTION__, __LINE__, ret, errno); | 
|  | } | 
|  | ret = drmModeAtomicAddProperty(pset, crtc.mId, crtc.mModePropertyId, | 
|  | connector.mModeBlobId); | 
|  | if (ret < 0) { | 
|  | ALOGE("%s:%d: failed %d errno %d\n", __FUNCTION__, __LINE__, ret, errno); | 
|  | } | 
|  | ret = drmModeAtomicAddProperty(pset, connector.mId, | 
|  | connector.mCrtcPropertyId, crtc.mId); | 
|  | if (ret < 0) { | 
|  | ALOGE("%s:%d: failed %d errno %d\n", __FUNCTION__, __LINE__, ret, errno); | 
|  | } | 
|  |  | 
|  | crtc.mDidSetCrtc = true; | 
|  | } else { | 
|  | DEBUG_LOG("%s: Already set crtc\n", __FUNCTION__); | 
|  | } | 
|  |  | 
|  | int rawOutSyncFd; | 
|  | uint64_t outSyncFdUint = | 
|  | static_cast<uint64_t>(reinterpret_cast<uintptr_t>(&rawOutSyncFd)); | 
|  |  | 
|  | ret = drmModeAtomicAddProperty(pset, crtc.mId, crtc.mOutFencePtrPropertyId, | 
|  | outSyncFdUint); | 
|  | if (ret < 0) { | 
|  | ALOGE("%s:%d: set OUT_FENCE_PTR failed %d errno %d\n", __FUNCTION__, | 
|  | __LINE__, ret, errno); | 
|  | } | 
|  |  | 
|  | if (crtc.mPlaneId == -1) { | 
|  | ALOGE("%s:%d: no plane available for crtc id %" PRIu32, __FUNCTION__, | 
|  | __LINE__, crtc.mId); | 
|  | return std::make_tuple(HWC2::Error::NoResources, base::unique_fd()); | 
|  | } | 
|  |  | 
|  | DrmPlane& plane = mPlanes[crtc.mPlaneId]; | 
|  |  | 
|  | DEBUG_LOG("%s: set plane: plane id %d crtc id %d fbid %d bo w h %d %d\n", | 
|  | __FUNCTION__, plane.mId, crtc.mId, bo.fb_id, bo.width, bo.height); | 
|  |  | 
|  | ret = drmModeAtomicAddProperty(pset, plane.mId, plane.mCrtcPropertyId, | 
|  | crtc.mId); | 
|  | if (ret < 0) { | 
|  | ALOGE("%s:%d: failed %d errno %d\n", __FUNCTION__, __LINE__, ret, errno); | 
|  | } | 
|  | ret = drmModeAtomicAddProperty(pset, plane.mId, plane.mInFenceFdPropertyId, | 
|  | inSyncFd.get()); | 
|  | if (ret < 0) { | 
|  | ALOGE("%s:%d: set IN_FENCE_FD failed %d errno %d\n", __FUNCTION__, __LINE__, | 
|  | ret, errno); | 
|  | } | 
|  | ret = | 
|  | drmModeAtomicAddProperty(pset, plane.mId, plane.mFbPropertyId, bo.fb_id); | 
|  | if (ret < 0) { | 
|  | ALOGE("%s:%d: failed %d errno %d\n", __FUNCTION__, __LINE__, ret, errno); | 
|  | } | 
|  | ret = drmModeAtomicAddProperty(pset, plane.mId, plane.mCrtcXPropertyId, 0); | 
|  | if (ret < 0) { | 
|  | ALOGE("%s:%d: failed %d errno %d\n", __FUNCTION__, __LINE__, ret, errno); | 
|  | } | 
|  | ret = drmModeAtomicAddProperty(pset, plane.mId, plane.mCrtcYPropertyId, 0); | 
|  | if (ret < 0) { | 
|  | ALOGE("%s:%d: failed %d errno %d\n", __FUNCTION__, __LINE__, ret, errno); | 
|  | } | 
|  | ret = drmModeAtomicAddProperty(pset, plane.mId, plane.mCrtcWPropertyId, | 
|  | bo.width); | 
|  | if (ret < 0) { | 
|  | ALOGE("%s:%d: failed %d errno %d\n", __FUNCTION__, __LINE__, ret, errno); | 
|  | } | 
|  | ret = drmModeAtomicAddProperty(pset, plane.mId, plane.mCrtcHPropertyId, | 
|  | bo.height); | 
|  | if (ret < 0) { | 
|  | ALOGE("%s:%d: failed %d errno %d\n", __FUNCTION__, __LINE__, ret, errno); | 
|  | } | 
|  | ret = drmModeAtomicAddProperty(pset, plane.mId, plane.mSrcXPropertyId, 0); | 
|  | if (ret < 0) { | 
|  | ALOGE("%s:%d: failed %d errno %d\n", __FUNCTION__, __LINE__, ret, errno); | 
|  | } | 
|  | ret = drmModeAtomicAddProperty(pset, plane.mId, plane.mSrcYPropertyId, 0); | 
|  | if (ret < 0) { | 
|  | ALOGE("%s:%d: failed %d errno %d\n", __FUNCTION__, __LINE__, ret, errno); | 
|  | } | 
|  | ret = drmModeAtomicAddProperty(pset, plane.mId, plane.mSrcWPropertyId, | 
|  | bo.width << 16); | 
|  | if (ret < 0) { | 
|  | ALOGE("%s:%d: failed %d errno %d\n", __FUNCTION__, __LINE__, ret, errno); | 
|  | } | 
|  | ret = drmModeAtomicAddProperty(pset, plane.mId, plane.mSrcHPropertyId, | 
|  | bo.height << 16); | 
|  | if (ret < 0) { | 
|  | ALOGE("%s:%d: failed %d errno %d\n", __FUNCTION__, __LINE__, ret, errno); | 
|  | } | 
|  |  | 
|  | uint32_t flags = DRM_MODE_ATOMIC_ALLOW_MODESET; | 
|  | ret = drmModeAtomicCommit(mFd.get(), pset, flags, 0); | 
|  |  | 
|  | if (ret) { | 
|  | ALOGE("%s: Atomic commit failed with %d %d\n", __FUNCTION__, ret, errno); | 
|  | error = HWC2::Error::NoResources; | 
|  | } | 
|  | base::unique_fd outSyncFd(rawOutSyncFd); | 
|  |  | 
|  | if (pset) { | 
|  | drmModeAtomicFree(pset); | 
|  | } | 
|  |  | 
|  | DEBUG_LOG("%s: out fence: %d\n", __FUNCTION__, outSyncFd.get()); | 
|  | return std::make_tuple(error, std::move(outSyncFd)); | 
|  | } | 
|  |  | 
|  | std::optional<std::vector<uint8_t>> DrmPresenter::getEdid(uint32_t id) { | 
|  | AutoReadLock lock(mStateMutex); | 
|  |  | 
|  | if (mConnectors[id].mEdidBlobId == -1) { | 
|  | ALOGW("%s: EDID not supported", __func__); | 
|  | return std::nullopt; | 
|  | } | 
|  | drmModePropertyBlobPtr blob = | 
|  | drmModeGetPropertyBlob(mFd.get(), mConnectors[id].mEdidBlobId); | 
|  | if (!blob) { | 
|  | ALOGE("%s: fail to read EDID from DRM", __func__); | 
|  | return std::nullopt; | 
|  | } | 
|  |  | 
|  | std::vector<uint8_t> edid; | 
|  | uint8_t* start = static_cast<uint8_t*>(blob->data); | 
|  | edid.insert(edid.begin(), start, start + blob->length); | 
|  |  | 
|  | drmModeFreePropertyBlob(blob); | 
|  |  | 
|  | return edid; | 
|  | } | 
|  |  | 
|  | DrmBuffer::DrmBuffer(const native_handle_t* handle, DrmPresenter& DrmPresenter) | 
|  | : mDrmPresenter(DrmPresenter), mBo({}) { | 
|  | if (!convertBoInfo(handle)) { | 
|  | mDrmPresenter.getDrmFB(mBo); | 
|  | } | 
|  | } | 
|  |  | 
|  | DrmBuffer::~DrmBuffer() { mDrmPresenter.clearDrmFB(mBo); } | 
|  |  | 
|  | int DrmBuffer::convertBoInfo(const native_handle_t* handle) { | 
|  | cros_gralloc_handle* gr_handle = (cros_gralloc_handle*)handle; | 
|  | if (!gr_handle) { | 
|  | ALOGE("%s: Null buffer handle", __FUNCTION__); | 
|  | return -1; | 
|  | } | 
|  | mBo.width = gr_handle->width; | 
|  | mBo.height = gr_handle->height; | 
|  | mBo.hal_format = gr_handle->droid_format; | 
|  | mBo.format = gr_handle->format; | 
|  | mBo.usage = gr_handle->usage; | 
|  | mBo.prime_fds[0] = gr_handle->fds[0]; | 
|  | mBo.pitches[0] = gr_handle->strides[0]; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | std::tuple<HWC2::Error, base::unique_fd> DrmBuffer::flushToDisplay( | 
|  | int display, base::borrowed_fd inWaitSyncFd) { | 
|  | return mDrmPresenter.flushToDisplay(display, mBo, inWaitSyncFd); | 
|  | } | 
|  |  | 
|  | DrmPresenter::DrmEventListener::DrmEventListener(DrmPresenter& presenter) | 
|  | : mPresenter(presenter) {} | 
|  |  | 
|  | DrmPresenter::DrmEventListener::~DrmEventListener() {} | 
|  |  | 
|  | bool DrmPresenter::DrmEventListener::init() { | 
|  | mEventFd = android::base::unique_fd( | 
|  | socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT)); | 
|  | if (!mEventFd.ok()) { | 
|  | ALOGE("Failed to open uevent socket: %s", strerror(errno)); | 
|  | return false; | 
|  | } | 
|  | struct sockaddr_nl addr; | 
|  | memset(&addr, 0, sizeof(addr)); | 
|  | addr.nl_family = AF_NETLINK; | 
|  | addr.nl_pid = 0; | 
|  | addr.nl_groups = 0xFFFFFFFF; | 
|  |  | 
|  | int ret = bind(mEventFd, (struct sockaddr*)&addr, sizeof(addr)); | 
|  | if (ret) { | 
|  | ALOGE("Failed to bind uevent socket: %s", strerror(errno)); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | FD_ZERO(&mMonitoredFds); | 
|  | FD_SET(mPresenter.mFd.get(), &mMonitoredFds); | 
|  | FD_SET(mEventFd.get(), &mMonitoredFds); | 
|  | mMaxFd = std::max(mPresenter.mFd.get(), mEventFd.get()); | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool DrmPresenter::DrmEventListener::threadLoop() { | 
|  | int ret; | 
|  | do { | 
|  | ret = select(mMaxFd + 1, &mMonitoredFds, NULL, NULL, NULL); | 
|  | } while (ret == -1 && errno == EINTR); | 
|  |  | 
|  | // if (FD_ISSET(mPresenter.mFd, &mFds)) { | 
|  | //   TODO: handle drm related events | 
|  | // } | 
|  |  | 
|  | if (FD_ISSET(mEventFd.get(), &mMonitoredFds)) { | 
|  | eventThreadLoop(); | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void DrmPresenter::DrmEventListener::eventThreadLoop() { | 
|  | char buffer[1024]; | 
|  | int ret; | 
|  |  | 
|  | struct timespec ts; | 
|  | uint64_t timestamp = 0; | 
|  | ret = clock_gettime(CLOCK_MONOTONIC, &ts); | 
|  | if (!ret) { | 
|  | timestamp = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec; | 
|  | } else { | 
|  | ALOGE("Failed to get monotonic clock on hotplug %d", ret); | 
|  | } | 
|  |  | 
|  | while (true) { | 
|  | ret = read(mEventFd.get(), &buffer, sizeof(buffer)); | 
|  | if (ret == 0) { | 
|  | return; | 
|  | } else if (ret < 0) { | 
|  | ALOGE("Got error reading uevent %d", ret); | 
|  | return; | 
|  | } | 
|  |  | 
|  | bool drmEvent = false, hotplugEvent = false; | 
|  | for (int i = 0; i < ret;) { | 
|  | char* event = buffer + i; | 
|  | if (strcmp(event, "DEVTYPE=drm_minor")) { | 
|  | drmEvent = true; | 
|  | } else if (strcmp(event, "HOTPLUG=1")) { | 
|  | hotplugEvent = true; | 
|  | } | 
|  |  | 
|  | i += strlen(event) + 1; | 
|  | } | 
|  |  | 
|  | if (drmEvent && hotplugEvent) { | 
|  | processHotplug(timestamp); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void DrmPresenter::DrmEventListener::processHotplug(uint64_t timestamp) { | 
|  | ALOGD("DrmEventListener detected hotplug event %" PRIu64, timestamp); | 
|  | mPresenter.handleHotplug(); | 
|  | } | 
|  | }  // namespace android |