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_native_display_delegate.h"
9 #include "base/logging.h"
10 #include "base/thread_task_runner_handle.h"
11 #include "base/threading/thread_restrictions.h"
12 #include "base/threading/worker_pool.h"
13 #include "ui/display/types/display_snapshot.h"
14 #include "ui/display/types/native_display_observer.h"
15 #include "ui/events/ozone/device/device_event.h"
16 #include "ui/events/ozone/device/device_manager.h"
17 #include "ui/ozone/common/display_snapshot_proxy.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/host/display_manager.h"
21 #include "ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h"
27 typedef base::Callback
<void(const base::FilePath
&, base::File
)>
28 OnOpenDeviceReplyCallback
;
30 void OpenDeviceOnWorkerThread(
31 const base::FilePath
& path
,
32 const scoped_refptr
<base::TaskRunner
>& reply_runner
,
33 const OnOpenDeviceReplyCallback
& callback
) {
34 base::File
file(path
, base::File::FLAG_OPEN
| base::File::FLAG_READ
|
35 base::File::FLAG_WRITE
);
37 base::File::Info info
;
40 CHECK(!info
.is_directory
);
41 CHECK(path
.DirName() == base::FilePath("/dev/dri"));
44 reply_runner
->PostTask(
45 FROM_HERE
, base::Bind(callback
, path
, base::Passed(file
.Pass())));
49 class DrmDisplaySnapshotProxy
: public DisplaySnapshotProxy
{
51 DrmDisplaySnapshotProxy(const DisplaySnapshot_Params
& params
,
52 DisplayManager
* display_manager
)
53 : DisplaySnapshotProxy(params
), display_manager_(display_manager
) {
54 display_manager_
->RegisterDisplay(this);
57 ~DrmDisplaySnapshotProxy() override
{
58 display_manager_
->UnregisterDisplay(this);
62 DisplayManager
* display_manager_
; // Not owned.
64 DISALLOW_COPY_AND_ASSIGN(DrmDisplaySnapshotProxy
);
69 DrmNativeDisplayDelegate::DrmNativeDisplayDelegate(
70 DrmGpuPlatformSupportHost
* proxy
,
71 DeviceManager
* device_manager
,
72 DisplayManager
* display_manager
,
73 const base::FilePath
& primary_graphics_card_path
)
75 device_manager_(device_manager
),
76 display_manager_(display_manager
),
77 primary_graphics_card_path_(primary_graphics_card_path
),
78 has_dummy_display_(false),
79 weak_ptr_factory_(this) {
80 proxy_
->RegisterHandler(this);
83 DrmNativeDisplayDelegate::~DrmNativeDisplayDelegate() {
84 device_manager_
->RemoveObserver(this);
85 proxy_
->UnregisterHandler(this);
88 void DrmNativeDisplayDelegate::Initialize() {
89 device_manager_
->AddObserver(this);
90 device_manager_
->ScanDevices(this);
92 if (!displays_
.empty())
94 DisplaySnapshot_Params params
;
97 // The file generated by frecon that contains EDID for the 1st display.
98 const base::FilePath
kEDIDFile("/tmp/display_info.bin");
100 // Just read it on current thread as this is necessary information
101 // to start. This access only tmpfs, which is fast.
102 // TODO(dnicoara|oshima): crbug.com/450886.
103 base::ThreadRestrictions::ScopedAllowIO allow_io
;
104 success
= CreateSnapshotFromEDIDFile(kEDIDFile
, ¶ms
);
107 // Fallback to command line if the file doesn't exit or failed to read.
108 if (success
|| CreateSnapshotFromCommandLine(¶ms
)) {
109 LOG_IF(ERROR
, !success
) << "Failed to read display_info.bin.";
110 DCHECK_NE(DISPLAY_CONNECTION_TYPE_NONE
, params
.type
);
111 displays_
.push_back(new DrmDisplaySnapshotProxy(params
, display_manager_
));
112 has_dummy_display_
= true;
114 LOG(ERROR
) << "Failed to obtain initial display info";
118 void DrmNativeDisplayDelegate::GrabServer() {
121 void DrmNativeDisplayDelegate::UngrabServer() {
124 bool DrmNativeDisplayDelegate::TakeDisplayControl() {
125 proxy_
->Send(new OzoneGpuMsg_TakeDisplayControl());
129 bool DrmNativeDisplayDelegate::RelinquishDisplayControl() {
130 proxy_
->Send(new OzoneGpuMsg_RelinquishDisplayControl());
134 void DrmNativeDisplayDelegate::SyncWithServer() {
137 void DrmNativeDisplayDelegate::SetBackgroundColor(uint32_t color_argb
) {
141 void DrmNativeDisplayDelegate::ForceDPMSOn() {
144 void DrmNativeDisplayDelegate::GetDisplays(
145 const GetDisplaysCallback
& callback
) {
146 get_displays_callback_
= callback
;
147 // GetDisplays() is supposed to force a refresh of the display list.
148 if (!proxy_
->Send(new OzoneGpuMsg_RefreshNativeDisplays())) {
149 get_displays_callback_
.Reset();
150 callback
.Run(displays_
.get());
154 void DrmNativeDisplayDelegate::AddMode(const DisplaySnapshot
& output
,
155 const DisplayMode
* mode
) {
158 void DrmNativeDisplayDelegate::Configure(const DisplaySnapshot
& output
,
159 const DisplayMode
* mode
,
160 const gfx::Point
& origin
,
161 const ConfigureCallback
& callback
) {
162 // The dummy display is used on the first run only. Note: cannot post a task
163 // here since there is no task runner.
164 if (has_dummy_display_
) {
169 configure_callback_map_
[output
.display_id()] = callback
;
173 status
= proxy_
->Send(new OzoneGpuMsg_ConfigureNativeDisplay(
174 output
.display_id(), GetDisplayModeParams(*mode
), origin
));
177 proxy_
->Send(new OzoneGpuMsg_DisableNativeDisplay(output
.display_id()));
181 OnDisplayConfigured(output
.display_id(), false);
184 void DrmNativeDisplayDelegate::CreateFrameBuffer(const gfx::Size
& size
) {
187 bool DrmNativeDisplayDelegate::GetHDCPState(const DisplaySnapshot
& output
,
193 bool DrmNativeDisplayDelegate::SetHDCPState(const DisplaySnapshot
& output
,
199 std::vector
<ColorCalibrationProfile
>
200 DrmNativeDisplayDelegate::GetAvailableColorCalibrationProfiles(
201 const DisplaySnapshot
& output
) {
203 return std::vector
<ColorCalibrationProfile
>();
206 bool DrmNativeDisplayDelegate::SetColorCalibrationProfile(
207 const DisplaySnapshot
& output
,
208 ColorCalibrationProfile new_profile
) {
213 void DrmNativeDisplayDelegate::AddObserver(NativeDisplayObserver
* observer
) {
214 observers_
.AddObserver(observer
);
217 void DrmNativeDisplayDelegate::RemoveObserver(NativeDisplayObserver
* observer
) {
218 observers_
.RemoveObserver(observer
);
221 void DrmNativeDisplayDelegate::OnDeviceEvent(const DeviceEvent
& event
) {
222 if (event
.device_type() != DeviceEvent::DISPLAY
)
225 switch (event
.action_type()) {
226 case DeviceEvent::ADD
:
227 VLOG(1) << "Got display added event for " << event
.path().value();
228 // The default card is a special case since it needs to be opened early on
229 // the GPU process in order to initialize EGL. If it is opened here as
230 // well, it will cause a race with opening it in the GPU process and the
231 // GPU process may fail initialization.
232 // TODO(dnicoara) Remove this when the media stack does not require super
233 // early initialization.
234 if (event
.path() == primary_graphics_card_path_
)
237 base::WorkerPool::PostTask(
239 base::Bind(&OpenDeviceOnWorkerThread
, event
.path(),
240 base::ThreadTaskRunnerHandle::Get(),
241 base::Bind(&DrmNativeDisplayDelegate::OnNewGraphicsDevice
,
242 weak_ptr_factory_
.GetWeakPtr())),
243 false /* task_is_slow */);
245 case DeviceEvent::CHANGE
:
246 VLOG(1) << "Got display changed event for " << event
.path().value();
248 case DeviceEvent::REMOVE
:
249 VLOG(1) << "Got display removed event for " << event
.path().value();
250 // It shouldn't be possible to remove this device.
251 DCHECK(primary_graphics_card_path_
!= event
.path())
252 << "Got event to remove primary graphics card "
253 << event
.path().value();
254 proxy_
->Send(new OzoneGpuMsg_RemoveGraphicsDevice(event
.path()));
258 FOR_EACH_OBSERVER(NativeDisplayObserver
, observers_
,
259 OnConfigurationChanged());
262 void DrmNativeDisplayDelegate::OnNewGraphicsDevice(const base::FilePath
& path
,
264 DCHECK(file
.IsValid());
265 proxy_
->Send(new OzoneGpuMsg_AddGraphicsDevice(
266 path
, base::FileDescriptor(file
.Pass())));
268 FOR_EACH_OBSERVER(NativeDisplayObserver
, observers_
,
269 OnConfigurationChanged());
272 void DrmNativeDisplayDelegate::OnChannelEstablished(
274 scoped_refptr
<base::SingleThreadTaskRunner
> send_runner
,
275 const base::Callback
<void(IPC::Message
*)>& send_callback
) {
276 device_manager_
->ScanDevices(this);
277 FOR_EACH_OBSERVER(NativeDisplayObserver
, observers_
,
278 OnConfigurationChanged());
281 void DrmNativeDisplayDelegate::OnChannelDestroyed(int host_id
) {
282 // If the channel got destroyed in the middle of a configuration then just
283 // respond with failure.
284 if (!get_displays_callback_
.is_null()) {
285 base::ThreadTaskRunnerHandle::Get()->PostTask(
287 base::Bind(&DrmNativeDisplayDelegate::RunUpdateDisplaysCallback
,
288 weak_ptr_factory_
.GetWeakPtr(), get_displays_callback_
));
289 get_displays_callback_
.Reset();
292 for (const auto& pair
: configure_callback_map_
) {
293 base::ThreadTaskRunnerHandle::Get()->PostTask(
294 FROM_HERE
, base::Bind(pair
.second
, false));
296 configure_callback_map_
.clear();
299 bool DrmNativeDisplayDelegate::OnMessageReceived(const IPC::Message
& message
) {
302 IPC_BEGIN_MESSAGE_MAP(DrmNativeDisplayDelegate
, message
)
303 IPC_MESSAGE_HANDLER(OzoneHostMsg_UpdateNativeDisplays
, OnUpdateNativeDisplays
)
304 IPC_MESSAGE_HANDLER(OzoneHostMsg_DisplayConfigured
, OnDisplayConfigured
)
305 IPC_MESSAGE_UNHANDLED(handled
= false)
306 IPC_END_MESSAGE_MAP()
311 void DrmNativeDisplayDelegate::OnUpdateNativeDisplays(
312 const std::vector
<DisplaySnapshot_Params
>& displays
) {
313 has_dummy_display_
= false;
315 for (size_t i
= 0; i
< displays
.size(); ++i
)
317 new DrmDisplaySnapshotProxy(displays
[i
], display_manager_
));
319 if (!get_displays_callback_
.is_null()) {
320 base::ThreadTaskRunnerHandle::Get()->PostTask(
322 base::Bind(&DrmNativeDisplayDelegate::RunUpdateDisplaysCallback
,
323 weak_ptr_factory_
.GetWeakPtr(), get_displays_callback_
));
324 get_displays_callback_
.Reset();
328 void DrmNativeDisplayDelegate::OnDisplayConfigured(int64_t display_id
,
330 auto it
= configure_callback_map_
.find(display_id
);
331 if (it
!= configure_callback_map_
.end()) {
332 base::ThreadTaskRunnerHandle::Get()->PostTask(
333 FROM_HERE
, base::Bind(it
->second
, status
));
334 configure_callback_map_
.erase(it
);
338 void DrmNativeDisplayDelegate::RunUpdateDisplaysCallback(
339 const GetDisplaysCallback
& callback
) const {
340 callback
.Run(displays_
.get());