[Ozone-Drm] Add support for async content protection
[chromium-blink-merge.git] / ui / ozone / platform / drm / gpu / drm_gpu_display_manager.cc
blob526b200513f1d57203370d665b1be79070f14a63
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"
7 #include "base/bind.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"
23 namespace ui {
25 namespace {
27 const char kContentProtection[] = "Content Protection";
29 struct ContentProtectionMapping {
30 const char* name;
31 HDCPState state;
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,
40 HDCPState state) {
41 std::string name;
42 for (size_t i = 0; i < arraysize(kContentProtectionStates); ++i) {
43 if (kContentProtectionStates[i].state == state) {
44 name = kContentProtectionStates[i].name;
45 break;
49 for (int i = 0; i < property->count_enums; ++i)
50 if (name == property->enums[i].name)
51 return i;
53 NOTREACHED();
54 return 0;
57 class DisplaySnapshotComparator {
58 public:
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,
65 uint32_t crtc,
66 uint32_t connector)
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();
74 private:
75 scoped_refptr<DrmDevice> drm_;
76 uint32_t crtc_;
77 uint32_t connector_;
80 class FindByDevicePath {
81 public:
82 explicit FindByDevicePath(const base::FilePath& path) : path_(path) {}
84 bool operator()(const scoped_refptr<DrmDevice>& device) {
85 return device->device_path() == path_;
88 private:
89 base::FilePath 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)
96 continue;
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;
105 NOTREACHED();
106 return std::string();
109 } // namespace
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))
129 return;
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]));
143 return displays;
146 bool DrmGpuDisplayManager::ConfigureDisplay(
147 int64_t id,
148 const DisplayMode_Params& mode_param,
149 const gfx::Point& origin) {
150 DrmDisplaySnapshot* display = FindDisplaySnapshot(id);
151 if (!display) {
152 LOG(ERROR) << "There is no display with ID " << id;
153 return false;
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];
162 break;
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).
170 if (!mode)
171 mode = FindDisplayMode(mode_param.size, mode_param.is_interlaced,
172 mode_param.refresh_rate);
174 if (!mode) {
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;
178 return false;
181 bool success =
182 Configure(*display, static_cast<const DrmDisplayMode*>(mode), origin);
183 if (success) {
184 display->set_origin(origin);
185 display->set_current_mode(mode);
188 return success;
191 bool DrmGpuDisplayManager::DisableDisplay(int64_t id) {
192 DrmDisplaySnapshot* display = FindDisplaySnapshot(id);
193 bool success = false;
194 if (display)
195 success = Configure(*display, NULL, gfx::Point());
196 else
197 LOG(ERROR) << "There is no display with ID " << id;
199 return success;
202 bool DrmGpuDisplayManager::TakeDisplayControl() {
203 for (const auto& drm : devices_) {
204 if (!drm->SetMaster()) {
205 LOG(ERROR) << "Failed to take control of the display";
206 return false;
209 return true;
212 bool DrmGpuDisplayManager::RelinquishDisplayControl() {
213 for (const auto& drm : devices_) {
214 if (!drm->DropMaster()) {
215 LOG(ERROR) << "Failed to relinquish control of the display";
216 return false;
219 return true;
222 void DrmGpuDisplayManager::AddGraphicsDevice(const base::FilePath& path,
223 const base::FileDescriptor& fd) {
224 base::File file(fd.fd);
225 auto it =
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() << "'";
229 return;
232 scoped_refptr<DrmDevice> device =
233 drm_device_generator_->CreateDevice(path, file.Pass());
234 if (!device) {
235 VLOG(2) << "Could not initialize DRM device for '" << path.value() << "'";
236 return;
239 devices_.push_back(device);
240 if (io_task_runner_)
241 device->InitializeTaskRunner(io_task_runner_);
244 void DrmGpuDisplayManager::RemoveGraphicsDevice(const base::FilePath& path) {
245 auto it =
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()
249 << "'";
250 return;
253 devices_.erase(it);
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];
261 return NULL;
264 const DrmDisplayMode* DrmGpuDisplayManager::FindDisplayMode(
265 const gfx::Size& size,
266 bool is_interlaced,
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]);
274 return NULL;
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
295 // display location.
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");
316 if (mode) {
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();
324 return false;
326 } else {
327 if (!screen_manager_->DisableDisplayController(output.drm(),
328 output.crtc())) {
329 VLOG(1) << "Failed to disable device="
330 << output.drm()->device_path().value()
331 << " crtc=" << output.crtc();
332 return false;
336 return true;
339 bool DrmGpuDisplayManager::GetHDCPState(int64_t display_id, HDCPState* state) {
340 DrmDisplaySnapshot* output = FindDisplaySnapshot(display_id);
341 if (!output) {
342 LOG(ERROR) << "There is no display with ID " << display_id;
343 return false;
346 ScopedDrmConnectorPtr connector(
347 output->drm()->GetConnector(output->connector()));
348 if (!connector) {
349 PLOG(ERROR) << "Failed to get connector " << output->connector();
350 return false;
353 ScopedDrmPropertyPtr hdcp_property(
354 output->drm()->GetProperty(connector.get(), kContentProtection));
355 if (!hdcp_property) {
356 PLOG(ERROR) << "'" << kContentProtection << "' property doesn't exist.";
357 return false;
360 std::string name =
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 << ")";
366 return true;
370 LOG(ERROR) << "Unknown content protection value '" << name << "'";
371 return false;
374 bool DrmGpuDisplayManager::SetHDCPState(int64_t display_id, HDCPState state) {
375 DrmDisplaySnapshot* output = FindDisplaySnapshot(display_id);
376 if (!output) {
377 LOG(ERROR) << "There is no display with ID " << display_id;
378 return false;
381 ScopedDrmConnectorPtr connector(
382 output->drm()->GetConnector(output->connector()));
383 if (!connector) {
384 PLOG(ERROR) << "Failed to get connector " << output->connector();
385 return false;
388 ScopedDrmPropertyPtr hdcp_property(
389 output->drm()->GetProperty(connector.get(), kContentProtection));
390 if (!hdcp_property) {
391 PLOG(ERROR) << "'" << kContentProtection << "' property doesn't exist.";
392 return false;
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());
427 } // namespace ui