[go: nahoru, domu]

blob: 3eefa83455d0d5f0911d061d42a7a0c73607eb90 [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h"
#include <stddef.h>
#include <cstring>
#include <memory>
#include <string>
#include <utility>
#include "base/containers/contains.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/ranges/algorithm.h"
#include "base/strings/string_number_conversions.h"
#include "ui/display/types/display_mode.h"
#include "ui/display/types/display_snapshot.h"
#include "ui/display/types/gamma_ramp_rgb_entry.h"
#include "ui/ozone/platform/drm/gpu/drm_device.h"
#include "ui/ozone/platform/drm/gpu/drm_device_manager.h"
#include "ui/ozone/platform/drm/gpu/drm_display.h"
#include "ui/ozone/platform/drm/gpu/drm_gpu_util.h"
#include "ui/ozone/platform/drm/gpu/screen_manager.h"
namespace ui {
namespace {
constexpr char kMultipleDisplayIdsCollisionDetected[] =
"Display.MultipleDisplays.GenerateId.CollisionDetection";
// A list of property names that are blocked from issuing a full display
// configuration (modeset) via a udev display CHANGE event.
const char* kBlockedEventsByTriggerProperty[] = {"Content Protection"};
struct DrmDisplayParams {
scoped_refptr<DrmDevice> drm;
std::unique_ptr<HardwareDisplayControllerInfo> display_info;
raw_ptr<display::DisplaySnapshot, ExperimentalAsh> snapshot;
};
class DisplayComparator {
public:
explicit DisplayComparator(const DrmDisplay* display)
: drm_(display->drm()),
crtc_(display->crtc()),
connector_(display->connector()) {}
DisplayComparator(const scoped_refptr<DrmDevice>& drm,
uint32_t crtc,
uint32_t connector)
: drm_(drm), crtc_(crtc), connector_(connector) {}
bool operator()(const std::unique_ptr<DrmDisplay>& other) const {
return drm_ == other->drm() && connector_ == other->connector() &&
crtc_ == other->crtc();
}
private:
scoped_refptr<DrmDevice> drm_;
uint32_t crtc_;
uint32_t connector_;
};
bool MatchMode(const display::DisplayMode& display_mode,
const drmModeModeInfo& m) {
return display_mode.size() == ModeSize(m) &&
display_mode.refresh_rate() == ModeRefreshRate(m) &&
display_mode.is_interlaced() == ModeIsInterlaced(m);
}
bool FindMatchingMode(const std::vector<drmModeModeInfo> modes,
const display::DisplayMode& display_mode,
drmModeModeInfo* mode) {
for (const drmModeModeInfo& m : modes) {
if (MatchMode(display_mode, m)) {
*mode = m;
return true;
}
}
return false;
}
bool FindModeForDisplay(
drmModeModeInfo* mode_ptr,
const display::DisplayMode& display_mode,
const std::vector<drmModeModeInfo>& modes,
const std::vector<std::unique_ptr<DrmDisplay>>& all_displays) {
bool mode_found = FindMatchingMode(modes, display_mode, mode_ptr);
if (!mode_found) {
// If the display doesn't have the mode natively, then lookup the mode
// from other displays and try using it on the current display (some
// displays support panel fitting and they can use different modes even
// if the mode isn't explicitly declared).
for (const auto& other_display : all_displays) {
mode_found =
FindMatchingMode(other_display->modes(), display_mode, mode_ptr);
if (mode_found)
break;
}
if (!mode_found) {
LOG(ERROR) << "Failed to find mode: size="
<< display_mode.size().ToString()
<< " is_interlaced=" << display_mode.is_interlaced()
<< " refresh_rate=" << display_mode.refresh_rate();
}
}
return mode_found;
}
std::string GetEventPropertyByKey(const std::string& key,
const EventPropertyMap event_props) {
const auto it = event_props.find(key);
if (it == event_props.end())
return std::string();
return std::string(it->second);
}
} // namespace
DrmGpuDisplayManager::DrmGpuDisplayManager(ScreenManager* screen_manager,
DrmDeviceManager* drm_device_manager)
: screen_manager_(screen_manager),
drm_device_manager_(drm_device_manager) {}
DrmGpuDisplayManager::~DrmGpuDisplayManager() = default;
void DrmGpuDisplayManager::SetDisplaysConfiguredCallback(
base::RepeatingClosure callback) {
displays_configured_callback_ = std::move(callback);
}
MovableDisplaySnapshots DrmGpuDisplayManager::GetDisplays() {
std::vector<std::unique_ptr<DrmDisplay>> old_displays;
old_displays.swap(displays_);
std::vector<DrmDisplayParams> displays_to_create;
MovableDisplaySnapshots display_snapshots;
const DrmDeviceVector& devices = drm_device_manager_->GetDrmDevices();
size_t device_index = 0;
MapEdidIdToDisplaySnapshot edid_id_collision_map;
bool collision_detected = false;
for (const auto& drm : devices) {
if (device_index >= kMaxDrmCount) {
LOG(WARNING) << "Reached the current limit of " << kMaxDrmCount
<< " connected DRM devices. Ignoring the remaining "
<< devices.size() - kMaxDrmCount << " connected devices.";
break;
}
// Receiving a signal that DRM state was updated. Need to reset the plane
// manager's resource cache since IDs may have changed.
base::flat_set<uint32_t> valid_connector_ids =
drm->plane_manager()->ResetConnectorsCacheAndGetValidIds(
drm->GetResources());
// Create new DisplaySnapshots and resolve display ID collisions.
auto display_infos = GetDisplayInfosAndUpdateCrtcs(*drm);
// Make sure that the display infos we got have valid connector IDs.
// If not, we need to remove the display info from the list. This removes
// any zombie connectors.
display_infos.erase(
std::remove_if(display_infos.begin(), display_infos.end(),
[&valid_connector_ids](const auto& display_info) {
return !base::Contains(
valid_connector_ids, display_info->connector()->connector_id);}),
display_infos.end());
for (auto& display_info : display_infos) {
display_snapshots.emplace_back(CreateDisplaySnapshot(
*drm, display_info.get(), static_cast<uint8_t>(device_index)));
display::DisplaySnapshot* current_display_snapshot =
display_snapshots.back().get();
const auto colliding_display_snapshot_iter = edid_id_collision_map.find(
current_display_snapshot->edid_display_id());
if (colliding_display_snapshot_iter != edid_id_collision_map.end()) {
collision_detected = true;
current_display_snapshot->AddIndexToDisplayId();
display::DisplaySnapshot* colliding_display_snapshot =
colliding_display_snapshot_iter->second;
colliding_display_snapshot->AddIndexToDisplayId();
edid_id_collision_map[colliding_display_snapshot->edid_display_id()] =
colliding_display_snapshot;
}
edid_id_collision_map[current_display_snapshot->edid_display_id()] =
current_display_snapshot;
// Ownership of |display_info| is handed over.
displays_to_create.push_back(
{drm, std::move(display_info), current_display_snapshot});
}
device_index++;
}
// Create a new DrmDisplay with each of the corresponding display info and
// display snapshot. Note: do not use |display_infos| beyond this point,
// since some of the objects' internal references will be surrendered.
for (const DrmDisplayParams& params : displays_to_create) {
// If the DrmDisplay was present previously, copy its origin to the
// corresponding DisplaySnapshot before creating a new DrmDisplay.
auto old_drm_display_it = base::ranges::find_if(
old_displays,
DisplayComparator(params.drm, params.display_info->crtc()->crtc_id,
params.display_info->connector()->connector_id));
if (old_drm_display_it != old_displays.end()) {
params.snapshot->set_origin(old_drm_display_it->get()->origin());
old_displays.erase(old_drm_display_it);
}
displays_.emplace_back(std::make_unique<DrmDisplay>(
params.drm, params.display_info.get(), *params.snapshot));
}
const bool multiple_connected_displays = display_snapshots.size() > 1;
if (multiple_connected_displays) {
base::UmaHistogramBoolean(kMultipleDisplayIdsCollisionDetected,
collision_detected);
}
NotifyScreenManager(displays_, old_displays);
return display_snapshots;
}
bool DrmGpuDisplayManager::TakeDisplayControl() {
const DrmDeviceVector& devices = drm_device_manager_->GetDrmDevices();
bool status = true;
for (const auto& drm : devices)
status &= drm->SetMaster();
// Roll-back any successful operation.
if (!status) {
LOG(ERROR) << "Failed to take control of the display";
RelinquishDisplayControl();
}
return status;
}
void DrmGpuDisplayManager::RelinquishDisplayControl() {
const DrmDeviceVector& devices = drm_device_manager_->GetDrmDevices();
for (const auto& drm : devices)
drm->DropMaster();
}
bool DrmGpuDisplayManager::ShouldDisplayEventTriggerConfiguration(
const EventPropertyMap& event_props) {
DCHECK(!event_props.empty());
const std::string event_seq_num =
GetEventPropertyByKey("SEQNUM", event_props);
std::string log_prefix =
"Display event CHANGE" +
(event_seq_num.empty() ? "" : "(SEQNUM:" + event_seq_num + ") ");
std::string trigger_prop_log;
const std::string event_dev_path =
GetEventPropertyByKey("DEVPATH", event_props);
const DrmDeviceVector& devices = drm_device_manager_->GetDrmDevices();
for (const auto& drm : devices) {
if (drm->device_path().value().find(event_dev_path) == std::string::npos)
continue;
// Get the connector's ID and convert it to an int.
const std::string connector_id_str =
GetEventPropertyByKey("CONNECTOR", event_props);
if (connector_id_str.empty()) {
break;
}
uint32_t connector_id;
{
const bool conversion_success =
base::StringToUint(connector_id_str, &connector_id);
DCHECK(conversion_success);
}
// Get the trigger property's ID and convert to an int.
const std::string trigger_prop_id_str =
GetEventPropertyByKey("PROPERTY", event_props);
if (trigger_prop_id_str.empty())
break;
uint32_t trigger_prop_id;
{
const bool conversion_success =
base::StringToUint(trigger_prop_id_str, &trigger_prop_id);
DCHECK(conversion_success);
}
ScopedDrmObjectPropertyPtr property_values(
drm->GetObjectProperties(connector_id, DRM_MODE_OBJECT_CONNECTOR));
DCHECK(property_values);
// Fetch the name of the property from the device.
ScopedDrmPropertyPtr drm_property(drm->GetProperty(trigger_prop_id));
DCHECK(drm_property);
const std::string enum_value =
GetEnumNameForProperty(*drm_property, *property_values);
DCHECK(!enum_value.empty());
trigger_prop_log =
"[CONNECTOR:" + connector_id_str +
"] trigger property: " + std::string(drm_property->name) + "=" +
enum_value + ", ";
for (const char* blocked_prop : kBlockedEventsByTriggerProperty) {
if (strcmp(drm_property->name, blocked_prop) == 0) {
VLOG(1) << log_prefix << trigger_prop_log
<< "resolution: blocked; display configuration task "
"rejected.";
return false;
}
}
}
VLOG(1) << log_prefix << trigger_prop_log
<< "resolution: allowed; display configuration task triggered.";
return true;
}
bool DrmGpuDisplayManager::ConfigureDisplays(
const std::vector<display::DisplayConfigurationParams>& config_requests,
uint32_t modeset_flag) {
ScreenManager::ControllerConfigsList controllers_to_configure;
for (const auto& config : config_requests) {
int64_t display_id = config.id;
DrmDisplay* display = FindDisplay(display_id);
if (!display) {
LOG(WARNING) << __func__ << ": there is no display with ID "
<< display_id;
return false;
}
std::unique_ptr<drmModeModeInfo> mode_ptr =
config.mode ? std::make_unique<drmModeModeInfo>() : nullptr;
if (config.mode) {
if (!FindModeForDisplay(mode_ptr.get(), *config.mode.value(),
display->modes(), displays_)) {
return false;
}
}
scoped_refptr<DrmDevice> drm = display->drm();
ScreenManager::ControllerConfigParams params(
display->display_id(), drm, display->crtc(), display->connector(),
config.origin, std::move(mode_ptr), config.enable_vrr,
display->base_connector_id());
controllers_to_configure.push_back(std::move(params));
}
bool config_success = screen_manager_->ConfigureDisplayControllers(
controllers_to_configure, modeset_flag);
if (displays_configured_callback_)
displays_configured_callback_.Run();
const bool test_only = modeset_flag == display::kTestModeset;
if (!test_only && config_success) {
for (const auto& controller : controllers_to_configure)
FindDisplay(controller.display_id)->SetOrigin(controller.origin);
}
return config_success;
}
bool DrmGpuDisplayManager::SetHdcpKeyProp(int64_t display_id,
const std::string& key) {
DrmDisplay* display = FindDisplay(display_id);
if (!display) {
LOG(ERROR) << "SetHdcpKeyProp: There is no display with ID " << display_id;
return false;
}
return display->SetHdcpKeyProp(key);
}
bool DrmGpuDisplayManager::GetHDCPState(
int64_t display_id,
display::HDCPState* state,
display::ContentProtectionMethod* protection_method) {
DrmDisplay* display = FindDisplay(display_id);
if (!display) {
LOG(WARNING) << __func__ << ": there is no display with ID " << display_id;
return false;
}
return display->GetHDCPState(state, protection_method);
}
bool DrmGpuDisplayManager::SetHDCPState(
int64_t display_id,
display::HDCPState state,
display::ContentProtectionMethod protection_method) {
DrmDisplay* display = FindDisplay(display_id);
if (!display) {
LOG(WARNING) << __func__ << ": there is no display with ID " << display_id;
return false;
}
return display->SetHDCPState(state, protection_method);
}
void DrmGpuDisplayManager::SetColorMatrix(
int64_t display_id,
const std::vector<float>& color_matrix) {
DrmDisplay* display = FindDisplay(display_id);
if (!display) {
LOG(WARNING) << __func__ << ": there is no display with ID " << display_id;
return;
}
display->SetColorMatrix(color_matrix);
}
void DrmGpuDisplayManager::SetBackgroundColor(int64_t display_id,
const uint64_t background_color) {
DrmDisplay* display = FindDisplay(display_id);
if (!display) {
LOG(WARNING) << __func__ << ": there is no display with ID" << display_id;
return;
}
display->SetBackgroundColor(background_color);
}
void DrmGpuDisplayManager::SetGammaCorrection(
int64_t display_id,
const display::GammaCurve& degamma,
const display::GammaCurve& gamma) {
DrmDisplay* display = FindDisplay(display_id);
if (!display) {
LOG(WARNING) << __func__ << ": there is no display with ID " << display_id;
return;
}
display->SetGammaCorrection(degamma, gamma);
}
bool DrmGpuDisplayManager::SetPrivacyScreen(int64_t display_id, bool enabled) {
DrmDisplay* display = FindDisplay(display_id);
if (!display) {
LOG(WARNING) << __func__ << ": there is no display with ID " << display_id;
return false;
}
return display->SetPrivacyScreen(enabled);
}
void DrmGpuDisplayManager::SetColorSpace(int64_t crtc_id,
const gfx::ColorSpace& color_space) {
for (const auto& display : displays_) {
if (display->crtc() == crtc_id) {
display->SetColorSpace(color_space);
return;
}
}
LOG(WARNING) << __func__ << ": there is no display with CRTC ID " << crtc_id;
}
DrmDisplay* DrmGpuDisplayManager::FindDisplay(int64_t display_id) {
for (const auto& display : displays_) {
if (display->display_id() == display_id)
return display.get();
}
return nullptr;
}
void DrmGpuDisplayManager::NotifyScreenManager(
const std::vector<std::unique_ptr<DrmDisplay>>& new_displays,
const std::vector<std::unique_ptr<DrmDisplay>>& old_displays) const {
ScreenManager::CrtcsWithDrmList controllers_to_remove;
for (const auto& old_display : old_displays) {
if (base::ranges::none_of(new_displays,
DisplayComparator(old_display.get()))) {
controllers_to_remove.emplace_back(old_display->crtc(),
old_display->drm());
}
}
if (!controllers_to_remove.empty())
screen_manager_->RemoveDisplayControllers(controllers_to_remove);
for (const auto& new_display : new_displays) {
if (base::ranges::none_of(old_displays,
DisplayComparator(new_display.get()))) {
screen_manager_->AddDisplayController(
new_display->drm(), new_display->crtc(), new_display->connector());
}
}
}
} // namespace ui