OSDN Git Service

drm_hwcomposer: Tidy-up DrmConnector 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 namespace android {
38
39 DrmDevice::DrmDevice() {
40   self.reset(this);
41   mDrmFbImporter = std::make_unique<DrmFbImporter>(self);
42 }
43
44 // NOLINTNEXTLINE (readability-function-cognitive-complexity): Fixme
45 std::tuple<int, int> DrmDevice::Init(const char *path, int num_displays) {
46   /* TODO: Use drmOpenControl here instead */
47   fd_ = UniqueFd(open(path, O_RDWR | O_CLOEXEC));
48   if (fd() < 0) {
49     // NOLINTNEXTLINE(concurrency-mt-unsafe): Fixme
50     ALOGE("Failed to open dri %s: %s", path, strerror(errno));
51     return std::make_tuple(-ENODEV, 0);
52   }
53
54   int ret = drmSetClientCap(fd(), DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
55   if (ret) {
56     ALOGE("Failed to set universal plane cap %d", ret);
57     return std::make_tuple(ret, 0);
58   }
59
60   ret = drmSetClientCap(fd(), DRM_CLIENT_CAP_ATOMIC, 1);
61   if (ret) {
62     ALOGE("Failed to set atomic cap %d", ret);
63     return std::make_tuple(ret, 0);
64   }
65
66 #ifdef DRM_CLIENT_CAP_WRITEBACK_CONNECTORS
67   ret = drmSetClientCap(fd(), DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1);
68   if (ret) {
69     ALOGI("Failed to set writeback cap %d", ret);
70     ret = 0;
71   }
72 #endif
73
74   uint64_t cap_value = 0;
75   if (drmGetCap(fd(), DRM_CAP_ADDFB2_MODIFIERS, &cap_value)) {
76     ALOGW("drmGetCap failed. Fallback to no modifier support.");
77     cap_value = 0;
78   }
79   HasAddFb2ModifiersSupport_ = cap_value != 0;
80
81   drmSetMaster(fd());
82   if (!drmIsMaster(fd())) {
83     ALOGE("DRM/KMS master access required");
84     return std::make_tuple(-EACCES, 0);
85   }
86
87   auto res = MakeDrmModeResUnique(fd());
88   if (!res) {
89     ALOGE("Failed to get DrmDevice resources");
90     return std::make_tuple(-ENODEV, 0);
91   }
92
93   min_resolution_ = std::pair<uint32_t, uint32_t>(res->min_width,
94                                                   res->min_height);
95   max_resolution_ = std::pair<uint32_t, uint32_t>(res->max_width,
96                                                   res->max_height);
97
98   for (int i = 0; i < res->count_crtcs; ++i) {
99     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
100     auto crtc = DrmCrtc::CreateInstance(*this, res->crtcs[i], i);
101     if (crtc) {
102       crtcs_.emplace_back(std::move(crtc));
103     }
104   }
105
106   for (int i = 0; i < res->count_encoders; ++i) {
107     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
108     auto enc = DrmEncoder::CreateInstance(*this, res->encoders[i], i);
109     if (enc) {
110       encoders_.emplace_back(std::move(enc));
111     }
112   }
113
114   for (int i = 0; i < res->count_connectors; ++i) {
115     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
116     auto conn = DrmConnector::CreateInstance(*this, res->connectors[i], i);
117
118     if (!conn) {
119       continue;
120     }
121
122     if (conn->IsWriteback()) {
123       writeback_connectors_.emplace_back(std::move(conn));
124     } else {
125       connectors_.emplace_back(std::move(conn));
126     }
127   }
128
129   auto add_displays = [this, &num_displays](bool internal, bool connected) {
130     for (auto &conn : connectors_) {
131       bool is_connected = conn->IsConnected();
132       if ((internal ? conn->IsInternal() : conn->IsExternal()) &&
133           (connected ? is_connected : !is_connected)) {
134         bound_connectors_[num_displays] = conn.get();
135         connectors_to_display_id_[conn.get()] = num_displays;
136         ++num_displays;
137       }
138     }
139   };
140
141   /* Put internal first to ensure Primary display will be internal
142    * in case at least 1 internal is available
143    */
144   add_displays(/*internal = */ true, /*connected = */ true);
145   add_displays(/*internal = */ false, /*connected = */ true);
146   add_displays(/*internal = */ true, /*connected = */ false);
147   add_displays(/*internal = */ false, /*connected = */ false);
148
149   // Catch-all for the above loops
150   if (ret)
151     return std::make_tuple(ret, 0);
152
153   auto plane_res = MakeDrmModePlaneResUnique(fd());
154   if (!plane_res) {
155     ALOGE("Failed to get plane resources");
156     return std::make_tuple(-ENOENT, 0);
157   }
158
159   for (uint32_t i = 0; i < plane_res->count_planes; ++i) {
160     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
161     auto plane = DrmPlane::CreateInstance(*this, plane_res->planes[i]);
162
163     if (plane) {
164       planes_.emplace_back(std::move(plane));
165     }
166   }
167
168   for (auto &conn : connectors_) {
169     ret = CreateDisplayPipe(conn.get());
170     if (ret) {
171       ALOGE("Failed CreateDisplayPipe %d with %d", conn->GetId(), ret);
172       return std::make_tuple(ret, 0);
173     }
174   }
175   return std::make_tuple(ret, bound_connectors_.size());
176 }
177
178 bool DrmDevice::HandlesDisplay(int display) const {
179   return bound_connectors_.count(display) != 0;
180 }
181
182 DrmConnector *DrmDevice::GetConnectorForDisplay(int display) const {
183   return bound_connectors_.at(display);
184 }
185
186 DrmCrtc *DrmDevice::GetCrtcForDisplay(int display) const {
187   return bound_crtcs_.at(display);
188 }
189
190 const std::vector<std::unique_ptr<DrmCrtc>> &DrmDevice::crtcs() const {
191   return crtcs_;
192 }
193
194 uint32_t DrmDevice::next_mode_id() {
195   return ++mode_id_;
196 }
197
198 int DrmDevice::TryEncoderForDisplay(int display, DrmEncoder *enc) {
199   /* First try to use the currently-bound crtc */
200   auto *crtc = FindCrtcById(enc->GetCurrentCrtcId());
201   if (crtc && bound_crtcs_.count(display) == 0) {
202     bound_crtcs_[display] = crtc;
203     bound_encoders_[crtc] = enc;
204     return 0;
205   }
206
207   /* Try to find a possible crtc which will work */
208   for (auto &crtc : crtcs_) {
209     /* Crtc not supported or we've already tried this earlier */
210     if (!enc->SupportsCrtc(*crtc) || crtc->GetId() == enc->GetCurrentCrtcId()) {
211       continue;
212     }
213
214     if (bound_crtcs_.count(display) == 0) {
215       bound_crtcs_[display] = crtc.get();
216       bound_encoders_[crtc.get()] = enc;
217       return 0;
218     }
219   }
220
221   /* We can't use the encoder, but nothing went wrong, try another one */
222   return -EAGAIN;
223 }
224
225 int DrmDevice::CreateDisplayPipe(DrmConnector *connector) {
226   int display = connectors_to_display_id_.at(connector);
227   /* Try to use current setup first */
228   auto *enc0 = FindEncoderById(connector->GetCurrentEncoderId());
229   if (enc0 != nullptr && encoders_to_display_id_.count(enc0) == 0) {
230     int ret = TryEncoderForDisplay(display, enc0);
231     if (!ret) {
232       encoders_to_display_id_[enc0] = display;
233       return 0;
234     }
235
236     if (ret != -EAGAIN) {
237       ALOGE("Could not set mode %d/%d", display, ret);
238       return ret;
239     }
240   }
241
242   for (auto &enc : encoders_) {
243     if (!connector->SupportsEncoder(*enc) ||
244         encoders_to_display_id_.count(enc.get()) != 0) {
245       continue;
246     }
247
248     int ret = TryEncoderForDisplay(display, enc.get());
249     if (!ret) {
250       encoders_to_display_id_[enc.get()] = display;
251       return 0;
252     }
253
254     if (ret != -EAGAIN) {
255       ALOGE("Could not set mode %d/%d", display, ret);
256       return ret;
257     }
258   }
259   ALOGE("Could not find a suitable encoder/crtc for display %d", display);
260   return -ENODEV;
261 }
262
263 auto DrmDevice::RegisterUserPropertyBlob(void *data, size_t length) const
264     -> DrmModeUserPropertyBlobUnique {
265   struct drm_mode_create_blob create_blob {};
266   create_blob.length = length;
267   create_blob.data = (__u64)data;
268
269   int ret = drmIoctl(fd(), DRM_IOCTL_MODE_CREATEPROPBLOB, &create_blob);
270   if (ret) {
271     ALOGE("Failed to create mode property blob %d", ret);
272     return {};
273   }
274
275   return DrmModeUserPropertyBlobUnique(
276       new uint32_t(create_blob.blob_id), [this](const uint32_t *it) {
277         struct drm_mode_destroy_blob destroy_blob {};
278         destroy_blob.blob_id = (__u32)*it;
279         int err = drmIoctl(fd(), DRM_IOCTL_MODE_DESTROYPROPBLOB, &destroy_blob);
280         if (err != 0) {
281           ALOGE("Failed to destroy mode property blob %" PRIu32 "/%d", *it,
282                 err);
283         }
284         // NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
285         delete it;
286       });
287 }
288
289 int DrmDevice::GetProperty(uint32_t obj_id, uint32_t obj_type,
290                            const char *prop_name, DrmProperty *property) const {
291   drmModeObjectPropertiesPtr props = nullptr;
292
293   props = drmModeObjectGetProperties(fd(), obj_id, obj_type);
294   if (!props) {
295     ALOGE("Failed to get properties for %d/%x", obj_id, obj_type);
296     return -ENODEV;
297   }
298
299   bool found = false;
300   for (int i = 0; !found && (size_t)i < props->count_props; ++i) {
301     drmModePropertyPtr p = drmModeGetProperty(fd(), props->props[i]);
302     if (!strcmp(p->name, prop_name)) {
303       property->Init(obj_id, p, props->prop_values[i]);
304       found = true;
305     }
306     drmModeFreeProperty(p);
307   }
308
309   drmModeFreeObjectProperties(props);
310   return found ? 0 : -ENOENT;
311 }
312
313 std::string DrmDevice::GetName() const {
314   auto *ver = drmGetVersion(fd());
315   if (!ver) {
316     ALOGW("Failed to get drm version for fd=%d", fd());
317     return "generic";
318   }
319
320   std::string name(ver->name);
321   drmFreeVersion(ver);
322   return name;
323 }
324
325 auto DrmDevice::IsKMSDev(const char *path) -> bool {
326   auto fd = UniqueFd(open(path, O_RDWR | O_CLOEXEC));
327   if (!fd) {
328     return false;
329   }
330
331   auto res = MakeDrmModeResUnique(fd.Get());
332   if (!res) {
333     return false;
334   }
335
336   bool is_kms = res->count_crtcs > 0 && res->count_connectors > 0 &&
337                 res->count_encoders > 0;
338
339   return is_kms;
340 }
341
342 }  // namespace android