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 <xf86drm.h>
22 #include <xf86drmMode.h>
23
24 #include <cinttypes>
25 #include <cstdint>
26 #include <string>
27
28 #include "drm/DrmAtomicStateManager.h"
29 #include "drm/DrmPlane.h"
30 #include "drm/ResourceManager.h"
31 #include "utils/log.h"
32 #include "utils/properties.h"
33
34 namespace android {
35
CreateInstance(std::string const & path,ResourceManager * res_man)36 auto DrmDevice::CreateInstance(std::string const &path,
37 ResourceManager *res_man)
38 -> std::unique_ptr<DrmDevice> {
39 if (!IsKMSDev(path.c_str())) {
40 return {};
41 }
42
43 auto device = std::unique_ptr<DrmDevice>(new DrmDevice(res_man));
44
45 if (device->Init(path.c_str()) != 0) {
46 return {};
47 }
48
49 return device;
50 }
51
DrmDevice(ResourceManager * res_man)52 DrmDevice::DrmDevice(ResourceManager *res_man) : res_man_(res_man) {
53 drm_fb_importer_ = std::make_unique<DrmFbImporter>(*this);
54 }
55
Init(const char * path)56 auto DrmDevice::Init(const char *path) -> int {
57 /* TODO: Use drmOpenControl here instead */
58 fd_ = UniqueFd(open(path, O_RDWR | O_CLOEXEC));
59 if (!fd_) {
60 // NOLINTNEXTLINE(concurrency-mt-unsafe): Fixme
61 ALOGE("Failed to open dri %s: %s", path, strerror(errno));
62 return -ENODEV;
63 }
64
65 int ret = drmSetClientCap(GetFd(), DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
66 if (ret != 0) {
67 ALOGE("Failed to set universal plane cap %d", ret);
68 return ret;
69 }
70
71 ret = drmSetClientCap(GetFd(), DRM_CLIENT_CAP_ATOMIC, 1);
72 if (ret != 0) {
73 ALOGE("Failed to set atomic cap %d", ret);
74 return ret;
75 }
76
77 #ifdef DRM_CLIENT_CAP_WRITEBACK_CONNECTORS
78 ret = drmSetClientCap(GetFd(), DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1);
79 if (ret != 0) {
80 ALOGI("Failed to set writeback cap %d", ret);
81 }
82 #endif
83
84 uint64_t cap_value = 0;
85 if (drmGetCap(GetFd(), DRM_CAP_ADDFB2_MODIFIERS, &cap_value) != 0) {
86 ALOGW("drmGetCap failed. Fallback to no modifier support.");
87 cap_value = 0;
88 }
89 HasAddFb2ModifiersSupport_ = cap_value != 0;
90
91 drmSetMaster(GetFd());
92 if (drmIsMaster(GetFd()) == 0) {
93 ALOGE("DRM/KMS master access required");
94 return -EACCES;
95 }
96
97 auto res = MakeDrmModeResUnique(GetFd());
98 if (!res) {
99 ALOGE("Failed to get DrmDevice resources");
100 return -ENODEV;
101 }
102
103 min_resolution_ = std::pair<uint32_t, uint32_t>(res->min_width,
104 res->min_height);
105 max_resolution_ = std::pair<uint32_t, uint32_t>(res->max_width,
106 res->max_height);
107
108 for (int i = 0; i < res->count_crtcs; ++i) {
109 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
110 auto crtc = DrmCrtc::CreateInstance(*this, res->crtcs[i], i);
111 if (crtc) {
112 crtcs_.emplace_back(std::move(crtc));
113 }
114 }
115
116 for (int i = 0; i < res->count_encoders; ++i) {
117 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
118 auto enc = DrmEncoder::CreateInstance(*this, res->encoders[i], i);
119 if (enc) {
120 encoders_.emplace_back(std::move(enc));
121 }
122 }
123
124 for (int i = 0; i < res->count_connectors; ++i) {
125 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
126 auto conn = DrmConnector::CreateInstance(*this, res->connectors[i], i);
127
128 if (!conn) {
129 continue;
130 }
131
132 if (conn->IsWriteback()) {
133 writeback_connectors_.emplace_back(std::move(conn));
134 } else {
135 connectors_.emplace_back(std::move(conn));
136 }
137 }
138
139 auto plane_res = MakeDrmModePlaneResUnique(GetFd());
140 if (!plane_res) {
141 ALOGE("Failed to get plane resources");
142 return -ENOENT;
143 }
144
145 for (uint32_t i = 0; i < plane_res->count_planes; ++i) {
146 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
147 auto plane = DrmPlane::CreateInstance(*this, plane_res->planes[i]);
148
149 if (plane) {
150 planes_.emplace_back(std::move(plane));
151 }
152 }
153
154 return 0;
155 }
156
RegisterUserPropertyBlob(void * data,size_t length) const157 auto DrmDevice::RegisterUserPropertyBlob(void *data, size_t length) const
158 -> DrmModeUserPropertyBlobUnique {
159 struct drm_mode_create_blob create_blob {};
160 create_blob.length = length;
161 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
162 create_blob.data = (__u64)data;
163
164 int ret = drmIoctl(GetFd(), DRM_IOCTL_MODE_CREATEPROPBLOB, &create_blob);
165 if (ret != 0) {
166 ALOGE("Failed to create mode property blob %d", ret);
167 return {};
168 }
169
170 return DrmModeUserPropertyBlobUnique(
171 new uint32_t(create_blob.blob_id), [this](const uint32_t *it) {
172 struct drm_mode_destroy_blob destroy_blob {};
173 destroy_blob.blob_id = (__u32)*it;
174 int err = drmIoctl(GetFd(), DRM_IOCTL_MODE_DESTROYPROPBLOB,
175 &destroy_blob);
176 if (err != 0) {
177 ALOGE("Failed to destroy mode property blob %" PRIu32 "/%d", *it,
178 err);
179 }
180 // NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
181 delete it;
182 });
183 }
184
GetProperty(uint32_t obj_id,uint32_t obj_type,const char * prop_name,DrmProperty * property) const185 int DrmDevice::GetProperty(uint32_t obj_id, uint32_t obj_type,
186 const char *prop_name, DrmProperty *property) const {
187 drmModeObjectPropertiesPtr props = nullptr;
188
189 props = drmModeObjectGetProperties(GetFd(), obj_id, obj_type);
190 if (props == nullptr) {
191 ALOGE("Failed to get properties for %d/%x", obj_id, obj_type);
192 return -ENODEV;
193 }
194
195 bool found = false;
196 for (int i = 0; !found && (size_t)i < props->count_props; ++i) {
197 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
198 drmModePropertyPtr p = drmModeGetProperty(GetFd(), props->props[i]);
199 if (strcmp(p->name, prop_name) == 0) {
200 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
201 property->Init(obj_id, p, props->prop_values[i]);
202 found = true;
203 }
204 drmModeFreeProperty(p);
205 }
206
207 drmModeFreeObjectProperties(props);
208 return found ? 0 : -ENOENT;
209 }
210
GetName() const211 std::string DrmDevice::GetName() const {
212 auto *ver = drmGetVersion(GetFd());
213 if (ver == nullptr) {
214 ALOGW("Failed to get drm version for fd=%d", GetFd());
215 return "generic";
216 }
217
218 std::string name(ver->name);
219 drmFreeVersion(ver);
220 return name;
221 }
222
IsKMSDev(const char * path)223 auto DrmDevice::IsKMSDev(const char *path) -> bool {
224 auto fd = UniqueFd(open(path, O_RDWR | O_CLOEXEC));
225 if (!fd) {
226 return false;
227 }
228
229 auto res = MakeDrmModeResUnique(fd.Get());
230 if (!res) {
231 return false;
232 }
233
234 bool is_kms = res->count_crtcs > 0 && res->count_connectors > 0 &&
235 res->count_encoders > 0;
236
237 return is_kms;
238 }
239
GetConnectors()240 auto DrmDevice::GetConnectors()
241 -> const std::vector<std::unique_ptr<DrmConnector>> & {
242 return connectors_;
243 }
244
GetPlanes()245 auto DrmDevice::GetPlanes() -> const std::vector<std::unique_ptr<DrmPlane>> & {
246 return planes_;
247 }
248
GetCrtcs()249 auto DrmDevice::GetCrtcs() -> const std::vector<std::unique_ptr<DrmCrtc>> & {
250 return crtcs_;
251 }
252
GetEncoders()253 auto DrmDevice::GetEncoders()
254 -> const std::vector<std::unique_ptr<DrmEncoder>> & {
255 return encoders_;
256 }
257
258 } // namespace android
259