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 "drmhwc"
18
19 #include "DrmDevice.h"
20
21 #include <sys/mman.h>
22 #include <xf86drm.h>
23 #include <xf86drmMode.h>
24
25 #include <cinttypes>
26 #include <cstdint>
27 #include <string>
28
29 #include "drm/DrmAtomicStateManager.h"
30 #include "drm/DrmPlane.h"
31 #include "drm/ResourceManager.h"
32 #include "utils/log.h"
33 #include "utils/properties.h"
34
35 namespace android {
36
CreateInstance(std::string const & path,ResourceManager * res_man,uint32_t index)37 auto DrmDevice::CreateInstance(std::string const &path,
38 ResourceManager *res_man, uint32_t index)
39 -> std::unique_ptr<DrmDevice> {
40 if (!IsKMSDev(path.c_str())) {
41 return {};
42 }
43
44 auto device = std::unique_ptr<DrmDevice>(new DrmDevice(res_man, index));
45
46 if (device->Init(path.c_str()) != 0) {
47 return {};
48 }
49
50 return device;
51 }
52
DrmDevice(ResourceManager * res_man,uint32_t index)53 DrmDevice::DrmDevice(ResourceManager *res_man, uint32_t index)
54 : index_in_dev_array_(index), res_man_(res_man) {
55 drm_fb_importer_ = std::make_unique<DrmFbImporter>(*this);
56 }
57
Init(const char * path)58 auto DrmDevice::Init(const char *path) -> int {
59 /* TODO: Use drmOpenControl here instead */
60 fd_ = MakeSharedFd(open(path, O_RDWR | O_CLOEXEC));
61 if (!fd_) {
62 // NOLINTNEXTLINE(concurrency-mt-unsafe): Fixme
63 ALOGE("Failed to open dri %s: %s", path, strerror(errno));
64 return -ENODEV;
65 }
66
67 int ret = drmSetClientCap(*GetFd(), DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
68 if (ret != 0) {
69 ALOGE("Failed to set universal plane cap %d", ret);
70 return ret;
71 }
72
73 ret = drmSetClientCap(*GetFd(), DRM_CLIENT_CAP_ATOMIC, 1);
74 if (ret != 0) {
75 ALOGE("Failed to set atomic cap %d", ret);
76 return ret;
77 }
78
79 #ifdef DRM_CLIENT_CAP_WRITEBACK_CONNECTORS
80 ret = drmSetClientCap(*GetFd(), DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1);
81 if (ret != 0) {
82 ALOGI("Failed to set writeback cap %d", ret);
83 }
84 #endif
85
86 uint64_t cap_value = 0;
87 if (drmGetCap(*GetFd(), DRM_CAP_ADDFB2_MODIFIERS, &cap_value) != 0) {
88 ALOGW("drmGetCap failed. Fallback to no modifier support.");
89 cap_value = 0;
90 }
91 HasAddFb2ModifiersSupport_ = cap_value != 0;
92
93 uint64_t cursor_width = 0;
94 uint64_t cursor_height = 0;
95 if (drmGetCap(*GetFd(), DRM_CAP_CURSOR_WIDTH, &cursor_width) == 0 &&
96 drmGetCap(*GetFd(), DRM_CAP_CURSOR_HEIGHT, &cursor_height) == 0) {
97 cap_cursor_size_ = std::pair<uint64_t, uint64_t>(cursor_width,
98 cursor_height);
99 }
100
101 drmSetMaster(*GetFd());
102 if (drmIsMaster(*GetFd()) == 0) {
103 ALOGE("DRM/KMS master access required");
104 return -EACCES;
105 }
106
107 auto res = MakeDrmModeResUnique(*GetFd());
108 if (!res) {
109 ALOGE("Failed to get DrmDevice resources");
110 return -ENODEV;
111 }
112
113 min_resolution_ = std::pair<uint32_t, uint32_t>(res->min_width,
114 res->min_height);
115 max_resolution_ = std::pair<uint32_t, uint32_t>(res->max_width,
116 res->max_height);
117
118 for (int i = 0; i < res->count_crtcs; ++i) {
119 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
120 auto crtc = DrmCrtc::CreateInstance(*this, res->crtcs[i], i);
121 if (crtc) {
122 crtcs_.emplace_back(std::move(crtc));
123 }
124 }
125
126 for (int i = 0; i < res->count_encoders; ++i) {
127 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
128 auto enc = DrmEncoder::CreateInstance(*this, res->encoders[i], i);
129 if (enc) {
130 encoders_.emplace_back(std::move(enc));
131 }
132 }
133
134 for (int i = 0; i < res->count_connectors; ++i) {
135 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
136 auto conn = DrmConnector::CreateInstance(*this, res->connectors[i], i);
137
138 if (!conn) {
139 continue;
140 }
141
142 if (conn->IsWriteback()) {
143 writeback_connectors_.emplace_back(std::move(conn));
144 } else {
145 connectors_.emplace_back(std::move(conn));
146 }
147 }
148
149 auto plane_res = MakeDrmModePlaneResUnique(*GetFd());
150 if (!plane_res) {
151 ALOGE("Failed to get plane resources");
152 return -ENOENT;
153 }
154
155 for (uint32_t i = 0; i < plane_res->count_planes; ++i) {
156 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
157 auto plane = DrmPlane::CreateInstance(*this, plane_res->planes[i]);
158
159 if (plane) {
160 planes_.emplace_back(std::move(plane));
161 }
162 }
163
164 return 0;
165 }
166
RegisterUserPropertyBlob(void * data,size_t length) const167 auto DrmDevice::RegisterUserPropertyBlob(void *data, size_t length) const
168 -> DrmModeUserPropertyBlobUnique {
169 struct drm_mode_create_blob create_blob {};
170 create_blob.length = length;
171 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
172 create_blob.data = (__u64)data;
173
174 auto ret = drmIoctl(*GetFd(), DRM_IOCTL_MODE_CREATEPROPBLOB, &create_blob);
175 if (ret != 0) {
176 ALOGE("Failed to create mode property blob %d", ret);
177 return {};
178 }
179
180 return DrmModeUserPropertyBlobUnique(
181 new uint32_t(create_blob.blob_id), [this](const uint32_t *it) {
182 struct drm_mode_destroy_blob destroy_blob {};
183 destroy_blob.blob_id = (__u32)*it;
184 auto err = drmIoctl(*GetFd(), DRM_IOCTL_MODE_DESTROYPROPBLOB,
185 &destroy_blob);
186 if (err != 0) {
187 ALOGE("Failed to destroy mode property blob %" PRIu32 "/%d", *it,
188 err);
189 }
190 // NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
191 delete it;
192 });
193 }
194
GetProperty(uint32_t obj_id,uint32_t obj_type,const char * prop_name,DrmProperty * property) const195 int DrmDevice::GetProperty(uint32_t obj_id, uint32_t obj_type,
196 const char *prop_name, DrmProperty *property) const {
197 drmModeObjectPropertiesPtr props = nullptr;
198
199 props = drmModeObjectGetProperties(*GetFd(), obj_id, obj_type);
200 if (props == nullptr) {
201 ALOGE("Failed to get properties for %d/%x", obj_id, obj_type);
202 return -ENODEV;
203 }
204
205 bool found = false;
206 for (int i = 0; !found && (size_t)i < props->count_props; ++i) {
207 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
208 drmModePropertyPtr p = drmModeGetProperty(*GetFd(), props->props[i]);
209 if (strcmp(p->name, prop_name) == 0) {
210 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
211 property->Init(GetFd(), obj_id, p, props->prop_values[i]);
212 found = true;
213 }
214 drmModeFreeProperty(p);
215 }
216
217 drmModeFreeObjectProperties(props);
218 return found ? 0 : -ENOENT;
219 }
220
GetName() const221 std::string DrmDevice::GetName() const {
222 auto *ver = drmGetVersion(*GetFd());
223 if (ver == nullptr) {
224 ALOGW("Failed to get drm version for fd=%d", *GetFd());
225 return "generic";
226 }
227
228 std::string name(ver->name);
229 drmFreeVersion(ver);
230 return name;
231 }
232
IsKMSDev(const char * path)233 auto DrmDevice::IsKMSDev(const char *path) -> bool {
234 auto fd = MakeUniqueFd(open(path, O_RDWR | O_CLOEXEC));
235 if (!fd) {
236 return false;
237 }
238
239 auto res = MakeDrmModeResUnique(*fd);
240 if (!res) {
241 return false;
242 }
243
244 auto is_kms = res->count_crtcs > 0 && res->count_connectors > 0 &&
245 res->count_encoders > 0;
246
247 return is_kms;
248 }
249
GetConnectors()250 auto DrmDevice::GetConnectors()
251 -> const std::vector<std::unique_ptr<DrmConnector>> & {
252 return connectors_;
253 }
254
GetWritebackConnectors()255 auto DrmDevice::GetWritebackConnectors()
256 -> const std::vector<std::unique_ptr<DrmConnector>> & {
257 return writeback_connectors_;
258 }
259
GetPlanes()260 auto DrmDevice::GetPlanes() -> const std::vector<std::unique_ptr<DrmPlane>> & {
261 return planes_;
262 }
263
GetCrtcs()264 auto DrmDevice::GetCrtcs() -> const std::vector<std::unique_ptr<DrmCrtc>> & {
265 return crtcs_;
266 }
267
GetEncoders()268 auto DrmDevice::GetEncoders()
269 -> const std::vector<std::unique_ptr<DrmEncoder>> & {
270 return encoders_;
271 }
272
273 class DumbBufferFd : public PrimeFdsSharedBase {
274 public:
275 SharedFd fd;
276 };
277
278 // NOLINTBEGIN(cppcoreguidelines-avoid-goto)
CreateBufferForModeset(uint32_t width,uint32_t height)279 auto DrmDevice::CreateBufferForModeset(uint32_t width, uint32_t height)
280 -> std::optional<BufferInfo> {
281 constexpr uint32_t kDumbBufferFormat = DRM_FORMAT_XRGB8888;
282 constexpr uint32_t kDumbBufferBpp = 32;
283
284 std::optional<BufferInfo> result;
285 void *ptr = MAP_FAILED;
286 struct drm_mode_create_dumb create = {
287 .height = height,
288 .width = width,
289 .bpp = kDumbBufferBpp,
290 .flags = 0,
291 };
292
293 int ret = drmIoctl(*fd_, DRM_IOCTL_MODE_CREATE_DUMB, &create);
294 if (ret != 0) {
295 ALOGE("Failed to DRM_IOCTL_MODE_CREATE_DUMB %d", errno);
296 return {};
297 }
298
299 struct drm_mode_map_dumb map = {
300 .handle = create.handle,
301 };
302
303 auto dumb_buffer_fd = std::make_shared<DumbBufferFd>();
304
305 BufferInfo buffer_info = {
306 .width = width,
307 .height = height,
308
309 .format = kDumbBufferFormat,
310 .pitches = {create.pitch},
311 .prime_fds = {-1, -1, -1, -1},
312 .modifiers = {DRM_FORMAT_MOD_NONE},
313
314 .color_space = BufferColorSpace::kUndefined,
315 .sample_range = BufferSampleRange::kUndefined,
316 .blend_mode = BufferBlendMode::kNone,
317
318 .fds_shared = dumb_buffer_fd,
319 };
320
321 ret = drmIoctl(*fd_, DRM_IOCTL_MODE_MAP_DUMB, &map);
322 if (ret != 0) {
323 ALOGE("Failed to DRM_IOCTL_MODE_MAP_DUMB %d", errno);
324 goto done;
325 }
326
327 ptr = mmap(nullptr, create.size, PROT_READ | PROT_WRITE, MAP_SHARED, *fd_,
328 (off_t)map.offset);
329 if (ptr == MAP_FAILED) {
330 ALOGE("Failed to mmap dumb buffer %d", errno);
331 goto done;
332 }
333
334 memset(ptr, 0, create.size);
335
336 if (munmap(ptr, create.size) != 0) {
337 ALOGE("Failed to unmap dumb buffer: %d", errno);
338 }
339
340 ret = drmPrimeHandleToFD(*fd_, create.handle, 0, &buffer_info.prime_fds[0]);
341 if (ret != 0) {
342 ALOGE("Failed to export dumb buffer as FD: %d", errno);
343 goto done;
344 }
345
346 dumb_buffer_fd->fd = MakeSharedFd(buffer_info.prime_fds[0]);
347
348 result = buffer_info;
349
350 done:
351 if (create.handle > 0) {
352 struct drm_mode_destroy_dumb destroy = {
353 .handle = create.handle,
354 };
355 drmIoctl(*fd_, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy);
356 }
357
358 return result;
359 }
360 // NOLINTEND(cppcoreguidelines-avoid-goto)
361
362 } // namespace android
363