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"
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"
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
,
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;
54 if (drmIoctl(fd
, DRM_IOCTL_MODE_CREATE_DUMB
, &request
) < 0) {
55 VPLOG(2) << "Cannot create dumb buffer";
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
;
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
,
79 unsigned int useconds
,
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
,
89 unsigned int useconds
,
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
) {
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
);
113 class DrmDevice::IOWatcher
114 : public base::RefCountedThreadSafe
<DrmDevice::IOWatcher
>,
115 public base::MessagePumpLibevent::Watcher
{
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
)
126 base::WaitableEvent
done(false, false);
127 io_task_runner_
->PostTask(
128 FROM_HERE
, base::Bind(&IOWatcher::SetPausedOnIO
, this, &done
));
134 io_task_runner_
->PostTask(FROM_HERE
,
135 base::Bind(&IOWatcher::UnregisterOnIO
, this));
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());
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_
;
185 DISALLOW_COPY_AND_ASSIGN(IOWatcher
);
188 DrmDevice::DrmDevice(const base::FilePath
& device_path
)
189 : device_path_(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() {
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()
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;
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());
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();
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());
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(),
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,
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
,
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",
324 return !drmModeRmFB(file_
.GetPlatformFile(), framebuffer
);
327 bool DrmDevice::PageFlip(uint32_t crtc_id
,
328 uint32_t framebuffer
,
330 const PageFlipCallback
& callback
) {
331 DCHECK(file_
.IsValid());
332 TRACE_EVENT2("drm", "DrmDevice::PageFlip", "crtc", crtc_id
, "framebuffer",
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
);
366 bool DrmDevice::PageFlipOverlay(uint32_t crtc_id
,
367 uint32_t framebuffer
,
368 const gfx::Rect
& location
,
369 const gfx::Rect
& source
,
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
,
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
]));
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
,
407 DCHECK(file_
.IsValid());
408 return !drmModeConnectorSetProperty(file_
.GetPlatformFile(), connector_id
,
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
,
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
]));
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
,
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(),
452 bool DrmDevice::CreateDumbBuffer(const SkImageInfo
& info
,
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
,
473 PLOG(ERROR
) << "Cannot prepare dumb buffer for mapping";
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";
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
,
499 bool DrmDevice::CommitProperties(drmModePropertySet
* properties
,
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(),
507 // If successful the payload will be removed by the event
508 ignore_result(payload
.release());
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);