2 * Copyright (C) 2015 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #define LOG_TAG "hwc-drm-device"
19 #include "DrmDevice.h"
23 #include <xf86drmMode.h>
33 #include "drm/DrmPlane.h"
34 #include "utils/log.h"
35 #include "utils/properties.h"
39 DrmDevice::DrmDevice() {
41 mDrmFbImporter = std::make_unique<DrmFbImporter>(self);
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));
49 // NOLINTNEXTLINE(concurrency-mt-unsafe): Fixme
50 ALOGE("Failed to open dri %s: %s", path, strerror(errno));
51 return std::make_tuple(-ENODEV, 0);
54 int ret = drmSetClientCap(fd(), DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
56 ALOGE("Failed to set universal plane cap %d", ret);
57 return std::make_tuple(ret, 0);
60 ret = drmSetClientCap(fd(), DRM_CLIENT_CAP_ATOMIC, 1);
62 ALOGE("Failed to set atomic cap %d", ret);
63 return std::make_tuple(ret, 0);
66 #ifdef DRM_CLIENT_CAP_WRITEBACK_CONNECTORS
67 ret = drmSetClientCap(fd(), DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1);
69 ALOGI("Failed to set writeback cap %d", ret);
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.");
79 HasAddFb2ModifiersSupport_ = cap_value != 0;
82 if (!drmIsMaster(fd())) {
83 ALOGE("DRM/KMS master access required");
84 return std::make_tuple(-EACCES, 0);
87 auto res = MakeDrmModeResUnique(fd());
89 ALOGE("Failed to get DrmDevice resources");
90 return std::make_tuple(-ENODEV, 0);
93 min_resolution_ = std::pair<uint32_t, uint32_t>(res->min_width,
95 max_resolution_ = std::pair<uint32_t, uint32_t>(res->max_width,
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);
102 crtcs_.emplace_back(std::move(crtc));
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);
110 encoders_.emplace_back(std::move(enc));
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);
122 if (conn->IsWriteback()) {
123 writeback_connectors_.emplace_back(std::move(conn));
125 connectors_.emplace_back(std::move(conn));
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;
141 /* Put internal first to ensure Primary display will be internal
142 * in case at least 1 internal is available
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);
149 // Catch-all for the above loops
151 return std::make_tuple(ret, 0);
153 auto plane_res = MakeDrmModePlaneResUnique(fd());
155 ALOGE("Failed to get plane resources");
156 return std::make_tuple(-ENOENT, 0);
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]);
164 planes_.emplace_back(std::move(plane));
168 for (auto &conn : connectors_) {
169 ret = CreateDisplayPipe(conn.get());
171 ALOGE("Failed CreateDisplayPipe %d with %d", conn->GetId(), ret);
172 return std::make_tuple(ret, 0);
175 return std::make_tuple(ret, bound_connectors_.size());
178 bool DrmDevice::HandlesDisplay(int display) const {
179 return bound_connectors_.count(display) != 0;
182 DrmConnector *DrmDevice::GetConnectorForDisplay(int display) const {
183 return bound_connectors_.at(display);
186 DrmCrtc *DrmDevice::GetCrtcForDisplay(int display) const {
187 return bound_crtcs_.at(display);
190 const std::vector<std::unique_ptr<DrmCrtc>> &DrmDevice::crtcs() const {
194 uint32_t DrmDevice::next_mode_id() {
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;
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()) {
214 if (bound_crtcs_.count(display) == 0) {
215 bound_crtcs_[display] = crtc.get();
216 bound_encoders_[crtc.get()] = enc;
221 /* We can't use the encoder, but nothing went wrong, try another one */
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);
232 encoders_to_display_id_[enc0] = display;
236 if (ret != -EAGAIN) {
237 ALOGE("Could not set mode %d/%d", display, ret);
242 for (auto &enc : encoders_) {
243 if (!connector->SupportsEncoder(*enc) ||
244 encoders_to_display_id_.count(enc.get()) != 0) {
248 int ret = TryEncoderForDisplay(display, enc.get());
250 encoders_to_display_id_[enc.get()] = display;
254 if (ret != -EAGAIN) {
255 ALOGE("Could not set mode %d/%d", display, ret);
259 ALOGE("Could not find a suitable encoder/crtc for display %d", display);
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;
269 int ret = drmIoctl(fd(), DRM_IOCTL_MODE_CREATEPROPBLOB, &create_blob);
271 ALOGE("Failed to create mode property blob %d", ret);
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);
281 ALOGE("Failed to destroy mode property blob %" PRIu32 "/%d", *it,
284 // NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
289 int DrmDevice::GetProperty(uint32_t obj_id, uint32_t obj_type,
290 const char *prop_name, DrmProperty *property) const {
291 drmModeObjectPropertiesPtr props = nullptr;
293 props = drmModeObjectGetProperties(fd(), obj_id, obj_type);
295 ALOGE("Failed to get properties for %d/%x", obj_id, obj_type);
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]);
306 drmModeFreeProperty(p);
309 drmModeFreeObjectProperties(props);
310 return found ? 0 : -ENOENT;
313 std::string DrmDevice::GetName() const {
314 auto *ver = drmGetVersion(fd());
316 ALOGW("Failed to get drm version for fd=%d", fd());
320 std::string name(ver->name);
325 auto DrmDevice::IsKMSDev(const char *path) -> bool {
326 auto fd = UniqueFd(open(path, O_RDWR | O_CLOEXEC));
331 auto res = MakeDrmModeResUnique(fd.Get());
336 bool is_kms = res->count_crtcs > 0 && res->count_connectors > 0 &&
337 res->count_encoders > 0;
342 } // namespace android