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/host/drm_display_host_manager.h"
10 #include "base/files/file_enumerator.h"
11 #include "base/posix/eintr_wrapper.h"
12 #include "base/thread_task_runner_handle.h"
13 #include "base/threading/thread_restrictions.h"
14 #include "base/threading/worker_pool.h"
15 #include "ui/display/types/display_snapshot.h"
16 #include "ui/events/ozone/device/device_event.h"
17 #include "ui/events/ozone/device/device_manager.h"
18 #include "ui/ozone/common/display_util.h"
19 #include "ui/ozone/common/gpu/ozone_gpu_messages.h"
20 #include "ui/ozone/platform/drm/common/drm_util.h"
21 #include "ui/ozone/platform/drm/host/drm_device_handle.h"
22 #include "ui/ozone/platform/drm/host/drm_display_host.h"
23 #include "ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h"
24 #include "ui/ozone/platform/drm/host/drm_native_display_delegate.h"
30 typedef base::Callback
<void(const base::FilePath
&, scoped_ptr
<DrmDeviceHandle
>)>
31 OnOpenDeviceReplyCallback
;
33 const char kDefaultGraphicsCardPattern
[] = "/dev/dri/card%d";
34 const char kVgemDevDriCardPath
[] = "/dev/dri/";
35 const char kVgemSysCardPath
[] = "/sys/bus/platform/devices/vgem/drm/";
37 const char* kDisplayActionString
[] = {
43 void OpenDeviceOnWorkerThread(
44 const base::FilePath
& path
,
45 const scoped_refptr
<base::TaskRunner
>& reply_runner
,
46 const OnOpenDeviceReplyCallback
& callback
) {
47 scoped_ptr
<DrmDeviceHandle
> handle(new DrmDeviceHandle());
48 handle
->Initialize(path
);
49 reply_runner
->PostTask(
50 FROM_HERE
, base::Bind(callback
, path
, base::Passed(handle
.Pass())));
53 base::FilePath
GetPrimaryDisplayCardPath() {
54 struct drm_mode_card_res res
;
55 for (int i
= 0; /* end on first card# that does not exist */; i
++) {
56 std::string card_path
= base::StringPrintf(kDefaultGraphicsCardPattern
, i
);
58 if (access(card_path
.c_str(), F_OK
) != 0)
61 int fd
= open(card_path
.c_str(), O_RDWR
| O_CLOEXEC
);
63 VPLOG(1) << "Failed to open '" << card_path
<< "'";
67 memset(&res
, 0, sizeof(struct drm_mode_card_res
));
68 int ret
= drmIoctl(fd
, DRM_IOCTL_MODE_GETRESOURCES
, &res
);
70 if (ret
== 0 && res
.count_crtcs
> 0) {
71 return base::FilePath(card_path
);
74 VPLOG_IF(1, ret
) << "Failed to get DRM resources for '" << card_path
<< "'";
77 return base::FilePath();
80 base::FilePath
GetVgemCardPath() {
81 base::FileEnumerator
file_iter(base::FilePath(kVgemSysCardPath
), false,
82 base::FileEnumerator::DIRECTORIES
,
83 FILE_PATH_LITERAL("card*"));
85 while (!file_iter
.Next().empty()) {
86 // Inspect the card%d directories in the directory and extract the filename.
87 std::string vgem_card_path
=
88 kVgemDevDriCardPath
+ file_iter
.GetInfo().GetName().BaseName().value();
89 DVLOG(1) << "VGEM card path is " << vgem_card_path
;
90 return base::FilePath(vgem_card_path
);
92 DVLOG(1) << "Don't support VGEM";
93 return base::FilePath();
96 class FindDrmDisplayHostById
{
98 explicit FindDrmDisplayHostById(int64_t display_id
)
99 : display_id_(display_id
) {}
101 bool operator()(const DrmDisplayHost
* display
) const {
102 return display
->snapshot()->display_id() == display_id_
;
111 DrmDisplayHostManager::DrmDisplayHostManager(
112 DrmGpuPlatformSupportHost
* proxy
,
113 DeviceManager
* device_manager
,
114 InputControllerEvdev
* input_controller
)
116 device_manager_(device_manager
),
117 input_controller_(input_controller
),
118 primary_graphics_card_path_(GetPrimaryDisplayCardPath()),
119 weak_ptr_factory_(this) {
121 // First device needs to be treated specially. We need to open this
122 // synchronously since the GPU process will need it to initialize the
124 base::ThreadRestrictions::ScopedAllowIO allow_io
;
125 primary_drm_device_handle_
.reset(new DrmDeviceHandle());
126 if (!primary_drm_device_handle_
->Initialize(primary_graphics_card_path_
)) {
127 LOG(FATAL
) << "Failed to open primary graphics card";
130 drm_devices_
.insert(primary_graphics_card_path_
);
132 vgem_card_path_
= GetVgemCardPath();
133 if (!vgem_card_path_
.empty()) {
134 int fd
= HANDLE_EINTR(
135 open(vgem_card_path_
.value().c_str(), O_RDWR
| O_CLOEXEC
));
137 PLOG(ERROR
) << "Failed to open vgem: " << vgem_card_path_
.value();
139 vgem_card_device_file_
.reset(fd
);
143 device_manager_
->AddObserver(this);
144 proxy_
->RegisterHandler(this);
146 ScopedVector
<HardwareDisplayControllerInfo
> display_infos
=
147 GetAvailableDisplayControllerInfos(primary_drm_device_handle_
->fd());
148 has_dummy_display_
= !display_infos
.empty();
149 for (size_t i
= 0; i
< display_infos
.size(); ++i
) {
150 displays_
.push_back(new DrmDisplayHost(
151 proxy_
, CreateDisplaySnapshotParams(display_infos
[i
],
152 primary_drm_device_handle_
->fd(), i
,
154 true /* is_dummy */));
158 DrmDisplayHostManager::~DrmDisplayHostManager() {
159 device_manager_
->RemoveObserver(this);
160 proxy_
->UnregisterHandler(this);
163 DrmDisplayHost
* DrmDisplayHostManager::GetDisplay(int64_t display_id
) {
164 auto it
= std::find_if(displays_
.begin(), displays_
.end(),
165 FindDrmDisplayHostById(display_id
));
166 if (it
== displays_
.end())
172 void DrmDisplayHostManager::AddDelegate(DrmNativeDisplayDelegate
* delegate
) {
174 delegate_
= delegate
;
177 void DrmDisplayHostManager::RemoveDelegate(DrmNativeDisplayDelegate
* delegate
) {
178 DCHECK_EQ(delegate_
, delegate
);
182 void DrmDisplayHostManager::TakeDisplayControl(
183 const DisplayControlCallback
& callback
) {
184 if (display_control_change_pending_
) {
185 LOG(ERROR
) << "TakeDisplayControl called while change already pending";
190 if (!display_externally_controlled_
) {
191 LOG(ERROR
) << "TakeDisplayControl called while display already owned";
196 take_display_control_callback_
= callback
;
197 display_control_change_pending_
= true;
199 if (!proxy_
->Send(new OzoneGpuMsg_TakeDisplayControl()))
200 OnTakeDisplayControl(false);
203 void DrmDisplayHostManager::RelinquishDisplayControl(
204 const DisplayControlCallback
& callback
) {
205 if (display_control_change_pending_
) {
207 << "RelinquishDisplayControl called while change already pending";
212 if (display_externally_controlled_
) {
213 LOG(ERROR
) << "RelinquishDisplayControl called while display not owned";
218 relinquish_display_control_callback_
= callback
;
219 display_control_change_pending_
= true;
221 if (!proxy_
->Send(new OzoneGpuMsg_RelinquishDisplayControl()))
222 OnRelinquishDisplayControl(false);
225 void DrmDisplayHostManager::UpdateDisplays(
226 const GetDisplaysCallback
& callback
) {
227 get_displays_callback_
= callback
;
228 if (!proxy_
->Send(new OzoneGpuMsg_RefreshNativeDisplays())) {
229 get_displays_callback_
.Reset();
230 RunUpdateDisplaysCallback(callback
);
234 void DrmDisplayHostManager::OnDeviceEvent(const DeviceEvent
& event
) {
235 if (event
.device_type() != DeviceEvent::DISPLAY
)
238 event_queue_
.push(DisplayEvent(event
.action_type(), event
.path()));
242 void DrmDisplayHostManager::ProcessEvent() {
243 while (!event_queue_
.empty() && !task_pending_
) {
244 DisplayEvent event
= event_queue_
.front();
246 VLOG(1) << "Got display event " << kDisplayActionString
[event
.action_type
]
247 << " for " << event
.path
.value();
248 switch (event
.action_type
) {
249 case DeviceEvent::ADD
:
250 if (event
.path
== vgem_card_path_
)
252 if (drm_devices_
.find(event
.path
) == drm_devices_
.end()) {
253 task_pending_
= base::WorkerPool::PostTask(
255 base::Bind(&OpenDeviceOnWorkerThread
, event
.path
,
256 base::ThreadTaskRunnerHandle::Get(),
257 base::Bind(&DrmDisplayHostManager::OnAddGraphicsDevice
,
258 weak_ptr_factory_
.GetWeakPtr())),
259 false /* task_is_slow */);
262 case DeviceEvent::CHANGE
:
263 task_pending_
= base::ThreadTaskRunnerHandle::Get()->PostTask(
265 base::Bind(&DrmDisplayHostManager::OnUpdateGraphicsDevice
,
266 weak_ptr_factory_
.GetWeakPtr()));
268 case DeviceEvent::REMOVE
:
269 DCHECK(event
.path
!= primary_graphics_card_path_
)
270 << "Removing primary graphics card";
271 DCHECK(event
.path
!= vgem_card_path_
) << "Removing VGEM device";
272 auto it
= drm_devices_
.find(event
.path
);
273 if (it
!= drm_devices_
.end()) {
274 task_pending_
= base::ThreadTaskRunnerHandle::Get()->PostTask(
276 base::Bind(&DrmDisplayHostManager::OnRemoveGraphicsDevice
,
277 weak_ptr_factory_
.GetWeakPtr(), event
.path
));
278 drm_devices_
.erase(it
);
285 void DrmDisplayHostManager::OnAddGraphicsDevice(
286 const base::FilePath
& path
,
287 scoped_ptr
<DrmDeviceHandle
> handle
) {
288 if (handle
->IsValid()) {
289 drm_devices_
.insert(path
);
290 proxy_
->Send(new OzoneGpuMsg_AddGraphicsDevice(
291 path
, base::FileDescriptor(handle
->PassFD())));
292 NotifyDisplayDelegate();
295 task_pending_
= false;
299 void DrmDisplayHostManager::OnUpdateGraphicsDevice() {
300 NotifyDisplayDelegate();
301 task_pending_
= false;
305 void DrmDisplayHostManager::OnRemoveGraphicsDevice(const base::FilePath
& path
) {
306 proxy_
->Send(new OzoneGpuMsg_RemoveGraphicsDevice(path
));
307 NotifyDisplayDelegate();
308 task_pending_
= false;
312 void DrmDisplayHostManager::OnChannelEstablished(
314 scoped_refptr
<base::SingleThreadTaskRunner
> send_runner
,
315 const base::Callback
<void(IPC::Message
*)>& send_callback
) {
316 // If in the middle of a configuration, just respond with the old list of
317 // displays. This is fine, since after the DRM resources are initialized and
318 // IPC-ed to the GPU NotifyDisplayDelegate() is called to let the display
319 // delegate know that the display configuration changed and it needs to
321 if (!get_displays_callback_
.is_null()) {
322 base::ThreadTaskRunnerHandle::Get()->PostTask(
324 base::Bind(&DrmDisplayHostManager::RunUpdateDisplaysCallback
,
325 weak_ptr_factory_
.GetWeakPtr(), get_displays_callback_
));
326 get_displays_callback_
.Reset();
329 // Signal that we're taking DRM master since we're going through the
330 // initialization process again and we'll take all the available resources.
331 if (!take_display_control_callback_
.is_null())
332 OnTakeDisplayControl(true);
334 if (!relinquish_display_control_callback_
.is_null())
335 OnRelinquishDisplayControl(false);
337 drm_devices_
.clear();
338 drm_devices_
.insert(primary_graphics_card_path_
);
339 scoped_ptr
<DrmDeviceHandle
> handle
= primary_drm_device_handle_
.Pass();
341 base::ThreadRestrictions::ScopedAllowIO allow_io
;
342 handle
.reset(new DrmDeviceHandle());
343 if (!handle
->Initialize(primary_graphics_card_path_
))
344 LOG(FATAL
) << "Failed to open primary graphics card";
347 // Send the primary device first since this is used to initialize graphics
349 proxy_
->Send(new OzoneGpuMsg_AddGraphicsDevice(
350 primary_graphics_card_path_
, base::FileDescriptor(handle
->PassFD())));
352 device_manager_
->ScanDevices(this);
353 NotifyDisplayDelegate();
356 void DrmDisplayHostManager::OnChannelDestroyed(int host_id
) {
359 bool DrmDisplayHostManager::OnMessageReceived(const IPC::Message
& message
) {
362 IPC_BEGIN_MESSAGE_MAP(DrmDisplayHostManager
, message
)
363 IPC_MESSAGE_HANDLER(OzoneHostMsg_UpdateNativeDisplays
, OnUpdateNativeDisplays
)
364 IPC_MESSAGE_HANDLER(OzoneHostMsg_DisplayConfigured
, OnDisplayConfigured
)
365 IPC_MESSAGE_HANDLER(OzoneHostMsg_HDCPStateReceived
, OnHDCPStateReceived
)
366 IPC_MESSAGE_HANDLER(OzoneHostMsg_HDCPStateUpdated
, OnHDCPStateUpdated
)
367 IPC_MESSAGE_HANDLER(OzoneHostMsg_DisplayControlTaken
, OnTakeDisplayControl
)
368 IPC_MESSAGE_HANDLER(OzoneHostMsg_DisplayControlRelinquished
,
369 OnRelinquishDisplayControl
)
370 IPC_MESSAGE_UNHANDLED(handled
= false)
371 IPC_END_MESSAGE_MAP()
376 void DrmDisplayHostManager::OnUpdateNativeDisplays(
377 const std::vector
<DisplaySnapshot_Params
>& params
) {
378 ScopedVector
<DrmDisplayHost
> old_displays(displays_
.Pass());
379 for (size_t i
= 0; i
< params
.size(); ++i
) {
380 auto it
= std::find_if(old_displays
.begin(), old_displays
.end(),
381 FindDrmDisplayHostById(params
[i
].display_id
));
382 if (it
== old_displays
.end()) {
384 new DrmDisplayHost(proxy_
, params
[i
], false /* is_dummy */));
386 (*it
)->UpdateDisplaySnapshot(params
[i
]);
387 displays_
.push_back(*it
);
388 old_displays
.weak_erase(it
);
392 if (!get_displays_callback_
.is_null()) {
393 base::ThreadTaskRunnerHandle::Get()->PostTask(
395 base::Bind(&DrmDisplayHostManager::RunUpdateDisplaysCallback
,
396 weak_ptr_factory_
.GetWeakPtr(), get_displays_callback_
));
397 get_displays_callback_
.Reset();
401 void DrmDisplayHostManager::OnDisplayConfigured(int64_t display_id
,
403 DrmDisplayHost
* display
= GetDisplay(display_id
);
405 display
->OnDisplayConfigured(status
);
407 LOG(ERROR
) << "Couldn't find display with id=" << display_id
;
410 void DrmDisplayHostManager::OnHDCPStateReceived(int64_t display_id
,
413 DrmDisplayHost
* display
= GetDisplay(display_id
);
415 display
->OnHDCPStateReceived(status
, state
);
417 LOG(ERROR
) << "Couldn't find display with id=" << display_id
;
420 void DrmDisplayHostManager::OnHDCPStateUpdated(int64_t display_id
,
422 DrmDisplayHost
* display
= GetDisplay(display_id
);
424 display
->OnHDCPStateUpdated(status
);
426 LOG(ERROR
) << "Couldn't find display with id=" << display_id
;
429 void DrmDisplayHostManager::OnTakeDisplayControl(bool status
) {
430 if (take_display_control_callback_
.is_null()) {
431 LOG(ERROR
) << "No callback for take display control";
435 DCHECK(display_externally_controlled_
);
436 DCHECK(display_control_change_pending_
);
439 input_controller_
->SetInputDevicesEnabled(true);
440 display_externally_controlled_
= false;
443 base::ThreadTaskRunnerHandle::Get()->PostTask(
444 FROM_HERE
, base::Bind(take_display_control_callback_
, status
));
445 take_display_control_callback_
.Reset();
446 display_control_change_pending_
= false;
449 void DrmDisplayHostManager::OnRelinquishDisplayControl(bool status
) {
450 if (relinquish_display_control_callback_
.is_null()) {
451 LOG(ERROR
) << "No callback for relinquish display control";
455 DCHECK(!display_externally_controlled_
);
456 DCHECK(display_control_change_pending_
);
459 input_controller_
->SetInputDevicesEnabled(false);
460 display_externally_controlled_
= true;
463 base::ThreadTaskRunnerHandle::Get()->PostTask(
464 FROM_HERE
, base::Bind(relinquish_display_control_callback_
, status
));
465 relinquish_display_control_callback_
.Reset();
466 display_control_change_pending_
= false;
469 void DrmDisplayHostManager::RunUpdateDisplaysCallback(
470 const GetDisplaysCallback
& callback
) const {
471 std::vector
<DisplaySnapshot
*> snapshots
;
472 for (auto* display
: displays_
)
473 snapshots
.push_back(display
->snapshot());
475 callback
.Run(snapshots
);
478 void DrmDisplayHostManager::NotifyDisplayDelegate() const {
480 delegate_
->OnConfigurationChanged();