• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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