blob: 3b1057b024eae231d12590b12e4b4a4710adaaf8 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// 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 <memory>
#include <utility>
#include "base/logging.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/gfx/linux/drm_util_linux.h"
#include "ui/ozone/platform/drm/common/drm_util.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/screen_manager.h"
namespace ui {
namespace {
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;
}
} // 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::SetClearOverlayCacheCallback(
base::RepeatingClosure callback) {
clear_overlay_cache_callback_ = std::move(callback);
}
MovableDisplaySnapshots DrmGpuDisplayManager::GetDisplays() {
std::vector<std::unique_ptr<DrmDisplay>> old_displays;
old_displays.swap(displays_);
MovableDisplaySnapshots params_list;
const DrmDeviceVector& devices = drm_device_manager_->GetDrmDevices();
size_t device_index = 0;
for (const auto& drm : devices) {
// Receiving a signal that DRM state was updated. Need to reset the plane
// manager's resource cache since IDs may have changed.
drm->plane_manager()->ResetConnectorsCache(drm->GetResources());
auto display_infos = GetAvailableDisplayControllerInfos(drm->get_fd());
for (const auto& display_info : display_infos) {
auto it = std::find_if(
old_displays.begin(), old_displays.end(),
DisplayComparator(drm, display_info->crtc()->crtc_id,
display_info->connector()->connector_id));
if (it != old_displays.end()) {
displays_.push_back(std::move(*it));
old_displays.erase(it);
} else {
displays_.push_back(std::make_unique<DrmDisplay>(drm));
}
auto display_snapshot =
displays_.back()->Update(display_info.get(), device_index);
if (display_snapshot) {
params_list.push_back(std::move(display_snapshot));
} else {
displays_.pop_back();
}
}
device_index++;
}
NotifyScreenManager(displays_, old_displays);
return params_list;
}
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::ConfigureDisplays(
const std::vector<display::DisplayConfigurationParams>& config_requests) {
bool config_success = true;
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(ERROR) << "There is no display with ID " << display_id;
config_success = false;
continue;
}
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_)) {
config_success = false;
continue;
}
}
scoped_refptr<DrmDevice> drm = display->drm();
VLOG(1) << "DRM configuring: device=" << drm->device_path().value()
<< " crtc=" << display->crtc()
<< " connector=" << display->connector()
<< " origin=" << config.origin.ToString() << " size="
<< (mode_ptr ? ModeSize(*(mode_ptr.get())).ToString() : "0x0")
<< " refresh_rate=" << (mode_ptr ? mode_ptr->vrefresh : 0) << "Hz";
ScreenManager::ControllerConfigParams params(
display->display_id(), drm, display->crtc(), display->connector(),
config.origin, std::move(mode_ptr));
controllers_to_configure.push_back(std::move(params));
}
if (controllers_to_configure.empty())
return config_success;
if (clear_overlay_cache_callback_)
clear_overlay_cache_callback_.Run();
config_success &=
screen_manager_->ConfigureDisplayControllers(controllers_to_configure);
for (const auto& controller : controllers_to_configure) {
int64_t display_id = controller.display_id;
DrmDisplay* display = FindDisplay(display_id);
auto config = std::find_if(
config_requests.begin(), config_requests.end(),
[display_id](const auto& request) { return request.id == display_id; });
if (config_success) {
display->SetOrigin(config->origin);
} else {
if (config->mode) {
VLOG(1) << "Failed to enable device="
<< display->drm()->device_path().value()
<< " crtc=" << display->crtc()
<< " connector=" << display->connector();
} else {
VLOG(1) << "Failed to disable device="
<< display->drm()->device_path().value()
<< " crtc=" << display->crtc();
}
}
}
return config_success;
}
bool DrmGpuDisplayManager::GetHDCPState(
int64_t display_id,
display::HDCPState* state,
display::ContentProtectionMethod* protection_method) {
DrmDisplay* display = FindDisplay(display_id);
if (!display) {
LOG(ERROR) << "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(ERROR) << "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(ERROR) << "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(ERROR) << "There is no display with ID" << display_id;
return;
}
display->SetBackgroundColor(background_color);
}
void DrmGpuDisplayManager::SetGammaCorrection(
int64_t display_id,
const std::vector<display::GammaRampRGBEntry>& degamma_lut,
const std::vector<display::GammaRampRGBEntry>& gamma_lut) {
DrmDisplay* display = FindDisplay(display_id);
if (!display) {
LOG(ERROR) << "There is no display with ID " << display_id;
return;
}
display->SetGammaCorrection(degamma_lut, gamma_lut);
}
void DrmGpuDisplayManager::SetPrivacyScreen(int64_t display_id, bool enabled) {
DrmDisplay* display = FindDisplay(display_id);
if (!display) {
LOG(ERROR) << "There is no display with ID " << display_id;
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(ERROR) << __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) {
auto it = std::find_if(new_displays.begin(), new_displays.end(),
DisplayComparator(old_display.get()));
if (it == new_displays.end()) {
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) {
auto it = std::find_if(old_displays.begin(), old_displays.end(),
DisplayComparator(new_display.get()));
if (it == old_displays.end()) {
screen_manager_->AddDisplayController(
new_display->drm(), new_display->crtc(), new_display->connector());
}
}
}
} // namespace ui