Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / ui / ozone / platform / drm / gpu / drm_device.cc
blob7b23b05e6b66c3c2c96076509f7b5dc7018c9b2a
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.
5 #include "ui/ozone/platform/drm/gpu/drm_device.h"
7 #include <fcntl.h>
8 #include <sys/mman.h>
9 #include <unistd.h>
10 #include <xf86drm.h>
11 #include <xf86drmMode.h>
13 #include "base/logging.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/stl_util.h"
16 #include "base/synchronization/waitable_event.h"
17 #include "base/task_runner.h"
18 #include "base/thread_task_runner_handle.h"
19 #include "base/trace_event/trace_event.h"
20 #include "third_party/skia/include/core/SkImageInfo.h"
21 #include "ui/display/types/gamma_ramp_rgb_entry.h"
22 #include "ui/ozone/platform/drm/common/drm_util.h"
23 #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h"
25 #if defined(USE_DRM_ATOMIC)
26 #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h"
27 #endif
29 namespace ui {
31 namespace {
33 typedef base::Callback<void(uint32_t /* frame */,
34 uint32_t /* seconds */,
35 uint32_t /* useconds */,
36 uint64_t /* id */)> DrmEventHandler;
38 bool DrmCreateDumbBuffer(int fd,
39 const SkImageInfo& info,
40 uint32_t* handle,
41 uint32_t* stride) {
42 struct drm_mode_create_dumb request;
43 memset(&request, 0, sizeof(request));
44 request.width = info.width();
45 request.height = info.height();
46 request.bpp = info.bytesPerPixel() << 3;
47 request.flags = 0;
49 if (drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &request) < 0) {
50 VPLOG(2) << "Cannot create dumb buffer";
51 return false;
54 // The driver may choose to align the last row as well. We don't care about
55 // the last alignment bits since they aren't used for display purposes, so
56 // just check that the expected size is <= to what the driver allocated.
57 DCHECK_LE(info.getSafeSize(request.pitch), request.size);
59 *handle = request.handle;
60 *stride = request.pitch;
61 return true;
64 bool DrmDestroyDumbBuffer(int fd, uint32_t handle) {
65 struct drm_mode_destroy_dumb destroy_request;
66 memset(&destroy_request, 0, sizeof(destroy_request));
67 destroy_request.handle = handle;
68 return !drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_request);
71 bool ProcessDrmEvent(int fd, const DrmEventHandler& callback) {
72 char buffer[1024];
73 int len = read(fd, buffer, sizeof(buffer));
74 if (len == 0)
75 return false;
77 if (len < static_cast<int>(sizeof(drm_event))) {
78 PLOG(ERROR) << "Failed to read DRM event";
79 return false;
82 int idx = 0;
83 while (idx < len) {
84 DCHECK_LE(static_cast<int>(sizeof(drm_event)), len - idx);
85 drm_event event;
86 memcpy(&event, &buffer[idx], sizeof(event));
87 switch (event.type) {
88 case DRM_EVENT_FLIP_COMPLETE: {
89 DCHECK_LE(static_cast<int>(sizeof(drm_event_vblank)), len - idx);
90 drm_event_vblank vblank;
91 memcpy(&vblank, &buffer[idx], sizeof(vblank));
92 callback.Run(vblank.sequence, vblank.tv_sec, vblank.tv_usec,
93 vblank.user_data);
94 } break;
95 case DRM_EVENT_VBLANK:
96 break;
97 default:
98 NOTREACHED();
99 break;
102 idx += event.length;
105 return true;
108 bool CanQueryForResources(int fd) {
109 drm_mode_card_res resources;
110 memset(&resources, 0, sizeof(resources));
111 // If there is no error getting DRM resources then assume this is a
112 // modesetting device.
113 return !drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &resources);
116 } // namespace
118 class DrmDevice::PageFlipManager
119 : public base::RefCountedThreadSafe<DrmDevice::PageFlipManager> {
120 public:
121 PageFlipManager() : next_id_(0) {}
123 void OnPageFlip(uint32_t frame,
124 uint32_t seconds,
125 uint32_t useconds,
126 uint64_t id) {
127 auto it =
128 std::find_if(callbacks_.begin(), callbacks_.end(), FindCallback(id));
129 if (it == callbacks_.end()) {
130 LOG(WARNING) << "Could not find callback for page flip id=" << id;
131 return;
134 DrmDevice::PageFlipCallback callback = it->callback;
135 callbacks_.erase(it);
136 callback.Run(frame, seconds, useconds);
139 uint64_t GetNextId() { return next_id_++; }
141 void RegisterCallback(uint64_t id,
142 const DrmDevice::PageFlipCallback& callback) {
143 callbacks_.push_back({id, callback});
146 private:
147 friend class base::RefCountedThreadSafe<DrmDevice::PageFlipManager>;
148 ~PageFlipManager() {}
150 struct PageFlip {
151 uint64_t id;
152 DrmDevice::PageFlipCallback callback;
155 struct FindCallback {
156 FindCallback(uint64_t id) : id(id) {}
158 bool operator()(const PageFlip& flip) const { return flip.id == id; }
160 uint64_t id;
163 uint64_t next_id_;
165 std::vector<PageFlip> callbacks_;
167 DISALLOW_COPY_AND_ASSIGN(PageFlipManager);
170 class DrmDevice::IOWatcher
171 : public base::RefCountedThreadSafe<DrmDevice::IOWatcher>,
172 public base::MessagePumpLibevent::Watcher {
173 public:
174 IOWatcher(int fd,
175 const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
176 const scoped_refptr<DrmDevice::PageFlipManager>& page_flip_manager)
177 : main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
178 io_task_runner_(io_task_runner),
179 page_flip_manager_(page_flip_manager),
180 paused_(true),
181 fd_(fd) {}
183 void SetPaused(bool paused) {
184 if (paused_ == paused)
185 return;
187 paused_ = paused;
188 base::WaitableEvent done(false, false);
189 io_task_runner_->PostTask(
190 FROM_HERE, base::Bind(&IOWatcher::SetPausedOnIO, this, &done));
191 done.Wait();
194 void Shutdown() {
195 if (!paused_)
196 io_task_runner_->PostTask(FROM_HERE,
197 base::Bind(&IOWatcher::UnregisterOnIO, this));
200 private:
201 friend class base::RefCountedThreadSafe<IOWatcher>;
203 ~IOWatcher() override {}
205 void RegisterOnIO() {
206 DCHECK(base::MessageLoopForIO::IsCurrent());
207 base::MessageLoopForIO::current()->WatchFileDescriptor(
208 fd_, true, base::MessageLoopForIO::WATCH_READ, &controller_, this);
211 void UnregisterOnIO() {
212 DCHECK(base::MessageLoopForIO::IsCurrent());
213 controller_.StopWatchingFileDescriptor();
216 void SetPausedOnIO(base::WaitableEvent* done) {
217 DCHECK(base::MessageLoopForIO::IsCurrent());
218 if (paused_)
219 UnregisterOnIO();
220 else
221 RegisterOnIO();
222 done->Signal();
225 void OnPageFlipOnIO(uint32_t frame,
226 uint32_t seconds,
227 uint32_t useconds,
228 uint64_t id) {
229 main_task_runner_->PostTask(
230 FROM_HERE,
231 base::Bind(&DrmDevice::PageFlipManager::OnPageFlip, page_flip_manager_,
232 frame, seconds, useconds, id));
235 // base::MessagePumpLibevent::Watcher overrides:
236 void OnFileCanReadWithoutBlocking(int fd) override {
237 DCHECK(base::MessageLoopForIO::IsCurrent());
238 TRACE_EVENT1("drm", "OnDrmEvent", "socket", fd);
240 if (!ProcessDrmEvent(
241 fd, base::Bind(&DrmDevice::IOWatcher::OnPageFlipOnIO, this)))
242 UnregisterOnIO();
245 void OnFileCanWriteWithoutBlocking(int fd) override { NOTREACHED(); }
247 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
248 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
250 scoped_refptr<DrmDevice::PageFlipManager> page_flip_manager_;
252 base::MessagePumpLibevent::FileDescriptorWatcher controller_;
254 bool paused_;
255 int fd_;
257 DISALLOW_COPY_AND_ASSIGN(IOWatcher);
260 DrmDevice::DrmDevice(const base::FilePath& device_path, base::File file)
261 : device_path_(device_path),
262 file_(file.Pass()),
263 page_flip_manager_(new PageFlipManager()) {
266 DrmDevice::~DrmDevice() {
267 if (watcher_)
268 watcher_->Shutdown();
271 bool DrmDevice::Initialize(bool use_atomic) {
272 // Ignore devices that cannot perform modesetting.
273 if (!CanQueryForResources(file_.GetPlatformFile())) {
274 VLOG(2) << "Cannot query for resources for '" << device_path_.value()
275 << "'";
276 return false;
279 #if defined(USE_DRM_ATOMIC)
280 // Use atomic only if the build, kernel & flags all allow it.
281 if (use_atomic && SetCapability(DRM_CLIENT_CAP_ATOMIC, 1))
282 plane_manager_.reset(new HardwareDisplayPlaneManagerAtomic());
283 #endif // defined(USE_DRM_ATOMIC)
285 if (!plane_manager_)
286 plane_manager_.reset(new HardwareDisplayPlaneManagerLegacy());
287 if (!plane_manager_->Initialize(this)) {
288 LOG(ERROR) << "Failed to initialize the plane manager for "
289 << device_path_.value();
290 plane_manager_.reset();
291 return false;
294 return true;
297 void DrmDevice::InitializeTaskRunner(
298 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) {
299 DCHECK(!task_runner_);
300 task_runner_ = task_runner;
301 watcher_ =
302 new IOWatcher(file_.GetPlatformFile(), task_runner_, page_flip_manager_);
305 ScopedDrmCrtcPtr DrmDevice::GetCrtc(uint32_t crtc_id) {
306 DCHECK(file_.IsValid());
307 return ScopedDrmCrtcPtr(drmModeGetCrtc(file_.GetPlatformFile(), crtc_id));
310 bool DrmDevice::SetCrtc(uint32_t crtc_id,
311 uint32_t framebuffer,
312 std::vector<uint32_t> connectors,
313 drmModeModeInfo* mode) {
314 DCHECK(file_.IsValid());
315 DCHECK(!connectors.empty());
316 DCHECK(mode);
318 TRACE_EVENT2("drm", "DrmDevice::SetCrtc", "crtc", crtc_id, "size",
319 gfx::Size(mode->hdisplay, mode->vdisplay).ToString());
320 return !drmModeSetCrtc(file_.GetPlatformFile(), crtc_id, framebuffer, 0, 0,
321 vector_as_array(&connectors), connectors.size(), mode);
324 bool DrmDevice::SetCrtc(drmModeCrtc* crtc, std::vector<uint32_t> connectors) {
325 DCHECK(file_.IsValid());
326 // If there's no buffer then the CRTC was disabled.
327 if (!crtc->buffer_id)
328 return DisableCrtc(crtc->crtc_id);
330 DCHECK(!connectors.empty());
332 TRACE_EVENT1("drm", "DrmDevice::RestoreCrtc", "crtc", crtc->crtc_id);
333 return !drmModeSetCrtc(file_.GetPlatformFile(), crtc->crtc_id,
334 crtc->buffer_id, crtc->x, crtc->y,
335 vector_as_array(&connectors), connectors.size(),
336 &crtc->mode);
339 bool DrmDevice::DisableCrtc(uint32_t crtc_id) {
340 DCHECK(file_.IsValid());
341 TRACE_EVENT1("drm", "DrmDevice::DisableCrtc", "crtc", crtc_id);
342 return !drmModeSetCrtc(file_.GetPlatformFile(), crtc_id, 0, 0, 0, NULL, 0,
343 NULL);
346 ScopedDrmConnectorPtr DrmDevice::GetConnector(uint32_t connector_id) {
347 DCHECK(file_.IsValid());
348 TRACE_EVENT1("drm", "DrmDevice::GetConnector", "connector", connector_id);
349 return ScopedDrmConnectorPtr(
350 drmModeGetConnector(file_.GetPlatformFile(), connector_id));
353 bool DrmDevice::AddFramebuffer(uint32_t width,
354 uint32_t height,
355 uint8_t depth,
356 uint8_t bpp,
357 uint32_t stride,
358 uint32_t handle,
359 uint32_t* framebuffer) {
360 DCHECK(file_.IsValid());
361 TRACE_EVENT1("drm", "DrmDevice::AddFramebuffer", "handle", handle);
362 return !drmModeAddFB(file_.GetPlatformFile(), width, height, depth, bpp,
363 stride, handle, framebuffer);
366 bool DrmDevice::RemoveFramebuffer(uint32_t framebuffer) {
367 DCHECK(file_.IsValid());
368 TRACE_EVENT1("drm", "DrmDevice::RemoveFramebuffer", "framebuffer",
369 framebuffer);
370 return !drmModeRmFB(file_.GetPlatformFile(), framebuffer);
373 bool DrmDevice::PageFlip(uint32_t crtc_id,
374 uint32_t framebuffer,
375 bool is_sync,
376 const PageFlipCallback& callback) {
377 DCHECK(file_.IsValid());
378 TRACE_EVENT2("drm", "DrmDevice::PageFlip", "crtc", crtc_id, "framebuffer",
379 framebuffer);
381 if (watcher_)
382 watcher_->SetPaused(is_sync);
384 // NOTE: Calling drmModeSetCrtc will immediately update the state, though
385 // callbacks to already scheduled page flips will be honored by the kernel.
386 uint64_t id = page_flip_manager_->GetNextId();
387 if (!drmModePageFlip(file_.GetPlatformFile(), crtc_id, framebuffer,
388 DRM_MODE_PAGE_FLIP_EVENT, reinterpret_cast<void*>(id))) {
389 // If successful the payload will be removed by a PageFlip event.
390 page_flip_manager_->RegisterCallback(id, callback);
392 // If the flip was requested synchronous or if no watcher has been installed
393 // yet, then synchronously handle the page flip events.
394 if (is_sync || !watcher_) {
395 TRACE_EVENT1("drm", "OnDrmEvent", "socket", file_.GetPlatformFile());
397 ProcessDrmEvent(
398 file_.GetPlatformFile(),
399 base::Bind(&PageFlipManager::OnPageFlip, page_flip_manager_));
402 return true;
405 return false;
408 bool DrmDevice::PageFlipOverlay(uint32_t crtc_id,
409 uint32_t framebuffer,
410 const gfx::Rect& location,
411 const gfx::Rect& source,
412 int overlay_plane) {
413 DCHECK(file_.IsValid());
414 TRACE_EVENT2("drm", "DrmDevice::PageFlipOverlay", "crtc", crtc_id,
415 "framebuffer", framebuffer);
416 return !drmModeSetPlane(file_.GetPlatformFile(), overlay_plane, crtc_id,
417 framebuffer, 0, location.x(), location.y(),
418 location.width(), location.height(), source.x(),
419 source.y(), source.width(), source.height());
422 ScopedDrmFramebufferPtr DrmDevice::GetFramebuffer(uint32_t framebuffer) {
423 DCHECK(file_.IsValid());
424 TRACE_EVENT1("drm", "DrmDevice::GetFramebuffer", "framebuffer", framebuffer);
425 return ScopedDrmFramebufferPtr(
426 drmModeGetFB(file_.GetPlatformFile(), framebuffer));
429 ScopedDrmPropertyPtr DrmDevice::GetProperty(drmModeConnector* connector,
430 const char* name) {
431 TRACE_EVENT2("drm", "DrmDevice::GetProperty", "connector",
432 connector->connector_id, "name", name);
433 for (int i = 0; i < connector->count_props; ++i) {
434 ScopedDrmPropertyPtr property(
435 drmModeGetProperty(file_.GetPlatformFile(), connector->props[i]));
436 if (!property)
437 continue;
439 if (strcmp(property->name, name) == 0)
440 return property.Pass();
443 return ScopedDrmPropertyPtr();
446 bool DrmDevice::SetProperty(uint32_t connector_id,
447 uint32_t property_id,
448 uint64_t value) {
449 DCHECK(file_.IsValid());
450 return !drmModeConnectorSetProperty(file_.GetPlatformFile(), connector_id,
451 property_id, value);
454 bool DrmDevice::GetCapability(uint64_t capability, uint64_t* value) {
455 DCHECK(file_.IsValid());
456 return !drmGetCap(file_.GetPlatformFile(), capability, value);
459 ScopedDrmPropertyBlobPtr DrmDevice::GetPropertyBlob(drmModeConnector* connector,
460 const char* name) {
461 DCHECK(file_.IsValid());
462 TRACE_EVENT2("drm", "DrmDevice::GetPropertyBlob", "connector",
463 connector->connector_id, "name", name);
464 for (int i = 0; i < connector->count_props; ++i) {
465 ScopedDrmPropertyPtr property(
466 drmModeGetProperty(file_.GetPlatformFile(), connector->props[i]));
467 if (!property)
468 continue;
470 if (strcmp(property->name, name) == 0 &&
471 (property->flags & DRM_MODE_PROP_BLOB))
472 return ScopedDrmPropertyBlobPtr(drmModeGetPropertyBlob(
473 file_.GetPlatformFile(), connector->prop_values[i]));
476 return ScopedDrmPropertyBlobPtr();
479 bool DrmDevice::SetCursor(uint32_t crtc_id,
480 uint32_t handle,
481 const gfx::Size& size) {
482 DCHECK(file_.IsValid());
483 TRACE_EVENT1("drm", "DrmDevice::SetCursor", "handle", handle);
484 return !drmModeSetCursor(file_.GetPlatformFile(), crtc_id, handle,
485 size.width(), size.height());
488 bool DrmDevice::MoveCursor(uint32_t crtc_id, const gfx::Point& point) {
489 DCHECK(file_.IsValid());
490 return !drmModeMoveCursor(file_.GetPlatformFile(), crtc_id, point.x(),
491 point.y());
494 bool DrmDevice::CreateDumbBuffer(const SkImageInfo& info,
495 uint32_t* handle,
496 uint32_t* stride) {
497 DCHECK(file_.IsValid());
499 TRACE_EVENT0("drm", "DrmDevice::CreateDumbBuffer");
500 return DrmCreateDumbBuffer(file_.GetPlatformFile(), info, handle, stride);
503 bool DrmDevice::DestroyDumbBuffer(uint32_t handle) {
504 DCHECK(file_.IsValid());
505 TRACE_EVENT1("drm", "DrmDevice::DestroyDumbBuffer", "handle", handle);
506 return DrmDestroyDumbBuffer(file_.GetPlatformFile(), handle);
509 bool DrmDevice::MapDumbBuffer(uint32_t handle, size_t size, void** pixels) {
510 struct drm_mode_map_dumb map_request;
511 memset(&map_request, 0, sizeof(map_request));
512 map_request.handle = handle;
513 if (drmIoctl(file_.GetPlatformFile(), DRM_IOCTL_MODE_MAP_DUMB,
514 &map_request)) {
515 PLOG(ERROR) << "Cannot prepare dumb buffer for mapping";
516 return false;
519 *pixels = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED,
520 file_.GetPlatformFile(), map_request.offset);
521 if (*pixels == MAP_FAILED) {
522 PLOG(ERROR) << "Cannot mmap dumb buffer";
523 return false;
526 return true;
529 bool DrmDevice::UnmapDumbBuffer(void* pixels, size_t size) {
530 return !munmap(pixels, size);
533 bool DrmDevice::CloseBufferHandle(uint32_t handle) {
534 struct drm_gem_close close_request;
535 memset(&close_request, 0, sizeof(close_request));
536 close_request.handle = handle;
537 return !drmIoctl(file_.GetPlatformFile(), DRM_IOCTL_GEM_CLOSE,
538 &close_request);
541 bool DrmDevice::CommitProperties(drmModePropertySet* properties,
542 uint32_t flags,
543 bool is_sync,
544 bool test_only,
545 const PageFlipCallback& callback) {
546 #if defined(USE_DRM_ATOMIC)
547 if (test_only)
548 flags |= DRM_MODE_ATOMIC_TEST_ONLY;
549 else
550 flags |= DRM_MODE_PAGE_FLIP_EVENT;
551 uint64_t id = page_flip_manager_->GetNextId();
552 if (!drmModePropertySetCommit(file_.GetPlatformFile(), flags,
553 reinterpret_cast<void*>(id), properties)) {
554 if (test_only)
555 return true;
556 page_flip_manager_->RegisterCallback(id, callback);
558 // If the flip was requested synchronous or if no watcher has been installed
559 // yet, then synchronously handle the page flip events.
560 if (is_sync || !watcher_) {
561 TRACE_EVENT1("drm", "OnDrmEvent", "socket", file_.GetPlatformFile());
563 ProcessDrmEvent(
564 file_.GetPlatformFile(),
565 base::Bind(&PageFlipManager::OnPageFlip, page_flip_manager_));
567 return true;
569 #endif // defined(USE_DRM_ATOMIC)
570 return false;
573 bool DrmDevice::SetCapability(uint64_t capability, uint64_t value) {
574 DCHECK(file_.IsValid());
575 return !drmSetClientCap(file_.GetPlatformFile(), capability, value);
578 bool DrmDevice::SetMaster() {
579 TRACE_EVENT1("drm", "DrmDevice::SetMaster", "path", device_path_.value());
580 DCHECK(file_.IsValid());
581 return (drmSetMaster(file_.GetPlatformFile()) == 0);
584 bool DrmDevice::DropMaster() {
585 TRACE_EVENT1("drm", "DrmDevice::DropMaster", "path", device_path_.value());
586 DCHECK(file_.IsValid());
587 return (drmDropMaster(file_.GetPlatformFile()) == 0);
590 bool DrmDevice::SetGammaRamp(uint32_t crtc_id,
591 const std::vector<GammaRampRGBEntry>& lut) {
592 ScopedDrmCrtcPtr crtc = GetCrtc(crtc_id);
594 // TODO(robert.bradford) resample the incoming ramp to match what the kernel
595 // expects.
596 if (static_cast<size_t>(crtc->gamma_size) != lut.size()) {
597 LOG(ERROR) << "Gamma table size mismatch: supplied " << lut.size()
598 << " expected " << crtc->gamma_size;
601 std::vector<uint16_t> r, g, b;
602 r.reserve(lut.size());
603 g.reserve(lut.size());
604 b.reserve(lut.size());
606 for (size_t i = 0; i < lut.size(); ++i) {
607 r.push_back(lut[i].r);
608 g.push_back(lut[i].g);
609 b.push_back(lut[i].b);
612 DCHECK(file_.IsValid());
613 TRACE_EVENT0("drm", "DrmDevice::SetGamma");
614 return (drmModeCrtcSetGamma(file_.GetPlatformFile(), crtc_id, r.size(), &r[0],
615 &g[0], &b[0]) == 0);
618 } // namespace ui