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 ::android::base::guest::AutoReadLock;
22 using ::android::base::guest::AutoWriteLock;
23 using ::android::base::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
init()33 HWC3::Error DrmClient::init() {
34 DEBUG_LOG("%s", __FUNCTION__);
35
36 mFd = ::android::base::unique_fd(open("/dev/dri/card0", O_RDWR | O_CLOEXEC));
37 if (mFd < 0) {
38 ALOGE("%s: failed to open drm device: %s", __FUNCTION__, strerror(errno));
39 return HWC3::Error::NoResources;
40 }
41
42 int ret = drmSetClientCap(mFd.get(), DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
43 if (ret) {
44 ALOGE("%s: failed to set cap universal plane %s\n", __FUNCTION__, strerror(errno));
45 return HWC3::Error::NoResources;
46 }
47
48 ret = drmSetClientCap(mFd.get(), DRM_CLIENT_CAP_ATOMIC, 1);
49 if (ret) {
50 ALOGE("%s: failed to set cap atomic %s\n", __FUNCTION__, strerror(errno));
51 return HWC3::Error::NoResources;
52 }
53
54 drmSetMaster(mFd.get());
55
56 if (!drmIsMaster(mFd.get())) {
57 ALOGE("%s: failed to get master drm device", __FUNCTION__);
58 return HWC3::Error::NoResources;
59 }
60
61 {
62 AutoWriteLock lock(mDisplaysMutex);
63 bool success = loadDrmDisplays();
64 if (success) {
65 DEBUG_LOG("%s: Successfully initialized DRM backend", __FUNCTION__);
66 } else {
67 ALOGE("%s: Failed to initialize DRM backend", __FUNCTION__);
68 return HWC3::Error::NoResources;
69 }
70 }
71
72 mDrmEventListener = DrmEventListener::create(mFd, [this]() { handleHotplug(); });
73 if (!mDrmEventListener) {
74 ALOGE("%s: Failed to initialize DRM event listener", __FUNCTION__);
75 } else {
76 DEBUG_LOG("%s: Successfully initialized DRM event listener", __FUNCTION__);
77 }
78
79 DEBUG_LOG("%s: Successfully initialized.", __FUNCTION__);
80 return HWC3::Error::None;
81 }
82
getDisplayConfigs(std::vector<DisplayConfig> * configs) const83 HWC3::Error DrmClient::getDisplayConfigs(std::vector<DisplayConfig>* configs) const {
84 DEBUG_LOG("%s", __FUNCTION__);
85
86 AutoReadLock lock(mDisplaysMutex);
87
88 configs->clear();
89
90 for (const auto& display : mDisplays) {
91 if (!display->isConnected()) {
92 continue;
93 }
94
95 configs->emplace_back(DisplayConfig{
96 .id = display->getId(),
97 .width = display->getWidth(),
98 .height = display->getHeight(),
99 .dpiX = display->getDpiX(),
100 .dpiY = display->getDpiY(),
101 .refreshRateHz = display->getRefreshRateUint(),
102 });
103 }
104
105 return HWC3::Error::None;
106 }
107
registerOnHotplugCallback(const HotplugCallback & cb)108 HWC3::Error DrmClient::registerOnHotplugCallback(const HotplugCallback& cb) {
109 mHotplugCallback = cb;
110 return HWC3::Error::None;
111 }
112
unregisterOnHotplugCallback()113 HWC3::Error DrmClient::unregisterOnHotplugCallback() {
114 mHotplugCallback.reset();
115 return HWC3::Error::None;
116 }
117
loadDrmDisplays()118 bool DrmClient::loadDrmDisplays() {
119 DEBUG_LOG("%s", __FUNCTION__);
120
121 std::vector<std::unique_ptr<DrmCrtc>> crtcs;
122 std::vector<std::unique_ptr<DrmConnector>> connectors;
123 std::vector<std::unique_ptr<DrmPlane>> planes;
124
125 drmModePlaneResPtr drmPlaneResources = drmModeGetPlaneResources(mFd.get());
126 for (uint32_t i = 0; i < drmPlaneResources->count_planes; ++i) {
127 const uint32_t planeId = drmPlaneResources->planes[i];
128
129 auto crtc = DrmPlane::create(mFd, planeId);
130 if (!crtc) {
131 ALOGE("%s: Failed to create DRM CRTC.", __FUNCTION__);
132 return false;
133 }
134
135 planes.emplace_back(std::move(crtc));
136 }
137 drmModeFreePlaneResources(drmPlaneResources);
138
139 drmModeRes* drmResources = drmModeGetResources(mFd.get());
140 for (uint32_t crtcIndex = 0; crtcIndex < drmResources->count_crtcs; crtcIndex++) {
141 const uint32_t crtcId = drmResources->crtcs[crtcIndex];
142
143 auto crtc = DrmCrtc::create(mFd, crtcId, crtcIndex);
144 if (!crtc) {
145 ALOGE("%s: Failed to create DRM CRTC.", __FUNCTION__);
146 return false;
147 }
148
149 crtcs.emplace_back(std::move(crtc));
150 }
151
152 for (uint32_t i = 0; i < drmResources->count_connectors; ++i) {
153 const uint32_t connectorId = drmResources->connectors[i];
154
155 auto connector = DrmConnector::create(mFd, connectorId);
156 if (!connector) {
157 ALOGE("%s: Failed to create DRM CRTC.", __FUNCTION__);
158 return false;
159 }
160
161 connectors.emplace_back(std::move(connector));
162 }
163
164 drmModeFreeResources(drmResources);
165
166 if (crtcs.size() != connectors.size()) {
167 ALOGE("%s: Failed assumption mCrtcs.size():%zu equals mConnectors.size():%zu", __FUNCTION__,
168 crtcs.size(), connectors.size());
169 return false;
170 }
171
172 for (uint32_t i = 0; i < crtcs.size(); i++) {
173 std::unique_ptr<DrmCrtc> crtc = std::move(crtcs[i]);
174 std::unique_ptr<DrmConnector> connector = std::move(connectors[i]);
175
176 auto planeIt =
177 std::find_if(planes.begin(), planes.end(), [&](const std::unique_ptr<DrmPlane>& plane) {
178 if (!plane->isOverlay() && !plane->isPrimary()) {
179 return false;
180 }
181 return plane->isCompatibleWith(*crtc);
182 });
183 if (planeIt == planes.end()) {
184 ALOGE("%s: Failed to find plane for display:%" PRIu32, __FUNCTION__, i);
185 return false;
186 }
187
188 std::unique_ptr<DrmPlane> plane = std::move(*planeIt);
189 planes.erase(planeIt);
190
191 auto display =
192 DrmDisplay::create(i, std::move(connector), std::move(crtc), std::move(plane), mFd);
193 if (!display) {
194 return false;
195 }
196 mDisplays.push_back(std::move(display));
197 }
198
199 return true;
200 }
201
create(const native_handle_t * handle)202 std::tuple<HWC3::Error, std::shared_ptr<DrmBuffer>> DrmClient::create(
203 const native_handle_t* handle) {
204 cros_gralloc_handle* crosHandle = (cros_gralloc_handle*)handle;
205 if (crosHandle == nullptr) {
206 ALOGE("%s: invalid cros_gralloc_handle", __FUNCTION__);
207 return std::make_tuple(HWC3::Error::NoResources, nullptr);
208 }
209
210 DrmPrimeBufferHandle primeHandle = 0;
211 int ret = drmPrimeFDToHandle(mFd.get(), crosHandle->fds[0], &primeHandle);
212 if (ret) {
213 ALOGE("%s: drmPrimeFDToHandle failed: %s (errno %d)", __FUNCTION__, strerror(errno), errno);
214 return std::make_tuple(HWC3::Error::NoResources, nullptr);
215 }
216
217 auto buffer = std::shared_ptr<DrmBuffer>(new DrmBuffer(*this));
218 buffer->mWidth = crosHandle->width;
219 buffer->mHeight = crosHandle->height;
220 buffer->mDrmFormat = crosHandle->format;
221 buffer->mPlaneFds[0] = crosHandle->fds[0];
222 buffer->mPlaneHandles[0] = primeHandle;
223 buffer->mPlanePitches[0] = crosHandle->strides[0];
224 buffer->mPlaneOffsets[0] = crosHandle->offsets[0];
225
226 uint32_t framebuffer = 0;
227 ret = drmModeAddFB2(mFd.get(), buffer->mWidth, buffer->mHeight, buffer->mDrmFormat,
228 buffer->mPlaneHandles, buffer->mPlanePitches, buffer->mPlaneOffsets,
229 &framebuffer, 0);
230 if (ret) {
231 ALOGE("%s: drmModeAddFB2 failed: %s (errno %d)", __FUNCTION__, strerror(errno), errno);
232 return std::make_tuple(HWC3::Error::NoResources, nullptr);
233 }
234 DEBUG_LOG("%s: created framebuffer:%" PRIu32, __FUNCTION__, framebuffer);
235 buffer->mDrmFramebuffer = framebuffer;
236
237 return std::make_tuple(HWC3::Error::None, std::shared_ptr<DrmBuffer>(buffer));
238 }
239
destroyDrmFramebuffer(DrmBuffer * buffer)240 HWC3::Error DrmClient::destroyDrmFramebuffer(DrmBuffer* buffer) {
241 if (buffer->mDrmFramebuffer) {
242 uint32_t framebuffer = *buffer->mDrmFramebuffer;
243 if (drmModeRmFB(mFd.get(), framebuffer)) {
244 ALOGE("%s: drmModeRmFB failed: %s (errno %d)", __FUNCTION__, strerror(errno), errno);
245 return HWC3::Error::NoResources;
246 }
247 DEBUG_LOG("%s: destroyed framebuffer:%" PRIu32, __FUNCTION__, framebuffer);
248 buffer->mDrmFramebuffer.reset();
249 }
250 if (buffer->mPlaneHandles[0]) {
251 struct drm_gem_close gem_close = {};
252 gem_close.handle = buffer->mPlaneHandles[0];
253 if (drmIoctl(mFd.get(), DRM_IOCTL_GEM_CLOSE, &gem_close)) {
254 ALOGE("%s: DRM_IOCTL_GEM_CLOSE failed: %s (errno %d)", __FUNCTION__, strerror(errno),
255 errno);
256 return HWC3::Error::NoResources;
257 }
258 }
259
260 return HWC3::Error::None;
261 }
262
handleHotplug()263 bool DrmClient::handleHotplug() {
264 DEBUG_LOG("%s", __FUNCTION__);
265
266 struct HotplugToReport {
267 uint32_t id;
268 uint32_t width;
269 uint32_t height;
270 uint32_t dpiX;
271 uint32_t dpiY;
272 uint32_t rr;
273 bool connected;
274 };
275
276 std::vector<HotplugToReport> hotplugs;
277
278 {
279 AutoWriteLock lock(mDisplaysMutex);
280
281 for (auto& display : mDisplays) {
282 auto change = display->checkAndHandleHotplug(mFd);
283 if (change == DrmHotplugChange::kNoChange) {
284 continue;
285 }
286
287 hotplugs.push_back(HotplugToReport{
288 .id = display->getId(),
289 .width = display->getWidth(),
290 .height = display->getHeight(),
291 .dpiX = display->getDpiX(),
292 .dpiY = display->getDpiY(),
293 .rr = display->getRefreshRateUint(),
294 .connected = change == DrmHotplugChange::kConnected,
295 });
296 }
297 }
298
299 for (const auto& hotplug : hotplugs) {
300 if (mHotplugCallback) {
301 (*mHotplugCallback)(hotplug.connected, //
302 hotplug.id, //
303 hotplug.width, //
304 hotplug.height, //
305 hotplug.dpiX, //
306 hotplug.dpiY, //
307 hotplug.rr);
308 }
309 }
310
311 return true;
312 }
313
flushToDisplay(int displayId,const std::shared_ptr<DrmBuffer> & buffer,::android::base::borrowed_fd inSyncFd)314 std::tuple<HWC3::Error, ::android::base::unique_fd> DrmClient::flushToDisplay(
315 int displayId, const std::shared_ptr<DrmBuffer>& buffer,
316 ::android::base::borrowed_fd inSyncFd) {
317 ATRACE_CALL();
318
319 if (!buffer->mDrmFramebuffer) {
320 ALOGE("%s: failed, no framebuffer created.", __FUNCTION__);
321 return std::make_tuple(HWC3::Error::NoResources, ::android::base::unique_fd());
322 }
323
324 AutoReadLock lock(mDisplaysMutex);
325 return mDisplays[displayId]->flush(mFd, inSyncFd, buffer);
326 }
327
getEdid(uint32_t displayId)328 std::optional<std::vector<uint8_t>> DrmClient::getEdid(uint32_t displayId) {
329 AutoReadLock lock(mDisplaysMutex);
330
331 if (displayId >= mDisplays.size()) {
332 DEBUG_LOG("%s: invalid display:%" PRIu32, __FUNCTION__, displayId);
333 return std::nullopt;
334 }
335
336 return mDisplays[displayId]->getEdid();
337 }
338
339 } // namespace aidl::android::hardware::graphics::composer3::impl
340