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.
4
5#include "ui/ozone/platform/dri/screen_manager.h"
6
7#include <xf86drmMode.h>
8
9#include "ui/gfx/geometry/point.h"
10#include "ui/gfx/geometry/rect.h"
11#include "ui/gfx/geometry/size.h"
12#include "ui/ozone/platform/dri/crtc_state.h"
13#include "ui/ozone/platform/dri/dri_util.h"
14#include "ui/ozone/platform/dri/hardware_display_controller.h"
15#include "ui/ozone/platform/dri/scanout_buffer.h"
16
17namespace ui {
18
19ScreenManager::ScreenManager(DriWrapper* dri,
20                             ScanoutBufferGenerator* buffer_generator)
21    : dri_(dri), buffer_generator_(buffer_generator) {
22}
23
24ScreenManager::~ScreenManager() {
25}
26
27void ScreenManager::AddDisplayController(uint32_t crtc, uint32_t connector) {
28  HardwareDisplayControllers::iterator it = FindDisplayController(crtc);
29  // TODO(dnicoara): Turn this into a DCHECK when async display configuration is
30  // properly supported. (When there can't be a race between forcing initial
31  // display configuration in ScreenManager and NativeDisplayDelegate creating
32  // the display controllers.)
33  if (it != controllers_.end()) {
34    LOG(WARNING) << "Display controller (crtc=" << crtc << ") already present.";
35    return;
36  }
37
38  controllers_.push_back(new HardwareDisplayController(
39      dri_, scoped_ptr<CrtcState>(new CrtcState(dri_, crtc, connector))));
40}
41
42void ScreenManager::RemoveDisplayController(uint32_t crtc) {
43  HardwareDisplayControllers::iterator it = FindDisplayController(crtc);
44  if (it != controllers_.end()) {
45    bool is_mirrored = (*it)->IsMirrored();
46    (*it)->RemoveCrtc(crtc);
47    if (!is_mirrored)
48      controllers_.erase(it);
49  }
50}
51
52bool ScreenManager::ConfigureDisplayController(uint32_t crtc,
53                                               uint32_t connector,
54                                               const gfx::Point& origin,
55                                               const drmModeModeInfo& mode) {
56  gfx::Rect modeset_bounds(
57      origin.x(), origin.y(), mode.hdisplay, mode.vdisplay);
58  HardwareDisplayControllers::iterator it = FindDisplayController(crtc);
59  DCHECK(controllers_.end() != it) << "Display controller (crtc=" << crtc
60                                   << ") doesn't exist.";
61
62  HardwareDisplayController* controller = *it;
63  controller = *it;
64  // If nothing changed just enable the controller. Note, we perform an exact
65  // comparison on the mode since the refresh rate may have changed.
66  if (SameMode(mode, controller->get_mode()) &&
67      origin == controller->origin() && !controller->IsDisabled())
68    return controller->Enable();
69
70  // Either the mode or the location of the display changed, so exit mirror
71  // mode and configure the display independently. If the caller still wants
72  // mirror mode, subsequent calls configuring the other controllers will
73  // restore mirror mode.
74  if (controller->IsMirrored()) {
75    controller =
76        new HardwareDisplayController(dri_, controller->RemoveCrtc(crtc));
77    controllers_.push_back(controller);
78    it = controllers_.end() - 1;
79  }
80
81  HardwareDisplayControllers::iterator mirror =
82      FindActiveDisplayControllerByLocation(modeset_bounds);
83  // Handle mirror mode.
84  if (mirror != controllers_.end() && it != mirror)
85    return HandleMirrorMode(it, mirror, crtc, connector);
86
87  return ModesetDisplayController(controller, origin, mode);
88}
89
90bool ScreenManager::DisableDisplayController(uint32_t crtc) {
91  HardwareDisplayControllers::iterator it = FindDisplayController(crtc);
92  if (it != controllers_.end()) {
93    if ((*it)->IsMirrored()) {
94      HardwareDisplayController* controller =
95          new HardwareDisplayController(dri_, (*it)->RemoveCrtc(crtc));
96      controllers_.push_back(controller);
97    }
98
99    (*it)->Disable();
100    return true;
101  }
102
103  LOG(ERROR) << "Failed to find display controller crtc=" << crtc;
104  return false;
105}
106
107base::WeakPtr<HardwareDisplayController> ScreenManager::GetDisplayController(
108    const gfx::Rect& bounds) {
109  // TODO(dnicoara): Remove hack once TestScreen uses a simple Ozone display
110  // configuration reader and ScreenManager is called from there to create the
111  // one display needed by the content_shell target.
112  if (controllers_.empty())
113    ForceInitializationOfPrimaryDisplay();
114
115  HardwareDisplayControllers::iterator it =
116      FindActiveDisplayControllerByLocation(bounds);
117  if (it != controllers_.end())
118    return (*it)->AsWeakPtr();
119
120  return base::WeakPtr<HardwareDisplayController>();
121}
122
123ScreenManager::HardwareDisplayControllers::iterator
124ScreenManager::FindDisplayController(uint32_t crtc) {
125  for (HardwareDisplayControllers::iterator it = controllers_.begin();
126       it != controllers_.end();
127       ++it) {
128    if ((*it)->HasCrtc(crtc))
129      return it;
130  }
131
132  return controllers_.end();
133}
134
135ScreenManager::HardwareDisplayControllers::iterator
136ScreenManager::FindActiveDisplayControllerByLocation(const gfx::Rect& bounds) {
137  for (HardwareDisplayControllers::iterator it = controllers_.begin();
138       it != controllers_.end();
139       ++it) {
140    gfx::Rect controller_bounds((*it)->origin(), (*it)->GetModeSize());
141    // We don't perform a strict check since content_shell will have windows
142    // smaller than the display size.
143    if (controller_bounds.Contains(bounds) && !(*it)->IsDisabled())
144      return it;
145  }
146
147  return controllers_.end();
148}
149
150void ScreenManager::ForceInitializationOfPrimaryDisplay() {
151  LOG(WARNING) << "Forcing initialization of primary display.";
152  ScopedVector<HardwareDisplayControllerInfo> displays =
153      GetAvailableDisplayControllerInfos(dri_->get_fd());
154
155  DCHECK_NE(0u, displays.size());
156
157  ScopedDrmPropertyPtr dpms(
158      dri_->GetProperty(displays[0]->connector(), "DPMS"));
159  if (dpms)
160    dri_->SetProperty(displays[0]->connector()->connector_id,
161                      dpms->prop_id,
162                      DRM_MODE_DPMS_ON);
163
164  AddDisplayController(displays[0]->crtc()->crtc_id,
165                       displays[0]->connector()->connector_id);
166  ConfigureDisplayController(displays[0]->crtc()->crtc_id,
167                             displays[0]->connector()->connector_id,
168                             gfx::Point(),
169                             displays[0]->connector()->modes[0]);
170}
171
172bool ScreenManager::ModesetDisplayController(
173    HardwareDisplayController* controller,
174    const gfx::Point& origin,
175    const drmModeModeInfo& mode) {
176  controller->set_origin(origin);
177  // Create a surface suitable for the current controller.
178  scoped_refptr<ScanoutBuffer> buffer =
179      buffer_generator_->Create(gfx::Size(mode.hdisplay, mode.vdisplay));
180
181  if (!buffer.get()) {
182    LOG(ERROR) << "Failed to create scanout buffer";
183    return false;
184  }
185
186  if (!controller->Modeset(OverlayPlane(buffer), mode)) {
187    LOG(ERROR) << "Failed to modeset controller";
188    return false;
189  }
190
191  return true;
192}
193
194bool ScreenManager::HandleMirrorMode(
195    HardwareDisplayControllers::iterator original,
196    HardwareDisplayControllers::iterator mirror,
197    uint32_t crtc,
198    uint32_t connector) {
199  (*mirror)->AddCrtc((*original)->RemoveCrtc(crtc));
200  if ((*mirror)->Enable()) {
201    controllers_.erase(original);
202    return true;
203  }
204
205  LOG(ERROR) << "Failed to switch to mirror mode";
206
207  // When things go wrong revert back to the previous configuration since
208  // it is expected that the configuration would not have changed if
209  // things fail.
210  (*original)->AddCrtc((*mirror)->RemoveCrtc(crtc));
211  (*original)->Enable();
212  return false;
213}
214
215}  // namespace ui
216