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