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_gpu_display_manager.h"
8 #include "base/command_line.h"
9 #include "base/file_descriptor_posix.h"
10 #include "base/files/file.h"
11 #include "base/single_thread_task_runner.h"
12 #include "ui/display/types/native_display_observer.h"
13 #include "ui/events/ozone/device/device_event.h"
14 #include "ui/ozone/common/display_util.h"
15 #include "ui/ozone/platform/drm/gpu/drm_device.h"
16 #include "ui/ozone/platform/drm/gpu/drm_device_generator.h"
17 #include "ui/ozone/platform/drm/gpu/drm_display_mode.h"
18 #include "ui/ozone/platform/drm/gpu/drm_display_snapshot.h"
19 #include "ui/ozone/platform/drm/gpu/drm_util.h"
20 #include "ui/ozone/platform/drm/gpu/screen_manager.h"
21 #include "ui/ozone/public/ozone_switches.h"
27 const char kContentProtection
[] = "Content Protection";
29 struct ContentProtectionMapping
{
34 const ContentProtectionMapping kContentProtectionStates
[] = {
35 {"Undesired", HDCP_STATE_UNDESIRED
},
36 {"Desired", HDCP_STATE_DESIRED
},
37 {"Enabled", HDCP_STATE_ENABLED
}};
39 uint32_t GetContentProtectionValue(drmModePropertyRes
* property
,
42 for (size_t i
= 0; i
< arraysize(kContentProtectionStates
); ++i
) {
43 if (kContentProtectionStates
[i
].state
== state
) {
44 name
= kContentProtectionStates
[i
].name
;
49 for (int i
= 0; i
< property
->count_enums
; ++i
)
50 if (name
== property
->enums
[i
].name
)
57 class DisplaySnapshotComparator
{
59 explicit DisplaySnapshotComparator(const DrmDisplaySnapshot
* snapshot
)
60 : drm_(snapshot
->drm()),
61 crtc_(snapshot
->crtc()),
62 connector_(snapshot
->connector()) {}
64 DisplaySnapshotComparator(const scoped_refptr
<DrmDevice
>& drm
,
67 : drm_(drm
), crtc_(crtc
), connector_(connector
) {}
69 bool operator()(const DrmDisplaySnapshot
* other
) const {
70 return drm_
== other
->drm() && connector_
== other
->connector() &&
71 crtc_
== other
->crtc();
75 scoped_refptr
<DrmDevice
> drm_
;
80 class FindByDevicePath
{
82 explicit FindByDevicePath(const base::FilePath
& path
) : path_(path
) {}
84 bool operator()(const scoped_refptr
<DrmDevice
>& device
) {
85 return device
->device_path() == path_
;
92 std::string
GetEnumNameForProperty(drmModeConnector
* connector
,
93 drmModePropertyRes
* property
) {
94 for (int prop_idx
= 0; prop_idx
< connector
->count_props
; ++prop_idx
) {
95 if (connector
->props
[prop_idx
] != property
->prop_id
)
98 for (int enum_idx
= 0; enum_idx
< property
->count_enums
; ++enum_idx
) {
99 const drm_mode_property_enum
& property_enum
= property
->enums
[enum_idx
];
100 if (property_enum
.value
== connector
->prop_values
[prop_idx
])
101 return property_enum
.name
;
106 return std::string();
111 DrmGpuDisplayManager::DrmGpuDisplayManager(
112 ScreenManager
* screen_manager
,
113 const scoped_refptr
<DrmDevice
>& primary_device
,
114 scoped_ptr
<DrmDeviceGenerator
> drm_device_generator
)
115 : screen_manager_(screen_manager
),
116 drm_device_generator_(drm_device_generator
.Pass()) {
117 devices_
.push_back(primary_device
);
120 DrmGpuDisplayManager::~DrmGpuDisplayManager() {
123 void DrmGpuDisplayManager::InitializeIOTaskRunner(
124 const scoped_refptr
<base::SingleThreadTaskRunner
>& task_runner
) {
125 DCHECK(!io_task_runner_
);
126 base::CommandLine
* cmd
= base::CommandLine::ForCurrentProcess();
127 // If not surfaceless, there isn't support for async page flips.
128 if (!cmd
->HasSwitch(switches::kOzoneUseSurfaceless
))
131 io_task_runner_
= task_runner
;
132 for (const auto& device
: devices_
)
133 device
->InitializeTaskRunner(io_task_runner_
);
136 std::vector
<DisplaySnapshot_Params
> DrmGpuDisplayManager::GetDisplays() {
137 RefreshDisplayList();
139 std::vector
<DisplaySnapshot_Params
> displays
;
140 for (size_t i
= 0; i
< cached_displays_
.size(); ++i
)
141 displays
.push_back(GetDisplaySnapshotParams(*cached_displays_
[i
]));
146 bool DrmGpuDisplayManager::ConfigureDisplay(
148 const DisplayMode_Params
& mode_param
,
149 const gfx::Point
& origin
) {
150 DrmDisplaySnapshot
* display
= FindDisplaySnapshot(id
);
152 LOG(ERROR
) << "There is no display with ID " << id
;
156 const DisplayMode
* mode
= NULL
;
157 for (size_t i
= 0; i
< display
->modes().size(); ++i
) {
158 if (mode_param
.size
== display
->modes()[i
]->size() &&
159 mode_param
.is_interlaced
== display
->modes()[i
]->is_interlaced() &&
160 mode_param
.refresh_rate
== display
->modes()[i
]->refresh_rate()) {
161 mode
= display
->modes()[i
];
166 // If the display doesn't have the mode natively, then lookup the mode from
167 // other displays and try using it on the current display (some displays
168 // support panel fitting and they can use different modes even if the mode
169 // isn't explicitly declared).
171 mode
= FindDisplayMode(mode_param
.size
, mode_param
.is_interlaced
,
172 mode_param
.refresh_rate
);
175 LOG(ERROR
) << "Failed to find mode: size=" << mode_param
.size
.ToString()
176 << " is_interlaced=" << mode_param
.is_interlaced
177 << " refresh_rate=" << mode_param
.refresh_rate
;
182 Configure(*display
, static_cast<const DrmDisplayMode
*>(mode
), origin
);
184 display
->set_origin(origin
);
185 display
->set_current_mode(mode
);
191 bool DrmGpuDisplayManager::DisableDisplay(int64_t id
) {
192 DrmDisplaySnapshot
* display
= FindDisplaySnapshot(id
);
193 bool success
= false;
195 success
= Configure(*display
, NULL
, gfx::Point());
197 LOG(ERROR
) << "There is no display with ID " << id
;
202 bool DrmGpuDisplayManager::TakeDisplayControl() {
203 for (const auto& drm
: devices_
) {
204 if (!drm
->SetMaster()) {
205 LOG(ERROR
) << "Failed to take control of the display";
212 bool DrmGpuDisplayManager::RelinquishDisplayControl() {
213 for (const auto& drm
: devices_
) {
214 if (!drm
->DropMaster()) {
215 LOG(ERROR
) << "Failed to relinquish control of the display";
222 void DrmGpuDisplayManager::AddGraphicsDevice(const base::FilePath
& path
,
223 const base::FileDescriptor
& fd
) {
224 base::File
file(fd
.fd
);
226 std::find_if(devices_
.begin(), devices_
.end(), FindByDevicePath(path
));
227 if (it
!= devices_
.end()) {
228 VLOG(2) << "Got request to add existing device '" << path
.value() << "'";
232 scoped_refptr
<DrmDevice
> device
=
233 drm_device_generator_
->CreateDevice(path
, file
.Pass());
235 VLOG(2) << "Could not initialize DRM device for '" << path
.value() << "'";
239 devices_
.push_back(device
);
241 device
->InitializeTaskRunner(io_task_runner_
);
244 void DrmGpuDisplayManager::RemoveGraphicsDevice(const base::FilePath
& path
) {
246 std::find_if(devices_
.begin(), devices_
.end(), FindByDevicePath(path
));
247 if (it
== devices_
.end()) {
248 VLOG(2) << "Got request to remove non-existent device '" << path
.value()
256 DrmDisplaySnapshot
* DrmGpuDisplayManager::FindDisplaySnapshot(int64_t id
) {
257 for (size_t i
= 0; i
< cached_displays_
.size(); ++i
)
258 if (cached_displays_
[i
]->display_id() == id
)
259 return cached_displays_
[i
];
264 const DrmDisplayMode
* DrmGpuDisplayManager::FindDisplayMode(
265 const gfx::Size
& size
,
267 float refresh_rate
) {
268 for (size_t i
= 0; i
< cached_modes_
.size(); ++i
)
269 if (cached_modes_
[i
]->size() == size
&&
270 cached_modes_
[i
]->is_interlaced() == is_interlaced
&&
271 cached_modes_
[i
]->refresh_rate() == refresh_rate
)
272 return static_cast<const DrmDisplayMode
*>(cached_modes_
[i
]);
277 void DrmGpuDisplayManager::RefreshDisplayList() {
278 ScopedVector
<DrmDisplaySnapshot
> old_displays(cached_displays_
.Pass());
279 ScopedVector
<const DisplayMode
> old_modes(cached_modes_
.Pass());
281 for (const auto& drm
: devices_
) {
282 ScopedVector
<HardwareDisplayControllerInfo
> displays
=
283 GetAvailableDisplayControllerInfos(drm
->get_fd());
284 for (size_t i
= 0; i
< displays
.size(); ++i
) {
285 DrmDisplaySnapshot
* display
= new DrmDisplaySnapshot(
286 drm
, displays
[i
]->connector(), displays
[i
]->crtc(), i
);
288 // If the display exists make sure to sync up the new snapshot with the
289 // old one to keep the user configured details.
290 auto it
= std::find_if(
291 old_displays
.begin(), old_displays
.end(),
292 DisplaySnapshotComparator(drm
, displays
[i
]->crtc()->crtc_id
,
293 displays
[i
]->connector()->connector_id
));
294 // Origin is only used within the platform code to keep track of the
296 if (it
!= old_displays
.end())
297 display
->set_origin((*it
)->origin());
299 cached_displays_
.push_back(display
);
300 cached_modes_
.insert(cached_modes_
.end(), display
->modes().begin(),
301 display
->modes().end());
305 NotifyScreenManager(cached_displays_
.get(), old_displays
.get());
308 bool DrmGpuDisplayManager::Configure(const DrmDisplaySnapshot
& output
,
309 const DrmDisplayMode
* mode
,
310 const gfx::Point
& origin
) {
311 VLOG(1) << "DRM configuring: device=" << output
.drm()->device_path().value()
312 << " crtc=" << output
.crtc() << " connector=" << output
.connector()
313 << " origin=" << origin
.ToString()
314 << " size=" << (mode
? mode
->size().ToString() : "0x0");
317 if (!screen_manager_
->ConfigureDisplayController(
318 output
.drm(), output
.crtc(), output
.connector(), origin
,
319 mode
->mode_info())) {
320 VLOG(1) << "Failed to configure: device="
321 << output
.drm()->device_path().value()
322 << " crtc=" << output
.crtc()
323 << " connector=" << output
.connector();
327 if (!screen_manager_
->DisableDisplayController(output
.drm(),
329 VLOG(1) << "Failed to disable device="
330 << output
.drm()->device_path().value()
331 << " crtc=" << output
.crtc();
339 bool DrmGpuDisplayManager::GetHDCPState(int64_t display_id
, HDCPState
* state
) {
340 DrmDisplaySnapshot
* output
= FindDisplaySnapshot(display_id
);
342 LOG(ERROR
) << "There is no display with ID " << display_id
;
346 ScopedDrmConnectorPtr
connector(
347 output
->drm()->GetConnector(output
->connector()));
349 PLOG(ERROR
) << "Failed to get connector " << output
->connector();
353 ScopedDrmPropertyPtr
hdcp_property(
354 output
->drm()->GetProperty(connector
.get(), kContentProtection
));
355 if (!hdcp_property
) {
356 PLOG(ERROR
) << "'" << kContentProtection
<< "' property doesn't exist.";
361 GetEnumNameForProperty(connector
.get(), hdcp_property
.get());
362 for (size_t i
= 0; i
< arraysize(kContentProtectionStates
); ++i
) {
363 if (name
== kContentProtectionStates
[i
].name
) {
364 *state
= kContentProtectionStates
[i
].state
;
365 VLOG(3) << "HDCP state: " << *state
<< " (" << name
<< ")";
370 LOG(ERROR
) << "Unknown content protection value '" << name
<< "'";
374 bool DrmGpuDisplayManager::SetHDCPState(int64_t display_id
, HDCPState state
) {
375 DrmDisplaySnapshot
* output
= FindDisplaySnapshot(display_id
);
377 LOG(ERROR
) << "There is no display with ID " << display_id
;
381 ScopedDrmConnectorPtr
connector(
382 output
->drm()->GetConnector(output
->connector()));
384 PLOG(ERROR
) << "Failed to get connector " << output
->connector();
388 ScopedDrmPropertyPtr
hdcp_property(
389 output
->drm()->GetProperty(connector
.get(), kContentProtection
));
390 if (!hdcp_property
) {
391 PLOG(ERROR
) << "'" << kContentProtection
<< "' property doesn't exist.";
395 return output
->drm()->SetProperty(
396 output
->connector(), hdcp_property
->prop_id
,
397 GetContentProtectionValue(hdcp_property
.get(), state
));
400 void DrmGpuDisplayManager::NotifyScreenManager(
401 const std::vector
<DrmDisplaySnapshot
*>& new_displays
,
402 const std::vector
<DrmDisplaySnapshot
*>& old_displays
) const {
403 for (size_t i
= 0; i
< old_displays
.size(); ++i
) {
404 const std::vector
<DrmDisplaySnapshot
*>::const_iterator it
=
405 std::find_if(new_displays
.begin(), new_displays
.end(),
406 DisplaySnapshotComparator(old_displays
[i
]));
408 if (it
== new_displays
.end()) {
409 screen_manager_
->RemoveDisplayController(old_displays
[i
]->drm(),
410 old_displays
[i
]->crtc());
414 for (size_t i
= 0; i
< new_displays
.size(); ++i
) {
415 const std::vector
<DrmDisplaySnapshot
*>::const_iterator it
=
416 std::find_if(old_displays
.begin(), old_displays
.end(),
417 DisplaySnapshotComparator(new_displays
[i
]));
419 if (it
== old_displays
.end()) {
420 screen_manager_
->AddDisplayController(new_displays
[i
]->drm(),
421 new_displays
[i
]->crtc(),
422 new_displays
[i
]->connector());