OSDN Git Service

drm_hwcomposer: CI: Set clang-tidy level to NORMAL for some files
[android-x86/external-drm_hwcomposer.git] / drm / DrmDevice.cpp
1 /*
2  * Copyright (C) 2015 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 #define LOG_TAG "hwc-drm-device"
18
19 #include "DrmDevice.h"
20
21 #include <fcntl.h>
22 #include <xf86drm.h>
23 #include <xf86drmMode.h>
24
25 #include <algorithm>
26 #include <array>
27 #include <cerrno>
28 #include <cinttypes>
29 #include <cstdint>
30 #include <sstream>
31 #include <string>
32
33 #include "drm/DrmPlane.h"
34 #include "utils/log.h"
35 #include "utils/properties.h"
36
37 static void trim_left(std::string *str) {
38   str->erase(std::begin(*str),
39              std::find_if(std::begin(*str), std::end(*str),
40                           [](int ch) { return std::isspace(ch) == 0; }));
41 }
42
43 static void trim_right(std::string *str) {
44   str->erase(std::find_if(std::rbegin(*str), std::rend(*str),
45                           [](int ch) { return std::isspace(ch) == 0; })
46                  .base(),
47              std::end(*str));
48 }
49
50 static void trim(std::string *str) {
51   trim_left(str);
52   trim_right(str);
53 }
54
55 namespace android {
56
57 static std::vector<std::string> read_primary_display_order_prop() {
58   std::array<char, PROPERTY_VALUE_MAX> display_order_buf{};
59   property_get("vendor.hwc.drm.primary_display_order", display_order_buf.data(),
60                "...");
61
62   std::vector<std::string> display_order;
63   std::istringstream str(display_order_buf.data());
64   for (std::string conn_name; std::getline(str, conn_name, ',');) {
65     trim(&conn_name);
66     display_order.push_back(std::move(conn_name));
67   }
68   return display_order;
69 }
70
71 static std::vector<DrmConnector *> make_primary_display_candidates(
72     const std::vector<std::unique_ptr<DrmConnector>> &connectors) {
73   std::vector<DrmConnector *> primary_candidates;
74   std::transform(std::begin(connectors), std::end(connectors),
75                  std::back_inserter(primary_candidates),
76                  [](const std::unique_ptr<DrmConnector> &conn) {
77                    return conn.get();
78                  });
79   primary_candidates.erase(std::remove_if(std::begin(primary_candidates),
80                                           std::end(primary_candidates),
81                                           [](const DrmConnector *conn) {
82                                             return conn->state() !=
83                                                    DRM_MODE_CONNECTED;
84                                           }),
85                            std::end(primary_candidates));
86
87   std::vector<std::string> display_order = read_primary_display_order_prop();
88   bool use_other = display_order.back() == "...";
89
90   // putting connectors from primary_display_order first
91   auto curr_connector = std::begin(primary_candidates);
92   for (const std::string &display_name : display_order) {
93     auto it = std::find_if(std::begin(primary_candidates),
94                            std::end(primary_candidates),
95                            [&display_name](const DrmConnector *conn) {
96                              return conn->name() == display_name;
97                            });
98     if (it != std::end(primary_candidates)) {
99       std::iter_swap(it, curr_connector);
100       ++curr_connector;
101     }
102   }
103
104   if (use_other) {
105     // then putting internal connectors second, everything else afterwards
106     std::partition(curr_connector, std::end(primary_candidates),
107                    [](const DrmConnector *conn) { return conn->internal(); });
108   } else {
109     primary_candidates.erase(curr_connector, std::end(primary_candidates));
110   }
111
112   return primary_candidates;
113 }
114
115 DrmDevice::DrmDevice() {
116   self.reset(this);
117   mDrmFbImporter = std::make_unique<DrmFbImporter>(self);
118 }
119
120 // NOLINTNEXTLINE (readability-function-cognitive-complexity): Fixme
121 std::tuple<int, int> DrmDevice::Init(const char *path, int num_displays) {
122   /* TODO: Use drmOpenControl here instead */
123   fd_ = UniqueFd(open(path, O_RDWR | O_CLOEXEC));
124   if (fd() < 0) {
125     // NOLINTNEXTLINE(concurrency-mt-unsafe): Fixme
126     ALOGE("Failed to open dri %s: %s", path, strerror(errno));
127     return std::make_tuple(-ENODEV, 0);
128   }
129
130   int ret = drmSetClientCap(fd(), DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
131   if (ret) {
132     ALOGE("Failed to set universal plane cap %d", ret);
133     return std::make_tuple(ret, 0);
134   }
135
136   ret = drmSetClientCap(fd(), DRM_CLIENT_CAP_ATOMIC, 1);
137   if (ret) {
138     ALOGE("Failed to set atomic cap %d", ret);
139     return std::make_tuple(ret, 0);
140   }
141
142 #ifdef DRM_CLIENT_CAP_WRITEBACK_CONNECTORS
143   ret = drmSetClientCap(fd(), DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1);
144   if (ret) {
145     ALOGI("Failed to set writeback cap %d", ret);
146     ret = 0;
147   }
148 #endif
149
150   uint64_t cap_value = 0;
151   if (drmGetCap(fd(), DRM_CAP_ADDFB2_MODIFIERS, &cap_value)) {
152     ALOGW("drmGetCap failed. Fallback to no modifier support.");
153     cap_value = 0;
154   }
155   HasAddFb2ModifiersSupport_ = cap_value != 0;
156
157   drmSetMaster(fd());
158   if (!drmIsMaster(fd())) {
159     ALOGE("DRM/KMS master access required");
160     return std::make_tuple(-EACCES, 0);
161   }
162
163   auto res = MakeDrmModeResUnique(fd());
164   if (!res) {
165     ALOGE("Failed to get DrmDevice resources");
166     return std::make_tuple(-ENODEV, 0);
167   }
168
169   min_resolution_ = std::pair<uint32_t, uint32_t>(res->min_width,
170                                                   res->min_height);
171   max_resolution_ = std::pair<uint32_t, uint32_t>(res->max_width,
172                                                   res->max_height);
173
174   // Assumes that the primary display will always be in the first
175   // drm_device opened.
176   bool found_primary = num_displays != 0;
177
178   for (int i = 0; !ret && i < res->count_crtcs; ++i) {
179     auto c = MakeDrmModeCrtcUnique(fd(), res->crtcs[i]);
180     if (!c) {
181       ALOGE("Failed to get crtc %d", res->crtcs[i]);
182       ret = -ENODEV;
183       break;
184     }
185
186     std::unique_ptr<DrmCrtc> crtc(new DrmCrtc(this, c.get(), i));
187
188     ret = crtc->Init();
189     if (ret) {
190       ALOGE("Failed to initialize crtc %d", res->crtcs[i]);
191       break;
192     }
193     crtcs_.emplace_back(std::move(crtc));
194   }
195
196   std::vector<uint32_t> possible_clones;
197   for (int i = 0; !ret && i < res->count_encoders; ++i) {
198     auto e = MakeDrmModeEncoderUnique(fd(), res->encoders[i]);
199     if (!e) {
200       ALOGE("Failed to get encoder %d", res->encoders[i]);
201       ret = -ENODEV;
202       break;
203     }
204
205     std::vector<DrmCrtc *> possible_crtcs;
206     DrmCrtc *current_crtc = nullptr;
207     for (auto &crtc : crtcs_) {
208       if ((1 << crtc->pipe()) & e->possible_crtcs)
209         possible_crtcs.push_back(crtc.get());
210
211       if (crtc->id() == e->crtc_id)
212         current_crtc = crtc.get();
213     }
214
215     std::unique_ptr<DrmEncoder> enc(
216         new DrmEncoder(e.get(), current_crtc, possible_crtcs));
217     possible_clones.push_back(e->possible_clones);
218
219     encoders_.emplace_back(std::move(enc));
220   }
221
222   for (unsigned int i = 0; i < encoders_.size(); i++) {
223     for (unsigned int j = 0; j < encoders_.size(); j++)
224       if (possible_clones[i] & (1 << j))
225         encoders_[i]->AddPossibleClone(encoders_[j].get());
226   }
227
228   for (int i = 0; !ret && i < res->count_connectors; ++i) {
229     auto c = MakeDrmModeConnectorUnique(fd(), res->connectors[i]);
230     if (!c) {
231       ALOGE("Failed to get connector %d", res->connectors[i]);
232       ret = -ENODEV;
233       break;
234     }
235
236     std::vector<DrmEncoder *> possible_encoders;
237     DrmEncoder *current_encoder = nullptr;
238     for (int j = 0; j < c->count_encoders; ++j) {
239       for (auto &encoder : encoders_) {
240         if (encoder->id() == c->encoders[j])
241           possible_encoders.push_back(encoder.get());
242         if (encoder->id() == c->encoder_id)
243           current_encoder = encoder.get();
244       }
245     }
246
247     std::unique_ptr<DrmConnector> conn(
248         new DrmConnector(this, c.get(), current_encoder, possible_encoders));
249
250     ret = conn->Init();
251     if (ret) {
252       ALOGE("Init connector %d failed", res->connectors[i]);
253       break;
254     }
255
256     if (conn->writeback())
257       writeback_connectors_.emplace_back(std::move(conn));
258     else
259       connectors_.emplace_back(std::move(conn));
260   }
261
262   // Primary display priority:
263   // 1) vendor.hwc.drm.primary_display_order property
264   // 2) internal connectors
265   // 3) anything else
266   std::vector<DrmConnector *>
267       primary_candidates = make_primary_display_candidates(connectors_);
268   if (!primary_candidates.empty() && !found_primary) {
269     DrmConnector &conn = **std::begin(primary_candidates);
270     conn.set_display(num_displays);
271     displays_[num_displays] = num_displays;
272     ++num_displays;
273     found_primary = true;
274   } else {
275     ALOGE(
276         "Failed to find primary display from "
277         "\"vendor.hwc.drm.primary_display_order\" property");
278   }
279
280   // If no priority display were found then pick first available as primary and
281   // for the others assign consecutive display_numbers.
282   for (auto &conn : connectors_) {
283     if (conn->external() || conn->internal()) {
284       if (!found_primary) {
285         conn->set_display(num_displays);
286         displays_[num_displays] = num_displays;
287         found_primary = true;
288         ++num_displays;
289       } else if (conn->display() < 0) {
290         conn->set_display(num_displays);
291         displays_[num_displays] = num_displays;
292         ++num_displays;
293       }
294     }
295   }
296
297   // Catch-all for the above loops
298   if (ret)
299     return std::make_tuple(ret, 0);
300
301   auto plane_res = MakeDrmModePlaneResUnique(fd());
302   if (!plane_res) {
303     ALOGE("Failed to get plane resources");
304     return std::make_tuple(-ENOENT, 0);
305   }
306
307   for (uint32_t i = 0; i < plane_res->count_planes; ++i) {
308     auto p = MakeDrmModePlaneUnique(fd(), plane_res->planes[i]);
309     if (!p) {
310       ALOGE("Failed to get plane %d", plane_res->planes[i]);
311       ret = -ENODEV;
312       break;
313     }
314
315     std::unique_ptr<DrmPlane> plane(new DrmPlane(this, p.get()));
316
317     ret = plane->Init();
318     if (ret) {
319       ALOGE("Init plane %d failed", plane_res->planes[i]);
320       break;
321     }
322
323     planes_.emplace_back(std::move(plane));
324   }
325   if (ret)
326     return std::make_tuple(ret, 0);
327
328   for (auto &conn : connectors_) {
329     ret = CreateDisplayPipe(conn.get());
330     if (ret) {
331       ALOGE("Failed CreateDisplayPipe %d with %d", conn->id(), ret);
332       return std::make_tuple(ret, 0);
333     }
334     if (!AttachWriteback(conn.get())) {
335       ALOGI("Display %d has writeback attach to it", conn->display());
336     }
337   }
338   return std::make_tuple(ret, displays_.size());
339 }
340
341 bool DrmDevice::HandlesDisplay(int display) const {
342   return displays_.find(display) != displays_.end();
343 }
344
345 DrmConnector *DrmDevice::GetConnectorForDisplay(int display) const {
346   for (const auto &conn : connectors_) {
347     if (conn->display() == display)
348       return conn.get();
349   }
350   return nullptr;
351 }
352
353 DrmConnector *DrmDevice::GetWritebackConnectorForDisplay(int display) const {
354   for (const auto &conn : writeback_connectors_) {
355     if (conn->display() == display)
356       return conn.get();
357   }
358   return nullptr;
359 }
360
361 // TODO(nobody): what happens when hotplugging
362 DrmConnector *DrmDevice::AvailableWritebackConnector(int display) const {
363   DrmConnector *writeback_conn = GetWritebackConnectorForDisplay(display);
364   DrmConnector *display_conn = GetConnectorForDisplay(display);
365   // If we have a writeback already attached to the same CRTC just use that,
366   // if possible.
367   if (display_conn && writeback_conn &&
368       writeback_conn->encoder()->CanClone(display_conn->encoder()))
369     return writeback_conn;
370
371   // Use another CRTC if available and doesn't have any connector
372   for (const auto &crtc : crtcs_) {
373     if (crtc->display() == display)
374       continue;
375     display_conn = GetConnectorForDisplay(crtc->display());
376     // If we have a display connected don't use it for writeback
377     if (display_conn && display_conn->state() == DRM_MODE_CONNECTED)
378       continue;
379     writeback_conn = GetWritebackConnectorForDisplay(crtc->display());
380     if (writeback_conn)
381       return writeback_conn;
382   }
383   return nullptr;
384 }
385
386 DrmCrtc *DrmDevice::GetCrtcForDisplay(int display) const {
387   for (const auto &crtc : crtcs_) {
388     if (crtc->display() == display)
389       return crtc.get();
390   }
391   return nullptr;
392 }
393
394 const std::vector<std::unique_ptr<DrmCrtc>> &DrmDevice::crtcs() const {
395   return crtcs_;
396 }
397
398 uint32_t DrmDevice::next_mode_id() {
399   return ++mode_id_;
400 }
401
402 int DrmDevice::TryEncoderForDisplay(int display, DrmEncoder *enc) {
403   /* First try to use the currently-bound crtc */
404   DrmCrtc *crtc = enc->crtc();
405   if (crtc && crtc->can_bind(display)) {
406     crtc->set_display(display);
407     enc->set_crtc(crtc);
408     return 0;
409   }
410
411   /* Try to find a possible crtc which will work */
412   for (DrmCrtc *crtc : enc->possible_crtcs()) {
413     /* We've already tried this earlier */
414     if (crtc == enc->crtc())
415       continue;
416
417     if (crtc->can_bind(display)) {
418       crtc->set_display(display);
419       enc->set_crtc(crtc);
420       return 0;
421     }
422   }
423
424   /* We can't use the encoder, but nothing went wrong, try another one */
425   return -EAGAIN;
426 }
427
428 int DrmDevice::CreateDisplayPipe(DrmConnector *connector) {
429   int display = connector->display();
430   /* Try to use current setup first */
431   if (connector->encoder()) {
432     int ret = TryEncoderForDisplay(display, connector->encoder());
433     if (!ret) {
434       return 0;
435     }
436
437     if (ret != -EAGAIN) {
438       ALOGE("Could not set mode %d/%d", display, ret);
439       return ret;
440     }
441   }
442
443   for (DrmEncoder *enc : connector->possible_encoders()) {
444     int ret = TryEncoderForDisplay(display, enc);
445     if (!ret) {
446       connector->set_encoder(enc);
447       return 0;
448     }
449
450     if (ret != -EAGAIN) {
451       ALOGE("Could not set mode %d/%d", display, ret);
452       return ret;
453     }
454   }
455   ALOGE("Could not find a suitable encoder/crtc for display %d",
456         connector->display());
457   return -ENODEV;
458 }
459
460 // Attach writeback connector to the CRTC linked to the display_conn
461 int DrmDevice::AttachWriteback(DrmConnector *display_conn) {
462   DrmCrtc *display_crtc = display_conn->encoder()->crtc();
463   if (GetWritebackConnectorForDisplay(display_crtc->display()) != nullptr) {
464     ALOGE("Display already has writeback attach to it");
465     return -EINVAL;
466   }
467   for (auto &writeback_conn : writeback_connectors_) {
468     if (writeback_conn->display() >= 0)
469       continue;
470     for (DrmEncoder *writeback_enc : writeback_conn->possible_encoders()) {
471       for (DrmCrtc *possible_crtc : writeback_enc->possible_crtcs()) {
472         if (possible_crtc != display_crtc)
473           continue;
474         // Use just encoders which had not been bound already
475         if (writeback_enc->can_bind(display_crtc->display())) {
476           writeback_enc->set_crtc(display_crtc);
477           writeback_conn->set_encoder(writeback_enc);
478           writeback_conn->set_display(display_crtc->display());
479           writeback_conn->UpdateModes();
480           return 0;
481         }
482       }
483     }
484   }
485   return -EINVAL;
486 }
487
488 auto DrmDevice::RegisterUserPropertyBlob(void *data, size_t length) const
489     -> DrmModeUserPropertyBlobUnique {
490   struct drm_mode_create_blob create_blob {};
491   create_blob.length = length;
492   create_blob.data = (__u64)data;
493
494   int ret = drmIoctl(fd(), DRM_IOCTL_MODE_CREATEPROPBLOB, &create_blob);
495   if (ret) {
496     ALOGE("Failed to create mode property blob %d", ret);
497     return DrmModeUserPropertyBlobUnique();
498   }
499
500   return DrmModeUserPropertyBlobUnique(
501       new uint32_t(create_blob.blob_id), [this](const uint32_t *it) {
502         struct drm_mode_destroy_blob destroy_blob {};
503         destroy_blob.blob_id = (__u32)*it;
504         int err = drmIoctl(fd(), DRM_IOCTL_MODE_DESTROYPROPBLOB, &destroy_blob);
505         if (err != 0) {
506           ALOGE("Failed to destroy mode property blob %" PRIu32 "/%d", *it,
507                 err);
508         }
509         // NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
510         delete it;
511       });
512 }
513
514 int DrmDevice::GetProperty(uint32_t obj_id, uint32_t obj_type,
515                            const char *prop_name, DrmProperty *property) const {
516   drmModeObjectPropertiesPtr props = nullptr;
517
518   props = drmModeObjectGetProperties(fd(), obj_id, obj_type);
519   if (!props) {
520     ALOGE("Failed to get properties for %d/%x", obj_id, obj_type);
521     return -ENODEV;
522   }
523
524   bool found = false;
525   for (int i = 0; !found && (size_t)i < props->count_props; ++i) {
526     drmModePropertyPtr p = drmModeGetProperty(fd(), props->props[i]);
527     if (!strcmp(p->name, prop_name)) {
528       property->Init(obj_id, p, props->prop_values[i]);
529       found = true;
530     }
531     drmModeFreeProperty(p);
532   }
533
534   drmModeFreeObjectProperties(props);
535   return found ? 0 : -ENOENT;
536 }
537
538 int DrmDevice::GetCrtcProperty(const DrmCrtc &crtc, const char *prop_name,
539                                DrmProperty *property) const {
540   return GetProperty(crtc.id(), DRM_MODE_OBJECT_CRTC, prop_name, property);
541 }
542
543 int DrmDevice::GetConnectorProperty(const DrmConnector &connector,
544                                     const char *prop_name,
545                                     DrmProperty *property) const {
546   return GetProperty(connector.id(), DRM_MODE_OBJECT_CONNECTOR, prop_name,
547                      property);
548 }
549
550 std::string DrmDevice::GetName() const {
551   auto *ver = drmGetVersion(fd());
552   if (!ver) {
553     ALOGW("Failed to get drm version for fd=%d", fd());
554     return "generic";
555   }
556
557   std::string name(ver->name);
558   drmFreeVersion(ver);
559   return name;
560 }
561
562 auto DrmDevice::IsKMSDev(const char *path) -> bool {
563   auto fd = UniqueFd(open(path, O_RDWR | O_CLOEXEC));
564   if (!fd) {
565     return false;
566   }
567
568   auto res = MakeDrmModeResUnique(fd.Get());
569   if (!res) {
570     return false;
571   }
572
573   bool is_kms = res->count_crtcs > 0 && res->count_connectors > 0 &&
574                 res->count_encoders > 0;
575
576   return is_kms;
577 }
578
579 }  // namespace android