Supervised user whitelists: Cleanup
[chromium-blink-merge.git] / ui / ozone / platform / drm / gpu / drm_device.cc
blob7b03b43036e61cde4942ab49d6b554791b8616e0
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/ozone/platform/drm/gpu/drm_util.h"
22 #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h"
24 #if defined(USE_DRM_ATOMIC)
25 #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h"
26 #endif
28 namespace ui {
30 namespace {
32 struct PageFlipPayload {
33 PageFlipPayload(const scoped_refptr<base::TaskRunner>& task_runner,
34 const DrmDevice::PageFlipCallback& callback)
35 : task_runner(task_runner), callback(callback) {}
37 // Task runner for the thread scheduling the page flip event. This is used to
38 // run the callback on the same thread the callback was created on.
39 scoped_refptr<base::TaskRunner> task_runner;
40 DrmDevice::PageFlipCallback callback;
43 bool DrmCreateDumbBuffer(int fd,
44 const SkImageInfo& info,
45 uint32_t* handle,
46 uint32_t* stride) {
47 struct drm_mode_create_dumb request;
48 memset(&request, 0, sizeof(request));
49 request.width = info.width();
50 request.height = info.height();
51 request.bpp = info.bytesPerPixel() << 3;
52 request.flags = 0;
54 if (drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &request) < 0) {
55 VPLOG(2) << "Cannot create dumb buffer";
56 return false;
59 // The driver may choose to align the last row as well. We don't care about
60 // the last alignment bits since they aren't used for display purposes, so
61 // just check that the expected size is <= to what the driver allocated.
62 DCHECK_LE(info.getSafeSize(request.pitch), request.size);
64 *handle = request.handle;
65 *stride = request.pitch;
66 return true;
69 bool DrmDestroyDumbBuffer(int fd, uint32_t handle) {
70 struct drm_mode_destroy_dumb destroy_request;
71 memset(&destroy_request, 0, sizeof(destroy_request));
72 destroy_request.handle = handle;
73 return !drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_request);
76 void HandlePageFlipEventOnIO(int fd,
77 unsigned int frame,
78 unsigned int seconds,
79 unsigned int useconds,
80 void* data) {
81 scoped_ptr<PageFlipPayload> payload(static_cast<PageFlipPayload*>(data));
82 payload->task_runner->PostTask(
83 FROM_HERE, base::Bind(payload->callback, frame, seconds, useconds));
86 void HandlePageFlipEventOnUI(int fd,
87 unsigned int frame,
88 unsigned int seconds,
89 unsigned int useconds,
90 void* data) {
91 scoped_ptr<PageFlipPayload> payload(static_cast<PageFlipPayload*>(data));
92 payload->callback.Run(frame, seconds, useconds);
95 bool CanQueryForResources(int fd) {
96 drm_mode_card_res resources;
97 memset(&resources, 0, sizeof(resources));
98 // If there is no error getting DRM resources then assume this is a
99 // modesetting device.
100 return !drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &resources);
103 bool Authenticate(int fd) {
104 drm_magic_t magic;
105 memset(&magic, 0, sizeof(magic));
106 // We need to make sure the DRM device has enough privilege. Use the DRM
107 // authentication logic to figure out if the device has enough permissions.
108 return !drmGetMagic(fd, &magic) && !drmAuthMagic(fd, magic);
111 } // namespace
113 class DrmDevice::IOWatcher
114 : public base::RefCountedThreadSafe<DrmDevice::IOWatcher>,
115 public base::MessagePumpLibevent::Watcher {
116 public:
117 IOWatcher(int fd,
118 const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner)
119 : io_task_runner_(io_task_runner), paused_(true), fd_(fd) {}
121 void SetPaused(bool paused) {
122 if (paused_ == paused)
123 return;
125 paused_ = paused;
126 base::WaitableEvent done(false, false);
127 io_task_runner_->PostTask(
128 FROM_HERE, base::Bind(&IOWatcher::SetPausedOnIO, this, &done));
129 done.Wait();
132 void Shutdown() {
133 if (!paused_)
134 io_task_runner_->PostTask(FROM_HERE,
135 base::Bind(&IOWatcher::UnregisterOnIO, this));
138 private:
139 friend class base::RefCountedThreadSafe<IOWatcher>;
141 ~IOWatcher() override {}
143 void RegisterOnIO() {
144 DCHECK(base::MessageLoopForIO::IsCurrent());
145 base::MessageLoopForIO::current()->WatchFileDescriptor(
146 fd_, true, base::MessageLoopForIO::WATCH_READ, &controller_, this);
149 void UnregisterOnIO() {
150 DCHECK(base::MessageLoopForIO::IsCurrent());
151 controller_.StopWatchingFileDescriptor();
154 void SetPausedOnIO(base::WaitableEvent* done) {
155 DCHECK(base::MessageLoopForIO::IsCurrent());
156 if (paused_)
157 UnregisterOnIO();
158 else
159 RegisterOnIO();
160 done->Signal();
163 // base::MessagePumpLibevent::Watcher overrides:
164 void OnFileCanReadWithoutBlocking(int fd) override {
165 DCHECK(base::MessageLoopForIO::IsCurrent());
166 TRACE_EVENT1("drm", "OnDrmEvent", "socket", fd);
168 drmEventContext event;
169 event.version = DRM_EVENT_CONTEXT_VERSION;
170 event.page_flip_handler = HandlePageFlipEventOnIO;
171 event.vblank_handler = nullptr;
173 drmHandleEvent(fd, &event);
176 void OnFileCanWriteWithoutBlocking(int fd) override { NOTREACHED(); }
178 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
180 base::MessagePumpLibevent::FileDescriptorWatcher controller_;
182 bool paused_;
183 int fd_;
185 DISALLOW_COPY_AND_ASSIGN(IOWatcher);
188 DrmDevice::DrmDevice(const base::FilePath& device_path)
189 : device_path_(device_path),
190 file_(device_path,
191 base::File::FLAG_OPEN | base::File::FLAG_READ |
192 base::File::FLAG_WRITE) {
193 LOG_IF(FATAL, !file_.IsValid())
194 << "Failed to open '" << device_path_.value()
195 << "': " << base::File::ErrorToString(file_.error_details());
198 DrmDevice::DrmDevice(const base::FilePath& device_path, base::File file)
199 : device_path_(device_path), file_(file.Pass()) {
202 DrmDevice::~DrmDevice() {
203 if (watcher_)
204 watcher_->Shutdown();
207 bool DrmDevice::Initialize() {
208 // Ignore devices that cannot perform modesetting.
209 if (!CanQueryForResources(file_.GetPlatformFile())) {
210 VLOG(2) << "Cannot query for resources for '" << device_path_.value()
211 << "'";
212 return false;
215 bool print_warning = true;
216 // TODO(dnicoara) Ugly hack to block until getting master. This is needed
217 // since DRM devices from an old GPU process may be getting deallocated while
218 // the new GPU process tries to take them.
219 // Move ownership of devices in the Browser process and just have the GPU
220 // processes authenticate.
221 while (!Authenticate(file_.GetPlatformFile())) {
222 PLOG_IF(WARNING, print_warning) << "Failed to take master on "
223 << device_path_.value();
224 print_warning = false;
226 usleep(100000);
227 file_ =
228 base::File(device_path_, base::File::FLAG_OPEN | base::File::FLAG_READ |
229 base::File::FLAG_WRITE);
230 LOG_IF(FATAL, !file_.IsValid())
231 << "Failed to open '" << device_path_.value()
232 << "': " << base::File::ErrorToString(file_.error_details());
235 VLOG(1) << "Succeeded in taking master on " << device_path_.value();
237 #if defined(USE_DRM_ATOMIC)
238 plane_manager_.reset(new HardwareDisplayPlaneManagerAtomic());
239 #else
240 plane_manager_.reset(new HardwareDisplayPlaneManagerLegacy());
241 #endif // defined(USE_DRM_ATOMIC)
242 if (!plane_manager_->Initialize(this)) {
243 LOG(ERROR) << "Failed to initialize the plane manager for "
244 << device_path_.value();
245 plane_manager_.reset();
246 return false;
249 return true;
252 void DrmDevice::InitializeTaskRunner(
253 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) {
254 DCHECK(!task_runner_);
255 task_runner_ = task_runner;
256 watcher_ = new IOWatcher(file_.GetPlatformFile(), task_runner_);
259 ScopedDrmCrtcPtr DrmDevice::GetCrtc(uint32_t crtc_id) {
260 DCHECK(file_.IsValid());
261 return ScopedDrmCrtcPtr(drmModeGetCrtc(file_.GetPlatformFile(), crtc_id));
264 bool DrmDevice::SetCrtc(uint32_t crtc_id,
265 uint32_t framebuffer,
266 std::vector<uint32_t> connectors,
267 drmModeModeInfo* mode) {
268 DCHECK(file_.IsValid());
269 DCHECK(!connectors.empty());
270 DCHECK(mode);
272 TRACE_EVENT2("drm", "DrmDevice::SetCrtc", "crtc", crtc_id, "size",
273 gfx::Size(mode->hdisplay, mode->vdisplay).ToString());
274 return !drmModeSetCrtc(file_.GetPlatformFile(), crtc_id, framebuffer, 0, 0,
275 vector_as_array(&connectors), connectors.size(), mode);
278 bool DrmDevice::SetCrtc(drmModeCrtc* crtc, std::vector<uint32_t> connectors) {
279 DCHECK(file_.IsValid());
280 // If there's no buffer then the CRTC was disabled.
281 if (!crtc->buffer_id)
282 return DisableCrtc(crtc->crtc_id);
284 DCHECK(!connectors.empty());
286 TRACE_EVENT1("drm", "DrmDevice::RestoreCrtc", "crtc", crtc->crtc_id);
287 return !drmModeSetCrtc(file_.GetPlatformFile(), crtc->crtc_id,
288 crtc->buffer_id, crtc->x, crtc->y,
289 vector_as_array(&connectors), connectors.size(),
290 &crtc->mode);
293 bool DrmDevice::DisableCrtc(uint32_t crtc_id) {
294 DCHECK(file_.IsValid());
295 TRACE_EVENT1("drm", "DrmDevice::DisableCrtc", "crtc", crtc_id);
296 return !drmModeSetCrtc(file_.GetPlatformFile(), crtc_id, 0, 0, 0, NULL, 0,
297 NULL);
300 ScopedDrmConnectorPtr DrmDevice::GetConnector(uint32_t connector_id) {
301 DCHECK(file_.IsValid());
302 TRACE_EVENT1("drm", "DrmDevice::GetConnector", "connector", connector_id);
303 return ScopedDrmConnectorPtr(
304 drmModeGetConnector(file_.GetPlatformFile(), connector_id));
307 bool DrmDevice::AddFramebuffer(uint32_t width,
308 uint32_t height,
309 uint8_t depth,
310 uint8_t bpp,
311 uint32_t stride,
312 uint32_t handle,
313 uint32_t* framebuffer) {
314 DCHECK(file_.IsValid());
315 TRACE_EVENT1("drm", "DrmDevice::AddFramebuffer", "handle", handle);
316 return !drmModeAddFB(file_.GetPlatformFile(), width, height, depth, bpp,
317 stride, handle, framebuffer);
320 bool DrmDevice::RemoveFramebuffer(uint32_t framebuffer) {
321 DCHECK(file_.IsValid());
322 TRACE_EVENT1("drm", "DrmDevice::RemoveFramebuffer", "framebuffer",
323 framebuffer);
324 return !drmModeRmFB(file_.GetPlatformFile(), framebuffer);
327 bool DrmDevice::PageFlip(uint32_t crtc_id,
328 uint32_t framebuffer,
329 bool is_sync,
330 const PageFlipCallback& callback) {
331 DCHECK(file_.IsValid());
332 TRACE_EVENT2("drm", "DrmDevice::PageFlip", "crtc", crtc_id, "framebuffer",
333 framebuffer);
335 if (watcher_)
336 watcher_->SetPaused(is_sync);
338 // NOTE: Calling drmModeSetCrtc will immediately update the state, though
339 // callbacks to already scheduled page flips will be honored by the kernel.
340 scoped_ptr<PageFlipPayload> payload(
341 new PageFlipPayload(base::ThreadTaskRunnerHandle::Get(), callback));
342 if (!drmModePageFlip(file_.GetPlatformFile(), crtc_id, framebuffer,
343 DRM_MODE_PAGE_FLIP_EVENT, payload.get())) {
344 // If successful the payload will be removed by a PageFlip event.
345 ignore_result(payload.release());
347 // If the flip was requested synchronous or if no watcher has been installed
348 // yet, then synchronously handle the page flip events.
349 if (is_sync || !watcher_) {
350 TRACE_EVENT1("drm", "OnDrmEvent", "socket", file_.GetPlatformFile());
352 drmEventContext event;
353 event.version = DRM_EVENT_CONTEXT_VERSION;
354 event.page_flip_handler = HandlePageFlipEventOnUI;
355 event.vblank_handler = nullptr;
357 drmHandleEvent(file_.GetPlatformFile(), &event);
360 return true;
363 return false;
366 bool DrmDevice::PageFlipOverlay(uint32_t crtc_id,
367 uint32_t framebuffer,
368 const gfx::Rect& location,
369 const gfx::Rect& source,
370 int overlay_plane) {
371 DCHECK(file_.IsValid());
372 TRACE_EVENT2("drm", "DrmDevice::PageFlipOverlay", "crtc", crtc_id,
373 "framebuffer", framebuffer);
374 return !drmModeSetPlane(file_.GetPlatformFile(), overlay_plane, crtc_id,
375 framebuffer, 0, location.x(), location.y(),
376 location.width(), location.height(), source.x(),
377 source.y(), source.width(), source.height());
380 ScopedDrmFramebufferPtr DrmDevice::GetFramebuffer(uint32_t framebuffer) {
381 DCHECK(file_.IsValid());
382 TRACE_EVENT1("drm", "DrmDevice::GetFramebuffer", "framebuffer", framebuffer);
383 return ScopedDrmFramebufferPtr(
384 drmModeGetFB(file_.GetPlatformFile(), framebuffer));
387 ScopedDrmPropertyPtr DrmDevice::GetProperty(drmModeConnector* connector,
388 const char* name) {
389 TRACE_EVENT2("drm", "DrmDevice::GetProperty", "connector",
390 connector->connector_id, "name", name);
391 for (int i = 0; i < connector->count_props; ++i) {
392 ScopedDrmPropertyPtr property(
393 drmModeGetProperty(file_.GetPlatformFile(), connector->props[i]));
394 if (!property)
395 continue;
397 if (strcmp(property->name, name) == 0)
398 return property.Pass();
401 return ScopedDrmPropertyPtr();
404 bool DrmDevice::SetProperty(uint32_t connector_id,
405 uint32_t property_id,
406 uint64_t value) {
407 DCHECK(file_.IsValid());
408 return !drmModeConnectorSetProperty(file_.GetPlatformFile(), connector_id,
409 property_id, value);
412 bool DrmDevice::GetCapability(uint64_t capability, uint64_t* value) {
413 DCHECK(file_.IsValid());
414 return !drmGetCap(file_.GetPlatformFile(), capability, value);
417 ScopedDrmPropertyBlobPtr DrmDevice::GetPropertyBlob(drmModeConnector* connector,
418 const char* name) {
419 DCHECK(file_.IsValid());
420 TRACE_EVENT2("drm", "DrmDevice::GetPropertyBlob", "connector",
421 connector->connector_id, "name", name);
422 for (int i = 0; i < connector->count_props; ++i) {
423 ScopedDrmPropertyPtr property(
424 drmModeGetProperty(file_.GetPlatformFile(), connector->props[i]));
425 if (!property)
426 continue;
428 if (strcmp(property->name, name) == 0 &&
429 property->flags & DRM_MODE_PROP_BLOB)
430 return ScopedDrmPropertyBlobPtr(drmModeGetPropertyBlob(
431 file_.GetPlatformFile(), connector->prop_values[i]));
434 return ScopedDrmPropertyBlobPtr();
437 bool DrmDevice::SetCursor(uint32_t crtc_id,
438 uint32_t handle,
439 const gfx::Size& size) {
440 DCHECK(file_.IsValid());
441 TRACE_EVENT1("drm", "DrmDevice::SetCursor", "handle", handle);
442 return !drmModeSetCursor(file_.GetPlatformFile(), crtc_id, handle,
443 size.width(), size.height());
446 bool DrmDevice::MoveCursor(uint32_t crtc_id, const gfx::Point& point) {
447 DCHECK(file_.IsValid());
448 return !drmModeMoveCursor(file_.GetPlatformFile(), crtc_id, point.x(),
449 point.y());
452 bool DrmDevice::CreateDumbBuffer(const SkImageInfo& info,
453 uint32_t* handle,
454 uint32_t* stride) {
455 DCHECK(file_.IsValid());
457 TRACE_EVENT0("drm", "DrmDevice::CreateDumbBuffer");
458 return DrmCreateDumbBuffer(file_.GetPlatformFile(), info, handle, stride);
461 bool DrmDevice::DestroyDumbBuffer(uint32_t handle) {
462 DCHECK(file_.IsValid());
463 TRACE_EVENT1("drm", "DrmDevice::DestroyDumbBuffer", "handle", handle);
464 return DrmDestroyDumbBuffer(file_.GetPlatformFile(), handle);
467 bool DrmDevice::MapDumbBuffer(uint32_t handle, size_t size, void** pixels) {
468 struct drm_mode_map_dumb map_request;
469 memset(&map_request, 0, sizeof(map_request));
470 map_request.handle = handle;
471 if (drmIoctl(file_.GetPlatformFile(), DRM_IOCTL_MODE_MAP_DUMB,
472 &map_request)) {
473 PLOG(ERROR) << "Cannot prepare dumb buffer for mapping";
474 return false;
477 *pixels = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED,
478 file_.GetPlatformFile(), map_request.offset);
479 if (*pixels == MAP_FAILED) {
480 PLOG(ERROR) << "Cannot mmap dumb buffer";
481 return false;
484 return true;
487 bool DrmDevice::UnmapDumbBuffer(void* pixels, size_t size) {
488 return !munmap(pixels, size);
491 bool DrmDevice::CloseBufferHandle(uint32_t handle) {
492 struct drm_gem_close close_request;
493 memset(&close_request, 0, sizeof(close_request));
494 close_request.handle = handle;
495 return !drmIoctl(file_.GetPlatformFile(), DRM_IOCTL_GEM_CLOSE,
496 &close_request);
499 bool DrmDevice::CommitProperties(drmModePropertySet* properties,
500 uint32_t flags,
501 const PageFlipCallback& callback) {
502 #if defined(USE_DRM_ATOMIC)
503 scoped_ptr<PageFlipPayload> payload(
504 new PageFlipPayload(base::ThreadTaskRunnerHandle::Get(), callback));
505 if (!drmModePropertySetCommit(file_.GetPlatformFile(), flags, payload.get(),
506 properties)) {
507 // If successful the payload will be removed by the event
508 ignore_result(payload.release());
509 return true;
511 return false;
512 #else
513 return false;
514 #endif // defined(USE_DRM_ATOMIC)
517 bool DrmDevice::SetMaster() {
518 DCHECK(file_.IsValid());
519 return (drmSetMaster(file_.GetPlatformFile()) == 0);
522 bool DrmDevice::DropMaster() {
523 DCHECK(file_.IsValid());
524 return (drmDropMaster(file_.GetPlatformFile()) == 0);
527 } // namespace ui