| Index: ui/ozone/platform/drm/host/drm_display_host_manager.cc
|
| diff --git a/ui/ozone/platform/drm/host/drm_display_host_manager.cc b/ui/ozone/platform/drm/host/drm_display_host_manager.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..3fa038dd42d9aa40bd89cac460168bfdaff32125
|
| --- /dev/null
|
| +++ b/ui/ozone/platform/drm/host/drm_display_host_manager.cc
|
| @@ -0,0 +1,461 @@
|
| +// 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/host/drm_display_host_manager.h"
|
| +
|
| +#include <fcntl.h>
|
| +#include <xf86drm.h>
|
| +
|
| +#include "base/files/file_enumerator.h"
|
| +#include "base/posix/eintr_wrapper.h"
|
| +#include "base/strings/stringprintf.h"
|
| +#include "base/thread_task_runner_handle.h"
|
| +#include "base/threading/worker_pool.h"
|
| +#include "ui/display/types/display_snapshot.h"
|
| +#include "ui/events/ozone/device/device_event.h"
|
| +#include "ui/events/ozone/device/device_manager.h"
|
| +#include "ui/ozone/common/display_util.h"
|
| +#include "ui/ozone/platform/drm/common/drm_util.h"
|
| +#include "ui/ozone/platform/drm/host/drm_device_handle.h"
|
| +#include "ui/ozone/platform/drm/host/drm_display_host.h"
|
| +#include "ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h"
|
| +#include "ui/ozone/platform/drm/host/drm_native_display_delegate.h"
|
| +
|
| +namespace ui {
|
| +
|
| +namespace {
|
| +
|
| +typedef base::Callback<void(const base::FilePath&, scoped_ptr<DrmDeviceHandle>)>
|
| + OnOpenDeviceReplyCallback;
|
| +
|
| +const char kDefaultGraphicsCardPattern[] = "/dev/dri/card%d";
|
| +const char kVgemDevDriCardPath[] = "/dev/dri/";
|
| +const char kVgemSysCardPath[] = "/sys/bus/platform/devices/vgem/drm/";
|
| +
|
| +const char* kDisplayActionString[] = {
|
| + "ADD",
|
| + "REMOVE",
|
| + "CHANGE",
|
| +};
|
| +
|
| +void OpenDeviceOnWorkerThread(
|
| + const base::FilePath& path,
|
| + const scoped_refptr<base::TaskRunner>& reply_runner,
|
| + const OnOpenDeviceReplyCallback& callback) {
|
| + scoped_ptr<DrmDeviceHandle> handle(new DrmDeviceHandle());
|
| + handle->Initialize(path);
|
| + reply_runner->PostTask(
|
| + FROM_HERE, base::Bind(callback, path, base::Passed(handle.Pass())));
|
| +}
|
| +
|
| +base::FilePath GetPrimaryDisplayCardPath() {
|
| + struct drm_mode_card_res res;
|
| + for (int i = 0; /* end on first card# that does not exist */; i++) {
|
| + std::string card_path = base::StringPrintf(kDefaultGraphicsCardPattern, i);
|
| +
|
| + if (access(card_path.c_str(), F_OK) != 0) {
|
| + LOG(WARNING) << "Can't access card: " << card_path;
|
| + break;
|
| + }
|
| +
|
| +
|
| + int fd = open(card_path.c_str(), O_RDWR | O_CLOEXEC);
|
| + if (fd < 0) {
|
| + VPLOG(1) << "Failed to open '" << card_path << "'";
|
| + continue;
|
| + }
|
| +
|
| + memset(&res, 0, sizeof(struct drm_mode_card_res));
|
| + int ret = drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res);
|
| + close(fd);
|
| + if (ret == 0 && res.count_crtcs > 0) {
|
| + return base::FilePath(card_path);
|
| + }
|
| +
|
| + VPLOG_IF(1, ret) << "Failed to get DRM resources for '" << card_path << "'";
|
| + }
|
| +
|
| + return base::FilePath();
|
| +}
|
| +
|
| +base::FilePath GetVgemCardPath() {
|
| + base::FileEnumerator file_iter(base::FilePath(kVgemSysCardPath), false,
|
| + base::FileEnumerator::DIRECTORIES,
|
| + FILE_PATH_LITERAL("card*"));
|
| +
|
| + while (!file_iter.Next().empty()) {
|
| + // Inspect the card%d directories in the directory and extract the filename.
|
| + std::string vgem_card_path =
|
| + kVgemDevDriCardPath + file_iter.GetInfo().GetName().BaseName().value();
|
| + DVLOG(1) << "VGEM card path is " << vgem_card_path;
|
| + return base::FilePath(vgem_card_path);
|
| + }
|
| + DVLOG(1) << "Don't support VGEM";
|
| + return base::FilePath();
|
| +}
|
| +
|
| +class FindDrmDisplayHostById {
|
| + public:
|
| + explicit FindDrmDisplayHostById(int64_t display_id)
|
| + : display_id_(display_id) {}
|
| +
|
| + bool operator()(const DrmDisplayHost* display) const {
|
| + return display->snapshot()->display_id() == display_id_;
|
| + }
|
| +
|
| + private:
|
| + int64_t display_id_;
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +DrmDisplayHostManager::DrmDisplayHostManager(
|
| + DrmGpuPlatformSupportHost* proxy,
|
| + DeviceManager* device_manager,
|
| + InputControllerEvdev* input_controller)
|
| + : proxy_(proxy),
|
| + device_manager_(device_manager),
|
| + input_controller_(input_controller),
|
| + primary_graphics_card_path_(GetPrimaryDisplayCardPath()),
|
| + weak_ptr_factory_(this) {
|
| + {
|
| + // First device needs to be treated specially. We need to open this
|
| + // synchronously since the GPU process will need it to initialize the
|
| + // graphics state.
|
| + primary_drm_device_handle_.reset(new DrmDeviceHandle());
|
| + if (!primary_drm_device_handle_->Initialize(primary_graphics_card_path_)) {
|
| + LOG(FATAL) << "Failed to open primary graphics card";
|
| + return;
|
| + }
|
| + drm_devices_.insert(primary_graphics_card_path_);
|
| +
|
| + vgem_card_path_ = GetVgemCardPath();
|
| + if (!vgem_card_path_.empty()) {
|
| + int fd = HANDLE_EINTR(
|
| + open(vgem_card_path_.value().c_str(), O_RDWR | O_CLOEXEC));
|
| + if (fd < 0) {
|
| + PLOG(ERROR) << "Failed to open vgem: " << vgem_card_path_.value();
|
| + }
|
| + vgem_card_device_file_.reset(fd);
|
| + }
|
| + }
|
| +
|
| + device_manager_->AddObserver(this);
|
| +
|
| + ScopedVector<HardwareDisplayControllerInfo> display_infos =
|
| + GetAvailableDisplayControllerInfos(primary_drm_device_handle_->fd());
|
| + has_dummy_display_ = !display_infos.empty();
|
| + for (size_t i = 0; i < display_infos.size(); ++i) {
|
| + displays_.push_back(new DrmDisplayHost(
|
| + proxy_, CreateDisplaySnapshotParams(display_infos[i],
|
| + primary_drm_device_handle_->fd(), i,
|
| + gfx::Point()),
|
| + true /* is_dummy */));
|
| + }
|
| +}
|
| +
|
| +DrmDisplayHostManager::~DrmDisplayHostManager() {
|
| + device_manager_->RemoveObserver(this);
|
| +}
|
| +
|
| +DrmDisplayHost* DrmDisplayHostManager::GetDisplay(int64_t display_id) {
|
| + auto it = std::find_if(displays_.begin(), displays_.end(),
|
| + FindDrmDisplayHostById(display_id));
|
| + if (it == displays_.end())
|
| + return nullptr;
|
| +
|
| + return *it;
|
| +}
|
| +
|
| +void DrmDisplayHostManager::AddDelegate(DrmNativeDisplayDelegate* delegate) {
|
| + DCHECK(!delegate_);
|
| + delegate_ = delegate;
|
| +}
|
| +
|
| +void DrmDisplayHostManager::RemoveDelegate(DrmNativeDisplayDelegate* delegate) {
|
| + DCHECK_EQ(delegate_, delegate);
|
| + delegate_ = nullptr;
|
| +}
|
| +
|
| +void DrmDisplayHostManager::TakeDisplayControl(
|
| + const DisplayControlCallback& callback) {
|
| + if (display_control_change_pending_) {
|
| + LOG(ERROR) << "TakeDisplayControl called while change already pending";
|
| + callback.Run(false);
|
| + return;
|
| + }
|
| +
|
| + if (!display_externally_controlled_) {
|
| + LOG(ERROR) << "TakeDisplayControl called while display already owned";
|
| + callback.Run(true);
|
| + return;
|
| + }
|
| +
|
| + take_display_control_callback_ = callback;
|
| + display_control_change_pending_ = true;
|
| +
|
| + if (!proxy_->TakeDisplayControl())
|
| + OnTakeDisplayControl(false);
|
| +}
|
| +
|
| +void DrmDisplayHostManager::RelinquishDisplayControl(
|
| + const DisplayControlCallback& callback) {
|
| + if (display_control_change_pending_) {
|
| + LOG(ERROR)
|
| + << "RelinquishDisplayControl called while change already pending";
|
| + callback.Run(false);
|
| + return;
|
| + }
|
| +
|
| + if (display_externally_controlled_) {
|
| + LOG(ERROR) << "RelinquishDisplayControl called while display not owned";
|
| + callback.Run(true);
|
| + return;
|
| + }
|
| +
|
| + relinquish_display_control_callback_ = callback;
|
| + display_control_change_pending_ = true;
|
| +
|
| + if (!proxy_->RelinquishDisplayControl())
|
| + OnRelinquishDisplayControl(false);
|
| +}
|
| +
|
| +void DrmDisplayHostManager::UpdateDisplays(
|
| + const GetDisplaysCallback& callback) {
|
| + get_displays_callback_ = callback;
|
| + if (!proxy_->RefreshNativeDisplays()) {
|
| + get_displays_callback_.Reset();
|
| + RunUpdateDisplaysCallback(callback);
|
| + }
|
| +}
|
| +
|
| +void DrmDisplayHostManager::OnDeviceEvent(const DeviceEvent& event) {
|
| + if (event.device_type() != DeviceEvent::DISPLAY)
|
| + return;
|
| +
|
| + event_queue_.push(DisplayEvent(event.action_type(), event.path()));
|
| + ProcessEvent();
|
| +}
|
| +
|
| +void DrmDisplayHostManager::ProcessEvent() {
|
| + while (!event_queue_.empty() && !task_pending_) {
|
| + DisplayEvent event = event_queue_.front();
|
| + event_queue_.pop();
|
| + VLOG(1) << "Got display event " << kDisplayActionString[event.action_type]
|
| + << " for " << event.path.value();
|
| + switch (event.action_type) {
|
| + case DeviceEvent::ADD:
|
| + if (event.path == vgem_card_path_)
|
| + continue;
|
| + if (drm_devices_.find(event.path) == drm_devices_.end()) {
|
| + task_pending_ = base::WorkerPool::PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&OpenDeviceOnWorkerThread, event.path,
|
| + base::ThreadTaskRunnerHandle::Get(),
|
| + base::Bind(&DrmDisplayHostManager::OnAddGraphicsDevice,
|
| + weak_ptr_factory_.GetWeakPtr())),
|
| + false /* task_is_slow */);
|
| + }
|
| + break;
|
| + case DeviceEvent::CHANGE:
|
| + task_pending_ = base::ThreadTaskRunnerHandle::Get()->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&DrmDisplayHostManager::OnUpdateGraphicsDevice,
|
| + weak_ptr_factory_.GetWeakPtr()));
|
| + break;
|
| + case DeviceEvent::REMOVE:
|
| + DCHECK(event.path != primary_graphics_card_path_)
|
| + << "Removing primary graphics card";
|
| + DCHECK(event.path != vgem_card_path_) << "Removing VGEM device";
|
| + auto it = drm_devices_.find(event.path);
|
| + if (it != drm_devices_.end()) {
|
| + task_pending_ = base::ThreadTaskRunnerHandle::Get()->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&DrmDisplayHostManager::OnRemoveGraphicsDevice,
|
| + weak_ptr_factory_.GetWeakPtr(), event.path));
|
| + drm_devices_.erase(it);
|
| + }
|
| + break;
|
| + }
|
| + }
|
| +}
|
| +
|
| +void DrmDisplayHostManager::OnAddGraphicsDevice(
|
| + const base::FilePath& path,
|
| + scoped_ptr<DrmDeviceHandle> handle) {
|
| + if (handle->IsValid()) {
|
| + drm_devices_.insert(path);
|
| + proxy_->AddGraphicsDevice(path, base::FileDescriptor(handle->PassFD()));
|
| + NotifyDisplayDelegate();
|
| + }
|
| +
|
| + task_pending_ = false;
|
| + ProcessEvent();
|
| +}
|
| +
|
| +void DrmDisplayHostManager::OnUpdateGraphicsDevice() {
|
| + NotifyDisplayDelegate();
|
| + task_pending_ = false;
|
| + ProcessEvent();
|
| +}
|
| +
|
| +void DrmDisplayHostManager::OnRemoveGraphicsDevice(const base::FilePath& path) {
|
| + proxy_->RemoveGraphicsDevice(path);
|
| + NotifyDisplayDelegate();
|
| + task_pending_ = false;
|
| + ProcessEvent();
|
| +}
|
| +
|
| +void DrmDisplayHostManager::OnChannelEstablished(int host_id) {
|
| + // If in the middle of a configuration, just respond with the old list of
|
| + // displays. This is fine, since after the DRM resources are initialized and
|
| + // IPC-ed to the GPU NotifyDisplayDelegate() is called to let the display
|
| + // delegate know that the display configuration changed and it needs to
|
| + // update it again.
|
| + if (!get_displays_callback_.is_null()) {
|
| + base::ThreadTaskRunnerHandle::Get()->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&DrmDisplayHostManager::RunUpdateDisplaysCallback,
|
| + weak_ptr_factory_.GetWeakPtr(), get_displays_callback_));
|
| + get_displays_callback_.Reset();
|
| + }
|
| +
|
| + // Signal that we're taking DRM master since we're going through the
|
| + // initialization process again and we'll take all the available resources.
|
| + if (!take_display_control_callback_.is_null())
|
| + OnTakeDisplayControl(true);
|
| +
|
| + if (!relinquish_display_control_callback_.is_null())
|
| + OnRelinquishDisplayControl(false);
|
| +
|
| + drm_devices_.clear();
|
| + drm_devices_.insert(primary_graphics_card_path_);
|
| + scoped_ptr<DrmDeviceHandle> handle = primary_drm_device_handle_.Pass();
|
| + if (!handle) {
|
| + handle.reset(new DrmDeviceHandle());
|
| + if (!handle->Initialize(primary_graphics_card_path_))
|
| + LOG(FATAL) << "Failed to open primary graphics card";
|
| + }
|
| +
|
| + // Send the primary device first since this is used to initialize graphics
|
| + // state.
|
| + proxy_->AddGraphicsDevice(primary_graphics_card_path_,
|
| + base::FileDescriptor(handle->PassFD()));
|
| +
|
| + device_manager_->ScanDevices(this);
|
| + NotifyDisplayDelegate();
|
| +}
|
| +
|
| +void DrmDisplayHostManager::OnChannelDestroyed(int host_id) {
|
| + // Do nothing.
|
| +}
|
| +
|
| +void DrmDisplayHostManager::OnUpdateNativeDisplays(
|
| + const std::vector<DisplaySnapshot_Params>& params) {
|
| + ScopedVector<DrmDisplayHost> old_displays(displays_.Pass());
|
| + for (size_t i = 0; i < params.size(); ++i) {
|
| + auto it = std::find_if(old_displays.begin(), old_displays.end(),
|
| + FindDrmDisplayHostById(params[i].display_id));
|
| + if (it == old_displays.end()) {
|
| + displays_.push_back(
|
| + new DrmDisplayHost(proxy_, params[i], false /* is_dummy */));
|
| + } else {
|
| + (*it)->UpdateDisplaySnapshot(params[i]);
|
| + displays_.push_back(*it);
|
| + old_displays.weak_erase(it);
|
| + }
|
| + }
|
| +
|
| + if (!get_displays_callback_.is_null()) {
|
| + base::ThreadTaskRunnerHandle::Get()->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&DrmDisplayHostManager::RunUpdateDisplaysCallback,
|
| + weak_ptr_factory_.GetWeakPtr(), get_displays_callback_));
|
| + get_displays_callback_.Reset();
|
| + }
|
| +}
|
| +
|
| +void DrmDisplayHostManager::OnDisplayConfigured(int64_t display_id,
|
| + bool status) {
|
| + DrmDisplayHost* display = GetDisplay(display_id);
|
| + if (display)
|
| + display->OnDisplayConfigured(status);
|
| + else
|
| + LOG(ERROR) << "Couldn't find display with id=" << display_id;
|
| +}
|
| +
|
| +void DrmDisplayHostManager::OnHDCPStateReceived(int64_t display_id,
|
| + bool status,
|
| + HDCPState state) {
|
| + DrmDisplayHost* display = GetDisplay(display_id);
|
| + if (display)
|
| + display->OnHDCPStateReceived(status, state);
|
| + else
|
| + LOG(ERROR) << "Couldn't find display with id=" << display_id;
|
| +}
|
| +
|
| +void DrmDisplayHostManager::OnHDCPStateUpdated(int64_t display_id,
|
| + bool status) {
|
| + DrmDisplayHost* display = GetDisplay(display_id);
|
| + if (display)
|
| + display->OnHDCPStateUpdated(status);
|
| + else
|
| + LOG(ERROR) << "Couldn't find display with id=" << display_id;
|
| +}
|
| +
|
| +void DrmDisplayHostManager::OnTakeDisplayControl(bool status) {
|
| + if (take_display_control_callback_.is_null()) {
|
| + LOG(ERROR) << "No callback for take display control";
|
| + return;
|
| + }
|
| +
|
| + DCHECK(display_externally_controlled_);
|
| + DCHECK(display_control_change_pending_);
|
| +
|
| + if (status) {
|
| + input_controller_->SetInputDevicesEnabled(true);
|
| + display_externally_controlled_ = false;
|
| + }
|
| +
|
| + base::ThreadTaskRunnerHandle::Get()->PostTask(
|
| + FROM_HERE, base::Bind(take_display_control_callback_, status));
|
| + take_display_control_callback_.Reset();
|
| + display_control_change_pending_ = false;
|
| +}
|
| +
|
| +void DrmDisplayHostManager::OnRelinquishDisplayControl(bool status) {
|
| + if (relinquish_display_control_callback_.is_null()) {
|
| + LOG(ERROR) << "No callback for relinquish display control";
|
| + return;
|
| + }
|
| +
|
| + DCHECK(!display_externally_controlled_);
|
| + DCHECK(display_control_change_pending_);
|
| +
|
| + if (status) {
|
| + input_controller_->SetInputDevicesEnabled(false);
|
| + display_externally_controlled_ = true;
|
| + }
|
| +
|
| + base::ThreadTaskRunnerHandle::Get()->PostTask(
|
| + FROM_HERE, base::Bind(relinquish_display_control_callback_, status));
|
| + relinquish_display_control_callback_.Reset();
|
| + display_control_change_pending_ = false;
|
| +}
|
| +
|
| +void DrmDisplayHostManager::RunUpdateDisplaysCallback(
|
| + const GetDisplaysCallback& callback) const {
|
| + std::vector<DisplaySnapshot*> snapshots;
|
| + for (auto* display : displays_)
|
| + snapshots.push_back(display->snapshot());
|
| +
|
| + callback.Run(snapshots);
|
| +}
|
| +
|
| +void DrmDisplayHostManager::NotifyDisplayDelegate() const {
|
| + if (delegate_)
|
| + delegate_->OnConfigurationChanged();
|
| +}
|
| +
|
| +} // namespace ui
|
|
|