OSDN Git Service

drm_hwcomposer: Tidy-up DrmPlane class
[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     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
309     auto plane = DrmPlane::CreateInstance(*this, plane_res->planes[i]);
310
311     if (plane) {
312       planes_.emplace_back(std::move(plane));
313     }
314   }
315
316   for (auto &conn : connectors_) {
317     ret = CreateDisplayPipe(conn.get());
318     if (ret) {
319       ALOGE("Failed CreateDisplayPipe %d with %d", conn->id(), ret);
320       return std::make_tuple(ret, 0);
321     }
322   }
323   return std::make_tuple(ret, displays_.size());
324 }
325
326 bool DrmDevice::HandlesDisplay(int display) const {
327   return displays_.find(display) != displays_.end();
328 }
329
330 DrmConnector *DrmDevice::GetConnectorForDisplay(int display) const {
331   for (const auto &conn : connectors_) {
332     if (conn->display() == display)
333       return conn.get();
334   }
335   return nullptr;
336 }
337
338 DrmCrtc *DrmDevice::GetCrtcForDisplay(int display) const {
339   for (const auto &crtc : crtcs_) {
340     if (crtc->display() == display)
341       return crtc.get();
342   }
343   return nullptr;
344 }
345
346 const std::vector<std::unique_ptr<DrmCrtc>> &DrmDevice::crtcs() const {
347   return crtcs_;
348 }
349
350 uint32_t DrmDevice::next_mode_id() {
351   return ++mode_id_;
352 }
353
354 int DrmDevice::TryEncoderForDisplay(int display, DrmEncoder *enc) {
355   /* First try to use the currently-bound crtc */
356   DrmCrtc *crtc = enc->crtc();
357   if (crtc && crtc->can_bind(display)) {
358     crtc->set_display(display);
359     enc->set_crtc(crtc);
360     return 0;
361   }
362
363   /* Try to find a possible crtc which will work */
364   for (DrmCrtc *crtc : enc->possible_crtcs()) {
365     /* We've already tried this earlier */
366     if (crtc == enc->crtc())
367       continue;
368
369     if (crtc->can_bind(display)) {
370       crtc->set_display(display);
371       enc->set_crtc(crtc);
372       return 0;
373     }
374   }
375
376   /* We can't use the encoder, but nothing went wrong, try another one */
377   return -EAGAIN;
378 }
379
380 int DrmDevice::CreateDisplayPipe(DrmConnector *connector) {
381   int display = connector->display();
382   /* Try to use current setup first */
383   if (connector->encoder()) {
384     int ret = TryEncoderForDisplay(display, connector->encoder());
385     if (!ret) {
386       return 0;
387     }
388
389     if (ret != -EAGAIN) {
390       ALOGE("Could not set mode %d/%d", display, ret);
391       return ret;
392     }
393   }
394
395   for (DrmEncoder *enc : connector->possible_encoders()) {
396     int ret = TryEncoderForDisplay(display, enc);
397     if (!ret) {
398       connector->set_encoder(enc);
399       return 0;
400     }
401
402     if (ret != -EAGAIN) {
403       ALOGE("Could not set mode %d/%d", display, ret);
404       return ret;
405     }
406   }
407   ALOGE("Could not find a suitable encoder/crtc for display %d",
408         connector->display());
409   return -ENODEV;
410 }
411
412 auto DrmDevice::RegisterUserPropertyBlob(void *data, size_t length) const
413     -> DrmModeUserPropertyBlobUnique {
414   struct drm_mode_create_blob create_blob {};
415   create_blob.length = length;
416   create_blob.data = (__u64)data;
417
418   int ret = drmIoctl(fd(), DRM_IOCTL_MODE_CREATEPROPBLOB, &create_blob);
419   if (ret) {
420     ALOGE("Failed to create mode property blob %d", ret);
421     return {};
422   }
423
424   return DrmModeUserPropertyBlobUnique(
425       new uint32_t(create_blob.blob_id), [this](const uint32_t *it) {
426         struct drm_mode_destroy_blob destroy_blob {};
427         destroy_blob.blob_id = (__u32)*it;
428         int err = drmIoctl(fd(), DRM_IOCTL_MODE_DESTROYPROPBLOB, &destroy_blob);
429         if (err != 0) {
430           ALOGE("Failed to destroy mode property blob %" PRIu32 "/%d", *it,
431                 err);
432         }
433         // NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
434         delete it;
435       });
436 }
437
438 int DrmDevice::GetProperty(uint32_t obj_id, uint32_t obj_type,
439                            const char *prop_name, DrmProperty *property) const {
440   drmModeObjectPropertiesPtr props = nullptr;
441
442   props = drmModeObjectGetProperties(fd(), obj_id, obj_type);
443   if (!props) {
444     ALOGE("Failed to get properties for %d/%x", obj_id, obj_type);
445     return -ENODEV;
446   }
447
448   bool found = false;
449   for (int i = 0; !found && (size_t)i < props->count_props; ++i) {
450     drmModePropertyPtr p = drmModeGetProperty(fd(), props->props[i]);
451     if (!strcmp(p->name, prop_name)) {
452       property->Init(obj_id, p, props->prop_values[i]);
453       found = true;
454     }
455     drmModeFreeProperty(p);
456   }
457
458   drmModeFreeObjectProperties(props);
459   return found ? 0 : -ENOENT;
460 }
461
462 int DrmDevice::GetCrtcProperty(const DrmCrtc &crtc, const char *prop_name,
463                                DrmProperty *property) const {
464   return GetProperty(crtc.id(), DRM_MODE_OBJECT_CRTC, prop_name, property);
465 }
466
467 int DrmDevice::GetConnectorProperty(const DrmConnector &connector,
468                                     const char *prop_name,
469                                     DrmProperty *property) const {
470   return GetProperty(connector.id(), DRM_MODE_OBJECT_CONNECTOR, prop_name,
471                      property);
472 }
473
474 std::string DrmDevice::GetName() const {
475   auto *ver = drmGetVersion(fd());
476   if (!ver) {
477     ALOGW("Failed to get drm version for fd=%d", fd());
478     return "generic";
479   }
480
481   std::string name(ver->name);
482   drmFreeVersion(ver);
483   return name;
484 }
485
486 auto DrmDevice::IsKMSDev(const char *path) -> bool {
487   auto fd = UniqueFd(open(path, O_RDWR | O_CLOEXEC));
488   if (!fd) {
489     return false;
490   }
491
492   auto res = MakeDrmModeResUnique(fd.Get());
493   if (!res) {
494     return false;
495   }
496
497   bool is_kms = res->count_crtcs > 0 && res->count_connectors > 0 &&
498                 res->count_encoders > 0;
499
500   return is_kms;
501 }
502
503 }  // namespace android