1 /*
2  * Copyright 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "DrmClient.h"
18 
19 #include <cros_gralloc_handle.h>
20 
21 using ::gfxstream::guest::AutoReadLock;
22 using ::gfxstream::guest::AutoWriteLock;
23 using ::gfxstream::guest::ReadWriteLock;
24 
25 namespace aidl::android::hardware::graphics::composer3::impl {
26 
~DrmClient()27 DrmClient::~DrmClient() {
28     if (mFd > 0) {
29         drmDropMaster(mFd.get());
30     }
31 }
32 
OpenVirtioGpuDrmFd()33 ::android::base::unique_fd DrmClient::OpenVirtioGpuDrmFd() {
34     for (int i = 0; i < 10; i++) {
35         const std::string path = "/dev/dri/card" + std::to_string(i);
36         DEBUG_LOG("%s: trying to open DRM device at %s", __FUNCTION__, path.c_str());
37 
38         ::android::base::unique_fd fd(open(path.c_str(), O_RDWR | O_CLOEXEC));
39 
40         if (fd < 0) {
41             ALOGE("%s: failed to open drm device %s: %s", __FUNCTION__, path.c_str(),
42                   strerror(errno));
43             continue;
44         }
45 
46         auto version = drmGetVersion(fd.get());
47         const std::string name = version->name;
48         drmFreeVersion(version);
49 
50         DEBUG_LOG("%s: The DRM device at %s is \"%s\"", __FUNCTION__, path.c_str(), name.c_str());
51         if (name.find("virtio") != std::string::npos) {
52             return fd;
53         }
54     }
55 
56     ALOGE(
57         "Failed to find virtio-gpu DRM node. Ranchu HWComposer "
58         "is only expected to be used with \"virtio_gpu\"");
59 
60     return ::android::base::unique_fd(-1);
61 }
62 
init()63 HWC3::Error DrmClient::init() {
64     DEBUG_LOG("%s", __FUNCTION__);
65 
66     mFd = OpenVirtioGpuDrmFd();
67     if (mFd < 0) {
68         ALOGE("%s: failed to open drm device: %s", __FUNCTION__, strerror(errno));
69         return HWC3::Error::NoResources;
70     }
71 
72     int ret = drmSetClientCap(mFd.get(), DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
73     if (ret) {
74         ALOGE("%s: failed to set cap universal plane %s\n", __FUNCTION__, strerror(errno));
75         return HWC3::Error::NoResources;
76     }
77 
78     ret = drmSetClientCap(mFd.get(), DRM_CLIENT_CAP_ATOMIC, 1);
79     if (ret) {
80         ALOGE("%s: failed to set cap atomic %s\n", __FUNCTION__, strerror(errno));
81         return HWC3::Error::NoResources;
82     }
83 
84     drmSetMaster(mFd.get());
85 
86     if (!drmIsMaster(mFd.get())) {
87         ALOGE("%s: failed to get master drm device", __FUNCTION__);
88         return HWC3::Error::NoResources;
89     }
90 
91     {
92         AutoWriteLock lock(mDisplaysMutex);
93         bool success = loadDrmDisplays();
94         if (success) {
95             DEBUG_LOG("%s: Successfully initialized DRM backend", __FUNCTION__);
96         } else {
97             ALOGE("%s: Failed to initialize DRM backend", __FUNCTION__);
98             return HWC3::Error::NoResources;
99         }
100     }
101 
102     mDrmEventListener = DrmEventListener::create(mFd, [this]() { handleHotplug(); });
103     if (!mDrmEventListener) {
104         ALOGE("%s: Failed to initialize DRM event listener", __FUNCTION__);
105     } else {
106         DEBUG_LOG("%s: Successfully initialized DRM event listener", __FUNCTION__);
107     }
108 
109     DEBUG_LOG("%s: Successfully initialized.", __FUNCTION__);
110     return HWC3::Error::None;
111 }
112 
getDisplayConfigs(std::vector<DisplayConfig> * configs) const113 HWC3::Error DrmClient::getDisplayConfigs(std::vector<DisplayConfig>* configs) const {
114     DEBUG_LOG("%s", __FUNCTION__);
115 
116     AutoReadLock lock(mDisplaysMutex);
117 
118     configs->clear();
119 
120     for (const auto& display : mDisplays) {
121         if (!display->isConnected()) {
122             continue;
123         }
124 
125         configs->emplace_back(DisplayConfig{
126             .id = display->getId(),
127             .width = display->getWidth(),
128             .height = display->getHeight(),
129             .dpiX = display->getDpiX(),
130             .dpiY = display->getDpiY(),
131             .refreshRateHz = display->getRefreshRateUint(),
132         });
133     }
134 
135     return HWC3::Error::None;
136 }
137 
registerOnHotplugCallback(const HotplugCallback & cb)138 HWC3::Error DrmClient::registerOnHotplugCallback(const HotplugCallback& cb) {
139     mHotplugCallback = cb;
140     return HWC3::Error::None;
141 }
142 
unregisterOnHotplugCallback()143 HWC3::Error DrmClient::unregisterOnHotplugCallback() {
144     mHotplugCallback.reset();
145     return HWC3::Error::None;
146 }
147 
loadDrmDisplays()148 bool DrmClient::loadDrmDisplays() {
149     DEBUG_LOG("%s", __FUNCTION__);
150 
151     std::vector<std::unique_ptr<DrmCrtc>> crtcs;
152     std::vector<std::unique_ptr<DrmConnector>> connectors;
153     std::vector<std::unique_ptr<DrmPlane>> planes;
154 
155     drmModePlaneResPtr drmPlaneResources = drmModeGetPlaneResources(mFd.get());
156     for (uint32_t i = 0; i < drmPlaneResources->count_planes; ++i) {
157         const uint32_t planeId = drmPlaneResources->planes[i];
158 
159         auto crtc = DrmPlane::create(mFd, planeId);
160         if (!crtc) {
161             ALOGE("%s: Failed to create DRM CRTC.", __FUNCTION__);
162             return false;
163         }
164 
165         planes.emplace_back(std::move(crtc));
166     }
167     drmModeFreePlaneResources(drmPlaneResources);
168 
169     drmModeRes* drmResources = drmModeGetResources(mFd.get());
170     for (uint32_t crtcIndex = 0; crtcIndex < drmResources->count_crtcs; crtcIndex++) {
171         const uint32_t crtcId = drmResources->crtcs[crtcIndex];
172 
173         auto crtc = DrmCrtc::create(mFd, crtcId, crtcIndex);
174         if (!crtc) {
175             ALOGE("%s: Failed to create DRM CRTC.", __FUNCTION__);
176             return false;
177         }
178 
179         crtcs.emplace_back(std::move(crtc));
180     }
181 
182     for (uint32_t i = 0; i < drmResources->count_connectors; ++i) {
183         const uint32_t connectorId = drmResources->connectors[i];
184 
185         auto connector = DrmConnector::create(mFd, connectorId);
186         if (!connector) {
187             ALOGE("%s: Failed to create DRM CRTC.", __FUNCTION__);
188             return false;
189         }
190 
191         connectors.emplace_back(std::move(connector));
192     }
193 
194     drmModeFreeResources(drmResources);
195 
196     if (crtcs.size() != connectors.size()) {
197         ALOGE("%s: Failed assumption mCrtcs.size():%zu equals mConnectors.size():%zu", __FUNCTION__,
198               crtcs.size(), connectors.size());
199         return false;
200     }
201 
202     for (uint32_t i = 0; i < crtcs.size(); i++) {
203         std::unique_ptr<DrmCrtc> crtc = std::move(crtcs[i]);
204         std::unique_ptr<DrmConnector> connector = std::move(connectors[i]);
205 
206         auto planeIt =
207             std::find_if(planes.begin(), planes.end(), [&](const std::unique_ptr<DrmPlane>& plane) {
208                 if (!plane->isOverlay() && !plane->isPrimary()) {
209                     return false;
210                 }
211                 return plane->isCompatibleWith(*crtc);
212             });
213         if (planeIt == planes.end()) {
214             ALOGE("%s: Failed to find plane for display:%" PRIu32, __FUNCTION__, i);
215             return false;
216         }
217 
218         std::unique_ptr<DrmPlane> plane = std::move(*planeIt);
219         planes.erase(planeIt);
220 
221         auto display =
222             DrmDisplay::create(i, std::move(connector), std::move(crtc), std::move(plane), mFd);
223         if (!display) {
224             return false;
225         }
226         mDisplays.push_back(std::move(display));
227     }
228 
229     return true;
230 }
231 
create(const native_handle_t * handle)232 std::tuple<HWC3::Error, std::shared_ptr<DrmBuffer>> DrmClient::create(
233     const native_handle_t* handle) {
234     cros_gralloc_handle* crosHandle = (cros_gralloc_handle*)handle;
235     if (crosHandle == nullptr) {
236         ALOGE("%s: invalid cros_gralloc_handle", __FUNCTION__);
237         return std::make_tuple(HWC3::Error::NoResources, nullptr);
238     }
239 
240     DrmPrimeBufferHandle primeHandle = 0;
241     int ret = drmPrimeFDToHandle(mFd.get(), crosHandle->fds[0], &primeHandle);
242     if (ret) {
243         ALOGE("%s: drmPrimeFDToHandle failed: %s (errno %d)", __FUNCTION__, strerror(errno), errno);
244         return std::make_tuple(HWC3::Error::NoResources, nullptr);
245     }
246 
247     auto buffer = std::shared_ptr<DrmBuffer>(new DrmBuffer(*this));
248     buffer->mWidth = crosHandle->width;
249     buffer->mHeight = crosHandle->height;
250     buffer->mDrmFormat = crosHandle->format;
251     buffer->mPlaneFds[0] = crosHandle->fds[0];
252     buffer->mPlaneHandles[0] = primeHandle;
253     buffer->mPlanePitches[0] = crosHandle->strides[0];
254     buffer->mPlaneOffsets[0] = crosHandle->offsets[0];
255 
256     uint32_t framebuffer = 0;
257     ret = drmModeAddFB2(mFd.get(), buffer->mWidth, buffer->mHeight, buffer->mDrmFormat,
258                         buffer->mPlaneHandles, buffer->mPlanePitches, buffer->mPlaneOffsets,
259                         &framebuffer, 0);
260     if (ret) {
261         ALOGE("%s: drmModeAddFB2 failed: %s (errno %d)", __FUNCTION__, strerror(errno), errno);
262         return std::make_tuple(HWC3::Error::NoResources, nullptr);
263     }
264     DEBUG_LOG("%s: created framebuffer:%" PRIu32, __FUNCTION__, framebuffer);
265     buffer->mDrmFramebuffer = framebuffer;
266 
267     return std::make_tuple(HWC3::Error::None, std::shared_ptr<DrmBuffer>(buffer));
268 }
269 
destroyDrmFramebuffer(DrmBuffer * buffer)270 HWC3::Error DrmClient::destroyDrmFramebuffer(DrmBuffer* buffer) {
271     if (buffer->mDrmFramebuffer) {
272         uint32_t framebuffer = *buffer->mDrmFramebuffer;
273         if (drmModeRmFB(mFd.get(), framebuffer)) {
274             ALOGE("%s: drmModeRmFB failed: %s (errno %d)", __FUNCTION__, strerror(errno), errno);
275             return HWC3::Error::NoResources;
276         }
277         DEBUG_LOG("%s: destroyed framebuffer:%" PRIu32, __FUNCTION__, framebuffer);
278         buffer->mDrmFramebuffer.reset();
279     }
280     if (buffer->mPlaneHandles[0]) {
281         struct drm_gem_close gem_close = {};
282         gem_close.handle = buffer->mPlaneHandles[0];
283         if (drmIoctl(mFd.get(), DRM_IOCTL_GEM_CLOSE, &gem_close)) {
284             ALOGE("%s: DRM_IOCTL_GEM_CLOSE failed: %s (errno %d)", __FUNCTION__, strerror(errno),
285                   errno);
286             return HWC3::Error::NoResources;
287         }
288     }
289 
290     return HWC3::Error::None;
291 }
292 
handleHotplug()293 bool DrmClient::handleHotplug() {
294     DEBUG_LOG("%s", __FUNCTION__);
295 
296     struct HotplugToReport {
297         uint32_t id;
298         uint32_t width;
299         uint32_t height;
300         uint32_t dpiX;
301         uint32_t dpiY;
302         uint32_t rr;
303         bool connected;
304     };
305 
306     std::vector<HotplugToReport> hotplugs;
307 
308     {
309         AutoWriteLock lock(mDisplaysMutex);
310 
311         for (auto& display : mDisplays) {
312             auto change = display->checkAndHandleHotplug(mFd);
313             if (change == DrmHotplugChange::kNoChange) {
314                 continue;
315             }
316 
317             hotplugs.push_back(HotplugToReport{
318                 .id = display->getId(),
319                 .width = display->getWidth(),
320                 .height = display->getHeight(),
321                 .dpiX = display->getDpiX(),
322                 .dpiY = display->getDpiY(),
323                 .rr = display->getRefreshRateUint(),
324                 .connected = change == DrmHotplugChange::kConnected,
325             });
326         }
327     }
328 
329     for (const auto& hotplug : hotplugs) {
330         if (mHotplugCallback) {
331             (*mHotplugCallback)(hotplug.connected,  //
332                                 hotplug.id,         //
333                                 hotplug.width,      //
334                                 hotplug.height,     //
335                                 hotplug.dpiX,       //
336                                 hotplug.dpiY,       //
337                                 hotplug.rr);
338         }
339     }
340 
341     return true;
342 }
343 
flushToDisplay(uint32_t displayId,const std::shared_ptr<DrmBuffer> & buffer,::android::base::borrowed_fd inSyncFd)344 std::tuple<HWC3::Error, ::android::base::unique_fd> DrmClient::flushToDisplay(
345     uint32_t displayId, const std::shared_ptr<DrmBuffer>& buffer,
346     ::android::base::borrowed_fd inSyncFd) {
347     ATRACE_CALL();
348 
349     if (!buffer->mDrmFramebuffer) {
350         ALOGE("%s: failed, no framebuffer created.", __FUNCTION__);
351         return std::make_tuple(HWC3::Error::NoResources, ::android::base::unique_fd());
352     }
353 
354     AutoReadLock lock(mDisplaysMutex);
355     return mDisplays[displayId]->flush(mFd, inSyncFd, buffer);
356 }
357 
getEdid(uint32_t displayId)358 std::optional<std::vector<uint8_t>> DrmClient::getEdid(uint32_t displayId) {
359     AutoReadLock lock(mDisplaysMutex);
360 
361     if (displayId >= mDisplays.size()) {
362         DEBUG_LOG("%s: invalid display:%" PRIu32, __FUNCTION__, displayId);
363         return std::nullopt;
364     }
365 
366     return mDisplays[displayId]->getEdid();
367 }
368 
369 }  // namespace aidl::android::hardware::graphics::composer3::impl
370