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