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/drm/gpu/drm_gpu_display_manager.h"
6 
7 #include <stddef.h>
8 #include <memory>
9 #include <utility>
10 
11 #include "base/logging.h"
12 #include "ui/display/types/display_mode.h"
13 #include "ui/display/types/display_snapshot.h"
14 #include "ui/display/types/gamma_ramp_rgb_entry.h"
15 #include "ui/gfx/linux/drm_util_linux.h"
16 #include "ui/ozone/platform/drm/common/drm_util.h"
17 #include "ui/ozone/platform/drm/gpu/drm_device.h"
18 #include "ui/ozone/platform/drm/gpu/drm_device_manager.h"
19 #include "ui/ozone/platform/drm/gpu/drm_display.h"
20 #include "ui/ozone/platform/drm/gpu/screen_manager.h"
21 
22 namespace ui {
23 
24 namespace {
25 
26 class DisplayComparator {
27  public:
DisplayComparator(const DrmDisplay * display)28   explicit DisplayComparator(const DrmDisplay* display)
29       : drm_(display->drm()),
30         crtc_(display->crtc()),
31         connector_(display->connector()) {}
32 
DisplayComparator(const scoped_refptr<DrmDevice> & drm,uint32_t crtc,uint32_t connector)33   DisplayComparator(const scoped_refptr<DrmDevice>& drm,
34                     uint32_t crtc,
35                     uint32_t connector)
36       : drm_(drm), crtc_(crtc), connector_(connector) {}
37 
operator ()(const std::unique_ptr<DrmDisplay> & other) const38   bool operator()(const std::unique_ptr<DrmDisplay>& other) const {
39     return drm_ == other->drm() && connector_ == other->connector() &&
40            crtc_ == other->crtc();
41   }
42 
43  private:
44   scoped_refptr<DrmDevice> drm_;
45   uint32_t crtc_;
46   uint32_t connector_;
47 };
48 
MatchMode(const display::DisplayMode & display_mode,const drmModeModeInfo & m)49 bool MatchMode(const display::DisplayMode& display_mode,
50                const drmModeModeInfo& m) {
51   return display_mode.size() == ModeSize(m) &&
52          display_mode.refresh_rate() == ModeRefreshRate(m) &&
53          display_mode.is_interlaced() == ModeIsInterlaced(m);
54 }
55 
FindMatchingMode(const std::vector<drmModeModeInfo> modes,const display::DisplayMode & display_mode,drmModeModeInfo * mode)56 bool FindMatchingMode(const std::vector<drmModeModeInfo> modes,
57                       const display::DisplayMode& display_mode,
58                       drmModeModeInfo* mode) {
59   for (const drmModeModeInfo& m : modes) {
60     if (MatchMode(display_mode, m)) {
61       *mode = m;
62       return true;
63     }
64   }
65   return false;
66 }
67 
FindModeForDisplay(drmModeModeInfo * mode_ptr,const display::DisplayMode & display_mode,const std::vector<drmModeModeInfo> & modes,const std::vector<std::unique_ptr<DrmDisplay>> & all_displays)68 bool FindModeForDisplay(
69     drmModeModeInfo* mode_ptr,
70     const display::DisplayMode& display_mode,
71     const std::vector<drmModeModeInfo>& modes,
72     const std::vector<std::unique_ptr<DrmDisplay>>& all_displays) {
73   bool mode_found = FindMatchingMode(modes, display_mode, mode_ptr);
74   if (!mode_found) {
75     // If the display doesn't have the mode natively, then lookup the mode
76     // from other displays and try using it on the current display (some
77     // displays support panel fitting and they can use different modes even
78     // if the mode isn't explicitly declared).
79     for (const auto& other_display : all_displays) {
80       mode_found =
81           FindMatchingMode(other_display->modes(), display_mode, mode_ptr);
82       if (mode_found)
83         break;
84     }
85     if (!mode_found) {
86       LOG(ERROR) << "Failed to find mode: size="
87                  << display_mode.size().ToString()
88                  << " is_interlaced=" << display_mode.is_interlaced()
89                  << " refresh_rate=" << display_mode.refresh_rate();
90     }
91   }
92   return mode_found;
93 }
94 
95 }  // namespace
96 
DrmGpuDisplayManager(ScreenManager * screen_manager,DrmDeviceManager * drm_device_manager)97 DrmGpuDisplayManager::DrmGpuDisplayManager(ScreenManager* screen_manager,
98                                            DrmDeviceManager* drm_device_manager)
99     : screen_manager_(screen_manager),
100       drm_device_manager_(drm_device_manager) {}
101 
102 DrmGpuDisplayManager::~DrmGpuDisplayManager() = default;
103 
SetClearOverlayCacheCallback(base::RepeatingClosure callback)104 void DrmGpuDisplayManager::SetClearOverlayCacheCallback(
105     base::RepeatingClosure callback) {
106   clear_overlay_cache_callback_ = std::move(callback);
107 }
108 
GetDisplays()109 MovableDisplaySnapshots DrmGpuDisplayManager::GetDisplays() {
110   std::vector<std::unique_ptr<DrmDisplay>> old_displays;
111   old_displays.swap(displays_);
112   MovableDisplaySnapshots params_list;
113 
114   const DrmDeviceVector& devices = drm_device_manager_->GetDrmDevices();
115   size_t device_index = 0;
116   for (const auto& drm : devices) {
117     // Receiving a signal that DRM state was updated. Need to reset the plane
118     // manager's resource cache since IDs may have changed.
119     drm->plane_manager()->ResetConnectorsCache(drm->GetResources());
120     auto display_infos = GetAvailableDisplayControllerInfos(drm->get_fd());
121     for (const auto& display_info : display_infos) {
122       auto it = std::find_if(
123           old_displays.begin(), old_displays.end(),
124           DisplayComparator(drm, display_info->crtc()->crtc_id,
125                             display_info->connector()->connector_id));
126       if (it != old_displays.end()) {
127         displays_.push_back(std::move(*it));
128         old_displays.erase(it);
129       } else {
130         displays_.push_back(std::make_unique<DrmDisplay>(drm));
131       }
132 
133       auto display_snapshot =
134           displays_.back()->Update(display_info.get(), device_index);
135       if (display_snapshot) {
136         params_list.push_back(std::move(display_snapshot));
137       } else {
138         displays_.pop_back();
139       }
140     }
141     device_index++;
142   }
143 
144   NotifyScreenManager(displays_, old_displays);
145   return params_list;
146 }
147 
TakeDisplayControl()148 bool DrmGpuDisplayManager::TakeDisplayControl() {
149   const DrmDeviceVector& devices = drm_device_manager_->GetDrmDevices();
150   bool status = true;
151   for (const auto& drm : devices)
152     status &= drm->SetMaster();
153 
154   // Roll-back any successful operation.
155   if (!status) {
156     LOG(ERROR) << "Failed to take control of the display";
157     RelinquishDisplayControl();
158   }
159 
160   return status;
161 }
162 
RelinquishDisplayControl()163 void DrmGpuDisplayManager::RelinquishDisplayControl() {
164   const DrmDeviceVector& devices = drm_device_manager_->GetDrmDevices();
165   for (const auto& drm : devices)
166     drm->DropMaster();
167 }
168 
ConfigureDisplays(const std::vector<display::DisplayConfigurationParams> & config_requests)169 base::flat_map<int64_t, bool> DrmGpuDisplayManager::ConfigureDisplays(
170     const std::vector<display::DisplayConfigurationParams>& config_requests) {
171   base::flat_map<int64_t, bool> statuses;
172   ScreenManager::ControllerConfigsList controllers_to_configure;
173 
174   for (const auto& config : config_requests) {
175     int64_t display_id = config.id;
176     DrmDisplay* display = FindDisplay(display_id);
177     if (!display) {
178       LOG(ERROR) << "There is no display with ID " << display_id;
179       statuses.insert(std::make_pair(display_id, false));
180       continue;
181     }
182 
183     std::unique_ptr<drmModeModeInfo> mode_ptr =
184         config.mode ? std::make_unique<drmModeModeInfo>() : nullptr;
185     if (config.mode) {
186       if (!FindModeForDisplay(mode_ptr.get(), *config.mode.value(),
187                               display->modes(), displays_)) {
188         statuses.insert(std::make_pair(display_id, false));
189         continue;
190       }
191     }
192 
193     scoped_refptr<DrmDevice> drm = display->drm();
194 
195     VLOG(1) << "DRM configuring: device=" << drm->device_path().value()
196             << " crtc=" << display->crtc()
197             << " connector=" << display->connector()
198             << " origin=" << config.origin.ToString() << " size="
199             << (mode_ptr ? ModeSize(*(mode_ptr.get())).ToString() : "0x0")
200             << " refresh_rate=" << (mode_ptr ? mode_ptr->vrefresh : 0) << "Hz";
201 
202     ScreenManager::ControllerConfigParams params(
203         display->display_id(), drm, display->crtc(), display->connector(),
204         config.origin, std::move(mode_ptr));
205     controllers_to_configure.push_back(std::move(params));
206   }
207 
208   if (controllers_to_configure.empty())
209     return statuses;
210 
211   if (clear_overlay_cache_callback_)
212     clear_overlay_cache_callback_.Run();
213 
214   auto config_statuses =
215       screen_manager_->ConfigureDisplayControllers(controllers_to_configure);
216   for (const auto& status : config_statuses) {
217     int64_t display_id = status.first;
218     bool success = status.second;
219     DrmDisplay* display = FindDisplay(display_id);
220     auto config = std::find_if(
221         config_requests.begin(), config_requests.end(),
222         [display_id](const auto& request) { return request.id == display_id; });
223 
224     if (success) {
225       display->SetOrigin(config->origin);
226     } else {
227       if (config->mode) {
228         VLOG(1) << "Failed to enable device="
229                 << display->drm()->device_path().value()
230                 << " crtc=" << display->crtc()
231                 << " connector=" << display->connector();
232       } else {
233         VLOG(1) << "Failed to disable device="
234                 << display->drm()->device_path().value()
235                 << " crtc=" << display->crtc();
236       }
237     }
238 
239     statuses.insert(std::make_pair(display_id, success));
240   }
241 
242   return statuses;
243 }
244 
GetHDCPState(int64_t display_id,display::HDCPState * state,display::ContentProtectionMethod * protection_method)245 bool DrmGpuDisplayManager::GetHDCPState(
246     int64_t display_id,
247     display::HDCPState* state,
248     display::ContentProtectionMethod* protection_method) {
249   DrmDisplay* display = FindDisplay(display_id);
250   if (!display) {
251     LOG(ERROR) << "There is no display with ID " << display_id;
252     return false;
253   }
254 
255   return display->GetHDCPState(state, protection_method);
256 }
257 
SetHDCPState(int64_t display_id,display::HDCPState state,display::ContentProtectionMethod protection_method)258 bool DrmGpuDisplayManager::SetHDCPState(
259     int64_t display_id,
260     display::HDCPState state,
261     display::ContentProtectionMethod protection_method) {
262   DrmDisplay* display = FindDisplay(display_id);
263   if (!display) {
264     LOG(ERROR) << "There is no display with ID " << display_id;
265     return false;
266   }
267 
268   return display->SetHDCPState(state, protection_method);
269 }
270 
SetColorMatrix(int64_t display_id,const std::vector<float> & color_matrix)271 void DrmGpuDisplayManager::SetColorMatrix(
272     int64_t display_id,
273     const std::vector<float>& color_matrix) {
274   DrmDisplay* display = FindDisplay(display_id);
275   if (!display) {
276     LOG(ERROR) << "There is no display with ID " << display_id;
277     return;
278   }
279 
280   display->SetColorMatrix(color_matrix);
281 }
282 
SetBackgroundColor(int64_t display_id,const uint64_t background_color)283 void DrmGpuDisplayManager::SetBackgroundColor(int64_t display_id,
284                                               const uint64_t background_color) {
285   DrmDisplay* display = FindDisplay(display_id);
286   if (!display) {
287     LOG(ERROR) << "There is no display with ID" << display_id;
288     return;
289   }
290 
291   display->SetBackgroundColor(background_color);
292 }
293 
SetGammaCorrection(int64_t display_id,const std::vector<display::GammaRampRGBEntry> & degamma_lut,const std::vector<display::GammaRampRGBEntry> & gamma_lut)294 void DrmGpuDisplayManager::SetGammaCorrection(
295     int64_t display_id,
296     const std::vector<display::GammaRampRGBEntry>& degamma_lut,
297     const std::vector<display::GammaRampRGBEntry>& gamma_lut) {
298   DrmDisplay* display = FindDisplay(display_id);
299   if (!display) {
300     LOG(ERROR) << "There is no display with ID " << display_id;
301     return;
302   }
303   display->SetGammaCorrection(degamma_lut, gamma_lut);
304 }
305 
SetPrivacyScreen(int64_t display_id,bool enabled)306 void DrmGpuDisplayManager::SetPrivacyScreen(int64_t display_id, bool enabled) {
307   DrmDisplay* display = FindDisplay(display_id);
308   if (!display) {
309     LOG(ERROR) << "There is no display with ID " << display_id;
310     return;
311   }
312 
313   display->SetPrivacyScreen(enabled);
314 }
315 
SetColorSpace(int64_t crtc_id,const gfx::ColorSpace & color_space)316 void DrmGpuDisplayManager::SetColorSpace(int64_t crtc_id,
317                                          const gfx::ColorSpace& color_space) {
318   for (const auto& display : displays_) {
319     if (display->crtc() == crtc_id) {
320       display->SetColorSpace(color_space);
321       return;
322     }
323   }
324   LOG(ERROR) << __func__ << " there is no display with CRTC ID " << crtc_id;
325 }
326 
FindDisplay(int64_t display_id)327 DrmDisplay* DrmGpuDisplayManager::FindDisplay(int64_t display_id) {
328   for (const auto& display : displays_) {
329     if (display->display_id() == display_id)
330       return display.get();
331   }
332 
333   return nullptr;
334 }
335 
NotifyScreenManager(const std::vector<std::unique_ptr<DrmDisplay>> & new_displays,const std::vector<std::unique_ptr<DrmDisplay>> & old_displays) const336 void DrmGpuDisplayManager::NotifyScreenManager(
337     const std::vector<std::unique_ptr<DrmDisplay>>& new_displays,
338     const std::vector<std::unique_ptr<DrmDisplay>>& old_displays) const {
339   ScreenManager::CrtcsWithDrmList controllers_to_remove;
340   for (const auto& old_display : old_displays) {
341     auto it = std::find_if(new_displays.begin(), new_displays.end(),
342                            DisplayComparator(old_display.get()));
343 
344     if (it == new_displays.end()) {
345       controllers_to_remove.emplace_back(old_display->crtc(),
346                                          old_display->drm());
347     }
348   }
349   if (!controllers_to_remove.empty())
350     screen_manager_->RemoveDisplayControllers(controllers_to_remove);
351 
352   for (const auto& new_display : new_displays) {
353     auto it = std::find_if(old_displays.begin(), old_displays.end(),
354                            DisplayComparator(new_display.get()));
355 
356     if (it == old_displays.end()) {
357       screen_manager_->AddDisplayController(
358           new_display->drm(), new_display->crtc(), new_display->connector());
359     }
360   }
361 }
362 
363 }  // namespace ui
364