1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ui/ozone/platform/drm/gpu/drm_device.h"
6
7 #include <fcntl.h>
8 #include <sys/mman.h>
9 #include <unistd.h>
10 #include <xf86drm.h>
11 #include <xf86drmMode.h>
12 #include <memory>
13 #include <utility>
14
15 #include "base/bind.h"
16 #include "base/logging.h"
17 #include "base/macros.h"
18 #include "base/memory/free_deleter.h"
19 #include "base/message_loop/message_loop_current.h"
20 #include "base/message_loop/message_pump_for_io.h"
21 #include "base/task_runner.h"
22 #include "base/threading/thread_task_runner_handle.h"
23 #include "base/trace_event/trace_event.h"
24 #include "base/trace_event/traced_value.h"
25 #include "third_party/skia/include/core/SkImageInfo.h"
26 #include "ui/display/types/gamma_ramp_rgb_entry.h"
27 #include "ui/ozone/platform/drm/common/drm_util.h"
28 #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h"
29 #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h"
30
31 namespace ui {
32
33 namespace {
34
35 using DrmEventHandler =
36 base::RepeatingCallback<void(uint32_t /* frame */,
37 base::TimeTicks /* timestamp */,
38 uint64_t /* id */)>;
39
DrmCreateDumbBuffer(int fd,const SkImageInfo & info,uint32_t * handle,uint32_t * stride)40 bool DrmCreateDumbBuffer(int fd,
41 const SkImageInfo& info,
42 uint32_t* handle,
43 uint32_t* stride) {
44 struct drm_mode_create_dumb request;
45 memset(&request, 0, sizeof(request));
46 request.width = info.width();
47 request.height = info.height();
48 request.bpp = info.bytesPerPixel() << 3;
49 request.flags = 0;
50
51 if (drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &request) < 0) {
52 VPLOG(2) << "Cannot create dumb buffer";
53 return false;
54 }
55
56 // The driver may choose to align the last row as well. We don't care about
57 // the last alignment bits since they aren't used for display purposes, so
58 // just check that the expected size is <= to what the driver allocated.
59 DCHECK_LE(info.computeByteSize(request.pitch), request.size);
60
61 *handle = request.handle;
62 *stride = request.pitch;
63 return true;
64 }
65
DrmDestroyDumbBuffer(int fd,uint32_t handle)66 bool DrmDestroyDumbBuffer(int fd, uint32_t handle) {
67 struct drm_mode_destroy_dumb destroy_request;
68 memset(&destroy_request, 0, sizeof(destroy_request));
69 destroy_request.handle = handle;
70 return !drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_request);
71 }
72
ProcessDrmEvent(int fd,const DrmEventHandler & callback)73 bool ProcessDrmEvent(int fd, const DrmEventHandler& callback) {
74 char buffer[1024];
75 int len = read(fd, buffer, sizeof(buffer));
76 if (len == 0)
77 return false;
78
79 if (len < static_cast<int>(sizeof(drm_event))) {
80 PLOG(ERROR) << "Failed to read DRM event";
81 return false;
82 }
83
84 int idx = 0;
85 while (idx < len) {
86 DCHECK_LE(static_cast<int>(sizeof(drm_event)), len - idx);
87 drm_event event;
88 memcpy(&event, &buffer[idx], sizeof(event));
89 switch (event.type) {
90 case DRM_EVENT_FLIP_COMPLETE: {
91 DCHECK_LE(static_cast<int>(sizeof(drm_event_vblank)), len - idx);
92 drm_event_vblank vblank;
93 memcpy(&vblank, &buffer[idx], sizeof(vblank));
94 std::unique_ptr<base::trace_event::TracedValue> drm_data(
95 new base::trace_event::TracedValue());
96 drm_data->SetInteger("frame_count", 1);
97 drm_data->SetInteger("vblank.tv_sec", vblank.tv_sec);
98 drm_data->SetInteger("vblank.tv_usec", vblank.tv_usec);
99 TRACE_EVENT_INSTANT1("benchmark,drm", "DrmEventFlipComplete",
100 TRACE_EVENT_SCOPE_THREAD, "data",
101 std::move(drm_data));
102 // Warning: It is generally unsafe to manufacture TimeTicks values; but
103 // here it is required for interfacing with libdrm. Assumption: libdrm
104 // is providing the timestamp from the CLOCK_MONOTONIC POSIX clock.
105 DCHECK_EQ(base::TimeTicks::GetClock(),
106 base::TimeTicks::Clock::LINUX_CLOCK_MONOTONIC);
107 const base::TimeTicks timestamp =
108 base::TimeTicks() + base::TimeDelta::FromMicroseconds(
109 static_cast<int64_t>(vblank.tv_sec) *
110 base::Time::kMicrosecondsPerSecond +
111 vblank.tv_usec);
112 callback.Run(vblank.sequence, timestamp, vblank.user_data);
113 } break;
114 case DRM_EVENT_VBLANK:
115 break;
116 default:
117 NOTREACHED();
118 break;
119 }
120
121 idx += event.length;
122 }
123
124 return true;
125 }
126
CanQueryForResources(int fd)127 bool CanQueryForResources(int fd) {
128 drm_mode_card_res resources;
129 memset(&resources, 0, sizeof(resources));
130 // If there is no error getting DRM resources then assume this is a
131 // modesetting device.
132 return !drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &resources);
133 }
134
135 } // namespace
136
DrmPropertyBlobMetadata(DrmDevice * drm,uint32_t id)137 DrmPropertyBlobMetadata::DrmPropertyBlobMetadata(DrmDevice* drm, uint32_t id)
138 : drm_(drm), id_(id) {}
139
~DrmPropertyBlobMetadata()140 DrmPropertyBlobMetadata::~DrmPropertyBlobMetadata() {
141 DCHECK(drm_);
142 DCHECK(id_);
143 drm_->DestroyPropertyBlob(id_);
144 }
145
146 class DrmDevice::PageFlipManager {
147 public:
PageFlipManager()148 PageFlipManager() : next_id_(0) {}
149 ~PageFlipManager() = default;
150
OnPageFlip(uint32_t frame,base::TimeTicks timestamp,uint64_t id)151 void OnPageFlip(uint32_t frame, base::TimeTicks timestamp, uint64_t id) {
152 auto it =
153 std::find_if(callbacks_.begin(), callbacks_.end(), FindCallback(id));
154 if (it == callbacks_.end()) {
155 LOG(WARNING) << "Could not find callback for page flip id=" << id;
156 return;
157 }
158
159 it->pending_calls--;
160 if (it->pending_calls)
161 return;
162
163 DrmDevice::PageFlipCallback callback = std::move(it->callback);
164 callbacks_.erase(it);
165 std::move(callback).Run(frame, timestamp);
166 }
167
GetNextId()168 uint64_t GetNextId() { return next_id_++; }
169
RegisterCallback(uint64_t id,uint64_t pending_calls,DrmDevice::PageFlipCallback callback)170 void RegisterCallback(uint64_t id,
171 uint64_t pending_calls,
172 DrmDevice::PageFlipCallback callback) {
173 callbacks_.push_back({id, pending_calls, std::move(callback)});
174 }
175
176 private:
177 struct PageFlip {
178 uint64_t id;
179 uint32_t pending_calls;
180 DrmDevice::PageFlipCallback callback;
181 };
182
183 struct FindCallback {
FindCallbackui::DrmDevice::PageFlipManager::FindCallback184 explicit FindCallback(uint64_t id) : id(id) {}
185
operator ()ui::DrmDevice::PageFlipManager::FindCallback186 bool operator()(const PageFlip& flip) const { return flip.id == id; }
187
188 const uint64_t id;
189 };
190
191 uint64_t next_id_;
192
193 std::vector<PageFlip> callbacks_;
194
195 DISALLOW_COPY_AND_ASSIGN(PageFlipManager);
196 };
197
198 class DrmDevice::IOWatcher : public base::MessagePumpLibevent::FdWatcher {
199 public:
IOWatcher(int fd,DrmDevice::PageFlipManager * page_flip_manager)200 IOWatcher(int fd, DrmDevice::PageFlipManager* page_flip_manager)
201 : page_flip_manager_(page_flip_manager), controller_(FROM_HERE), fd_(fd) {
202 Register();
203 }
204
~IOWatcher()205 ~IOWatcher() override { Unregister(); }
206
207 private:
Register()208 void Register() {
209 DCHECK(base::MessageLoopCurrentForIO::IsSet());
210 base::MessageLoopCurrentForIO::Get()->WatchFileDescriptor(
211 fd_, true, base::MessagePumpForIO::WATCH_READ, &controller_, this);
212 }
213
Unregister()214 void Unregister() {
215 DCHECK(base::MessageLoopCurrentForIO::IsSet());
216 controller_.StopWatchingFileDescriptor();
217 }
218
219 // base::MessagePumpLibevent::FdWatcher overrides:
OnFileCanReadWithoutBlocking(int fd)220 void OnFileCanReadWithoutBlocking(int fd) override {
221 DCHECK(base::MessageLoopCurrentForIO::IsSet());
222 TRACE_EVENT1("drm", "OnDrmEvent", "socket", fd);
223
224 if (!ProcessDrmEvent(
225 fd, base::BindRepeating(&DrmDevice::PageFlipManager::OnPageFlip,
226 base::Unretained(page_flip_manager_))))
227 Unregister();
228 }
229
OnFileCanWriteWithoutBlocking(int fd)230 void OnFileCanWriteWithoutBlocking(int fd) override { NOTREACHED(); }
231
232 DrmDevice::PageFlipManager* page_flip_manager_;
233
234 base::MessagePumpLibevent::FdWatchController controller_;
235
236 int fd_;
237
238 DISALLOW_COPY_AND_ASSIGN(IOWatcher);
239 };
240
DrmDevice(const base::FilePath & device_path,base::File file,bool is_primary_device,std::unique_ptr<GbmDevice> gbm)241 DrmDevice::DrmDevice(const base::FilePath& device_path,
242 base::File file,
243 bool is_primary_device,
244 std::unique_ptr<GbmDevice> gbm)
245 : device_path_(device_path),
246 file_(std::move(file)),
247 page_flip_manager_(new PageFlipManager()),
248 is_primary_device_(is_primary_device),
249 gbm_(std::move(gbm)) {}
250
251 DrmDevice::~DrmDevice() = default;
252
Initialize()253 bool DrmDevice::Initialize() {
254 // Ignore devices that cannot perform modesetting.
255 if (!CanQueryForResources(file_.GetPlatformFile())) {
256 VLOG(2) << "Cannot query for resources for '" << device_path_.value()
257 << "'";
258 return false;
259 }
260
261 // Use atomic only if kernel allows it.
262 is_atomic_ = SetCapability(DRM_CLIENT_CAP_ATOMIC, 1);
263 if (is_atomic_)
264 plane_manager_ = std::make_unique<HardwareDisplayPlaneManagerAtomic>(this);
265 else
266 plane_manager_ = std::make_unique<HardwareDisplayPlaneManagerLegacy>(this);
267 if (!plane_manager_->Initialize()) {
268 LOG(ERROR) << "Failed to initialize the plane manager for "
269 << device_path_.value();
270 plane_manager_.reset();
271 return false;
272 }
273
274 uint64_t value;
275 allow_addfb2_modifiers_ =
276 GetCapability(DRM_CAP_ADDFB2_MODIFIERS, &value) && value;
277
278 watcher_.reset(
279 new IOWatcher(file_.GetPlatformFile(), page_flip_manager_.get()));
280
281 return true;
282 }
283
GetResources()284 ScopedDrmResourcesPtr DrmDevice::GetResources() {
285 DCHECK(file_.IsValid());
286 return ScopedDrmResourcesPtr(drmModeGetResources(file_.GetPlatformFile()));
287 }
288
GetObjectProperties(uint32_t object_id,uint32_t object_type)289 ScopedDrmObjectPropertyPtr DrmDevice::GetObjectProperties(
290 uint32_t object_id,
291 uint32_t object_type) {
292 DCHECK(file_.IsValid());
293 return ScopedDrmObjectPropertyPtr(drmModeObjectGetProperties(
294 file_.GetPlatformFile(), object_id, object_type));
295 }
296
GetCrtc(uint32_t crtc_id)297 ScopedDrmCrtcPtr DrmDevice::GetCrtc(uint32_t crtc_id) {
298 DCHECK(file_.IsValid());
299 return ScopedDrmCrtcPtr(drmModeGetCrtc(file_.GetPlatformFile(), crtc_id));
300 }
301
SetCrtc(uint32_t crtc_id,uint32_t framebuffer,std::vector<uint32_t> connectors,const drmModeModeInfo & mode)302 bool DrmDevice::SetCrtc(uint32_t crtc_id,
303 uint32_t framebuffer,
304 std::vector<uint32_t> connectors,
305 const drmModeModeInfo& mode) {
306 DCHECK(file_.IsValid());
307 DCHECK(!connectors.empty());
308
309 TRACE_EVENT2("drm", "DrmDevice::SetCrtc", "crtc", crtc_id, "size",
310 gfx::Size(mode.hdisplay, mode.vdisplay).ToString());
311 return !drmModeSetCrtc(file_.GetPlatformFile(), crtc_id, framebuffer, 0, 0,
312 connectors.data(), connectors.size(),
313 const_cast<drmModeModeInfo*>(&mode));
314 }
315
DisableCrtc(uint32_t crtc_id)316 bool DrmDevice::DisableCrtc(uint32_t crtc_id) {
317 DCHECK(file_.IsValid());
318 TRACE_EVENT1("drm", "DrmDevice::DisableCrtc", "crtc", crtc_id);
319 return !drmModeSetCrtc(file_.GetPlatformFile(), crtc_id, 0, 0, 0, nullptr, 0,
320 nullptr);
321 }
322
GetConnector(uint32_t connector_id)323 ScopedDrmConnectorPtr DrmDevice::GetConnector(uint32_t connector_id) {
324 DCHECK(file_.IsValid());
325 TRACE_EVENT1("drm", "DrmDevice::GetConnector", "connector", connector_id);
326 return ScopedDrmConnectorPtr(
327 drmModeGetConnector(file_.GetPlatformFile(), connector_id));
328 }
329
AddFramebuffer2(uint32_t width,uint32_t height,uint32_t format,uint32_t handles[4],uint32_t strides[4],uint32_t offsets[4],uint64_t modifiers[4],uint32_t * framebuffer,uint32_t flags)330 bool DrmDevice::AddFramebuffer2(uint32_t width,
331 uint32_t height,
332 uint32_t format,
333 uint32_t handles[4],
334 uint32_t strides[4],
335 uint32_t offsets[4],
336 uint64_t modifiers[4],
337 uint32_t* framebuffer,
338 uint32_t flags) {
339 DCHECK(file_.IsValid());
340 TRACE_EVENT1("drm", "DrmDevice::AddFramebuffer", "handle", handles[0]);
341 return !drmModeAddFB2WithModifiers(file_.GetPlatformFile(), width, height,
342 format, handles, strides, offsets,
343 modifiers, framebuffer, flags);
344 }
345
RemoveFramebuffer(uint32_t framebuffer)346 bool DrmDevice::RemoveFramebuffer(uint32_t framebuffer) {
347 DCHECK(file_.IsValid());
348 TRACE_EVENT1("drm", "DrmDevice::RemoveFramebuffer", "framebuffer",
349 framebuffer);
350 return !drmModeRmFB(file_.GetPlatformFile(), framebuffer);
351 }
352
PageFlip(uint32_t crtc_id,uint32_t framebuffer,scoped_refptr<PageFlipRequest> page_flip_request)353 bool DrmDevice::PageFlip(uint32_t crtc_id,
354 uint32_t framebuffer,
355 scoped_refptr<PageFlipRequest> page_flip_request) {
356 DCHECK(file_.IsValid());
357 TRACE_EVENT2("drm", "DrmDevice::PageFlip", "crtc", crtc_id, "framebuffer",
358 framebuffer);
359
360 // NOTE: Calling drmModeSetCrtc will immediately update the state, though
361 // callbacks to already scheduled page flips will be honored by the kernel.
362 uint64_t id = page_flip_manager_->GetNextId();
363 if (!drmModePageFlip(file_.GetPlatformFile(), crtc_id, framebuffer,
364 DRM_MODE_PAGE_FLIP_EVENT, reinterpret_cast<void*>(id))) {
365 // If successful the payload will be removed by a PageFlip event.
366 page_flip_manager_->RegisterCallback(id, 1,
367 page_flip_request->AddPageFlip());
368 return true;
369 }
370
371 return false;
372 }
373
GetFramebuffer(uint32_t framebuffer)374 ScopedDrmFramebufferPtr DrmDevice::GetFramebuffer(uint32_t framebuffer) {
375 DCHECK(file_.IsValid());
376 TRACE_EVENT1("drm", "DrmDevice::GetFramebuffer", "framebuffer", framebuffer);
377 return ScopedDrmFramebufferPtr(
378 drmModeGetFB(file_.GetPlatformFile(), framebuffer));
379 }
380
GetPlane(uint32_t plane_id)381 ScopedDrmPlanePtr DrmDevice::GetPlane(uint32_t plane_id) {
382 DCHECK(file_.IsValid());
383 return ScopedDrmPlanePtr(drmModeGetPlane(file_.GetPlatformFile(), plane_id));
384 }
385
GetPlaneResources()386 ScopedDrmPlaneResPtr DrmDevice::GetPlaneResources() {
387 DCHECK(file_.IsValid());
388 return ScopedDrmPlaneResPtr(
389 drmModeGetPlaneResources(file_.GetPlatformFile()));
390 }
391
GetProperty(drmModeConnector * connector,const char * name)392 ScopedDrmPropertyPtr DrmDevice::GetProperty(drmModeConnector* connector,
393 const char* name) {
394 TRACE_EVENT2("drm", "DrmDevice::GetProperty", "connector",
395 connector->connector_id, "name", name);
396 for (int i = 0; i < connector->count_props; ++i) {
397 ScopedDrmPropertyPtr property(
398 drmModeGetProperty(file_.GetPlatformFile(), connector->props[i]));
399 if (!property)
400 continue;
401
402 if (strcmp(property->name, name) == 0)
403 return property;
404 }
405
406 return ScopedDrmPropertyPtr();
407 }
408
GetProperty(uint32_t id)409 ScopedDrmPropertyPtr DrmDevice::GetProperty(uint32_t id) {
410 return ScopedDrmPropertyPtr(drmModeGetProperty(file_.GetPlatformFile(), id));
411 }
412
SetProperty(uint32_t connector_id,uint32_t property_id,uint64_t value)413 bool DrmDevice::SetProperty(uint32_t connector_id,
414 uint32_t property_id,
415 uint64_t value) {
416 DCHECK(file_.IsValid());
417 return !drmModeConnectorSetProperty(file_.GetPlatformFile(), connector_id,
418 property_id, value);
419 }
420
CreatePropertyBlob(const void * blob,size_t size)421 ScopedDrmPropertyBlob DrmDevice::CreatePropertyBlob(const void* blob,
422 size_t size) {
423 uint32_t id = 0;
424 int ret = drmModeCreatePropertyBlob(file_.GetPlatformFile(), blob, size, &id);
425 DCHECK(!ret && id);
426
427 return ScopedDrmPropertyBlob(new DrmPropertyBlobMetadata(this, id));
428 }
429
DestroyPropertyBlob(uint32_t id)430 void DrmDevice::DestroyPropertyBlob(uint32_t id) {
431 drmModeDestroyPropertyBlob(file_.GetPlatformFile(), id);
432 }
433
GetCapability(uint64_t capability,uint64_t * value)434 bool DrmDevice::GetCapability(uint64_t capability, uint64_t* value) {
435 DCHECK(file_.IsValid());
436 return !drmGetCap(file_.GetPlatformFile(), capability, value);
437 }
438
GetPropertyBlob(uint32_t property_id)439 ScopedDrmPropertyBlobPtr DrmDevice::GetPropertyBlob(uint32_t property_id) {
440 DCHECK(file_.IsValid());
441 return ScopedDrmPropertyBlobPtr(
442 drmModeGetPropertyBlob(file_.GetPlatformFile(), property_id));
443 }
444
GetPropertyBlob(drmModeConnector * connector,const char * name)445 ScopedDrmPropertyBlobPtr DrmDevice::GetPropertyBlob(drmModeConnector* connector,
446 const char* name) {
447 DCHECK(file_.IsValid());
448 TRACE_EVENT2("drm", "DrmDevice::GetPropertyBlob", "connector",
449 connector->connector_id, "name", name);
450 for (int i = 0; i < connector->count_props; ++i) {
451 ScopedDrmPropertyPtr property(
452 drmModeGetProperty(file_.GetPlatformFile(), connector->props[i]));
453 if (!property)
454 continue;
455
456 if (strcmp(property->name, name) == 0 &&
457 (property->flags & DRM_MODE_PROP_BLOB))
458 return ScopedDrmPropertyBlobPtr(drmModeGetPropertyBlob(
459 file_.GetPlatformFile(), connector->prop_values[i]));
460 }
461
462 return ScopedDrmPropertyBlobPtr();
463 }
464
SetObjectProperty(uint32_t object_id,uint32_t object_type,uint32_t property_id,uint32_t property_value)465 bool DrmDevice::SetObjectProperty(uint32_t object_id,
466 uint32_t object_type,
467 uint32_t property_id,
468 uint32_t property_value) {
469 DCHECK(file_.IsValid());
470 return !drmModeObjectSetProperty(file_.GetPlatformFile(), object_id,
471 object_type, property_id, property_value);
472 }
473
SetCursor(uint32_t crtc_id,uint32_t handle,const gfx::Size & size)474 bool DrmDevice::SetCursor(uint32_t crtc_id,
475 uint32_t handle,
476 const gfx::Size& size) {
477 DCHECK(file_.IsValid());
478 TRACE_EVENT2("drm", "DrmDevice::SetCursor", "crtc_id", crtc_id, "handle",
479 handle);
480 return !drmModeSetCursor(file_.GetPlatformFile(), crtc_id, handle,
481 size.width(), size.height());
482 }
483
MoveCursor(uint32_t crtc_id,const gfx::Point & point)484 bool DrmDevice::MoveCursor(uint32_t crtc_id, const gfx::Point& point) {
485 DCHECK(file_.IsValid());
486 TRACE_EVENT1("drm", "DrmDevice::MoveCursor", "crtc_id", crtc_id);
487 return !drmModeMoveCursor(file_.GetPlatformFile(), crtc_id, point.x(),
488 point.y());
489 }
490
CreateDumbBuffer(const SkImageInfo & info,uint32_t * handle,uint32_t * stride)491 bool DrmDevice::CreateDumbBuffer(const SkImageInfo& info,
492 uint32_t* handle,
493 uint32_t* stride) {
494 DCHECK(file_.IsValid());
495
496 TRACE_EVENT0("drm", "DrmDevice::CreateDumbBuffer");
497 return DrmCreateDumbBuffer(file_.GetPlatformFile(), info, handle, stride);
498 }
499
DestroyDumbBuffer(uint32_t handle)500 bool DrmDevice::DestroyDumbBuffer(uint32_t handle) {
501 DCHECK(file_.IsValid());
502 TRACE_EVENT1("drm", "DrmDevice::DestroyDumbBuffer", "handle", handle);
503 return DrmDestroyDumbBuffer(file_.GetPlatformFile(), handle);
504 }
505
MapDumbBuffer(uint32_t handle,size_t size,void ** pixels)506 bool DrmDevice::MapDumbBuffer(uint32_t handle, size_t size, void** pixels) {
507 struct drm_mode_map_dumb map_request;
508 memset(&map_request, 0, sizeof(map_request));
509 map_request.handle = handle;
510 if (drmIoctl(file_.GetPlatformFile(), DRM_IOCTL_MODE_MAP_DUMB,
511 &map_request)) {
512 PLOG(ERROR) << "Cannot prepare dumb buffer for mapping";
513 return false;
514 }
515
516 *pixels = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED,
517 file_.GetPlatformFile(), map_request.offset);
518 if (*pixels == MAP_FAILED) {
519 PLOG(ERROR) << "Cannot mmap dumb buffer";
520 return false;
521 }
522
523 return true;
524 }
525
UnmapDumbBuffer(void * pixels,size_t size)526 bool DrmDevice::UnmapDumbBuffer(void* pixels, size_t size) {
527 return !munmap(pixels, size);
528 }
529
CloseBufferHandle(uint32_t handle)530 bool DrmDevice::CloseBufferHandle(uint32_t handle) {
531 struct drm_gem_close close_request;
532 memset(&close_request, 0, sizeof(close_request));
533 close_request.handle = handle;
534 return !drmIoctl(file_.GetPlatformFile(), DRM_IOCTL_GEM_CLOSE,
535 &close_request);
536 }
537
CommitProperties(drmModeAtomicReq * properties,uint32_t flags,uint32_t crtc_count,scoped_refptr<PageFlipRequest> page_flip_request)538 bool DrmDevice::CommitProperties(
539 drmModeAtomicReq* properties,
540 uint32_t flags,
541 uint32_t crtc_count,
542 scoped_refptr<PageFlipRequest> page_flip_request) {
543 uint64_t id = 0;
544 if (page_flip_request) {
545 flags |= DRM_MODE_PAGE_FLIP_EVENT;
546 id = page_flip_manager_->GetNextId();
547 }
548
549 if (!drmModeAtomicCommit(file_.GetPlatformFile(), properties, flags,
550 reinterpret_cast<void*>(id))) {
551 if (page_flip_request) {
552 page_flip_manager_->RegisterCallback(id, crtc_count,
553 page_flip_request->AddPageFlip());
554 }
555
556 return true;
557 }
558 return false;
559 }
560
SetCapability(uint64_t capability,uint64_t value)561 bool DrmDevice::SetCapability(uint64_t capability, uint64_t value) {
562 DCHECK(file_.IsValid());
563
564 struct drm_set_client_cap cap = {capability, value};
565 return !drmIoctl(file_.GetPlatformFile(), DRM_IOCTL_SET_CLIENT_CAP, &cap);
566 }
567
SetMaster()568 bool DrmDevice::SetMaster() {
569 TRACE_EVENT1("drm", "DrmDevice::SetMaster", "path", device_path_.value());
570 DCHECK(file_.IsValid());
571 return (drmSetMaster(file_.GetPlatformFile()) == 0);
572 }
573
DropMaster()574 bool DrmDevice::DropMaster() {
575 TRACE_EVENT1("drm", "DrmDevice::DropMaster", "path", device_path_.value());
576 DCHECK(file_.IsValid());
577 return (drmDropMaster(file_.GetPlatformFile()) == 0);
578 }
579
SetGammaRamp(uint32_t crtc_id,const std::vector<display::GammaRampRGBEntry> & lut)580 bool DrmDevice::SetGammaRamp(
581 uint32_t crtc_id,
582 const std::vector<display::GammaRampRGBEntry>& lut) {
583 ScopedDrmCrtcPtr crtc = GetCrtc(crtc_id);
584 size_t gamma_size = static_cast<size_t>(crtc->gamma_size);
585
586 if (gamma_size == 0 && lut.empty())
587 return true;
588
589 if (gamma_size == 0) {
590 LOG(ERROR) << "Gamma table not supported";
591 return false;
592 }
593
594 // TODO(robert.bradford) resample the incoming ramp to match what the kernel
595 // expects.
596 if (!lut.empty() && gamma_size != lut.size()) {
597 LOG(ERROR) << "Gamma table size mismatch: supplied " << lut.size()
598 << " expected " << gamma_size;
599 return false;
600 }
601
602 std::vector<uint16_t> r, g, b;
603 r.reserve(gamma_size);
604 g.reserve(gamma_size);
605 b.reserve(gamma_size);
606
607 if (lut.empty()) {
608 // Create a linear gamma ramp table to deactivate the feature.
609 for (size_t i = 0; i < gamma_size; ++i) {
610 uint16_t value = (i * ((1 << 16) - 1)) / (gamma_size - 1);
611 r.push_back(value);
612 g.push_back(value);
613 b.push_back(value);
614 }
615 } else {
616 for (size_t i = 0; i < gamma_size; ++i) {
617 r.push_back(lut[i].r);
618 g.push_back(lut[i].g);
619 b.push_back(lut[i].b);
620 }
621 }
622
623 DCHECK(file_.IsValid());
624 TRACE_EVENT0("drm", "DrmDevice::SetGamma");
625 return (drmModeCrtcSetGamma(file_.GetPlatformFile(), crtc_id, r.size(), &r[0],
626 &g[0], &b[0]) == 0);
627 }
628
629 } // namespace ui
630