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/hardware_display_plane_manager_legacy.h"
6 
7 #include <errno.h>
8 #include <sync/sync.h>
9 #include <memory>
10 #include <utility>
11 
12 #include "base/bind.h"
13 #include "base/logging.h"
14 #include "base/posix/eintr_wrapper.h"
15 #include "base/task/post_task.h"
16 #include "base/task/thread_pool.h"
17 #include "ui/gfx/gpu_fence.h"
18 #include "ui/gfx/presentation_feedback.h"
19 #include "ui/ozone/platform/drm/gpu/crtc_controller.h"
20 #include "ui/ozone/platform/drm/gpu/drm_device.h"
21 #include "ui/ozone/platform/drm/gpu/drm_framebuffer.h"
22 #include "ui/ozone/platform/drm/gpu/hardware_display_plane.h"
23 #include "ui/ozone/platform/drm/gpu/page_flip_request.h"
24 
25 namespace ui {
26 
27 namespace {
28 
29 // We currently wait for the fences serially, but it's possible
30 // that merging the fences and waiting on the merged fence fd
31 // is more efficient. We should revisit once we have more info.
WaitForPlaneFences(ui::DrmOverlayPlaneList planes)32 ui::DrmOverlayPlaneList WaitForPlaneFences(ui::DrmOverlayPlaneList planes) {
33   for (const auto& plane : planes) {
34     if (plane.gpu_fence)
35       plane.gpu_fence->Wait();
36   }
37   return planes;
38 }
39 
40 }  // namespace
41 
HardwareDisplayPlaneManagerLegacy(DrmDevice * drm)42 HardwareDisplayPlaneManagerLegacy::HardwareDisplayPlaneManagerLegacy(
43     DrmDevice* drm)
44     : HardwareDisplayPlaneManager(drm) {}
45 
46 HardwareDisplayPlaneManagerLegacy::~HardwareDisplayPlaneManagerLegacy() =
47     default;
48 
Commit(CommitRequest commit_request,uint32_t flags)49 bool HardwareDisplayPlaneManagerLegacy::Commit(CommitRequest commit_request,
50                                                uint32_t flags) {
51   if (flags & DRM_MODE_ATOMIC_TEST_ONLY)
52     // Legacy DRM does not support testing.
53     return true;
54 
55   bool status = true;
56   for (const auto& crtc_request : commit_request) {
57     if (crtc_request.should_enable()) {
58       // Overlays are not supported in legacy hence why we're only looking at
59       // the primary plane.
60       uint32_t fb_id = DrmOverlayPlane::GetPrimaryPlane(crtc_request.overlays())
61                            ->buffer->opaque_framebuffer_id();
62       status &=
63           drm_->SetCrtc(crtc_request.crtc_id(), fb_id,
64                         std::vector<uint32_t>(1, crtc_request.connector_id()),
65                         crtc_request.mode());
66     } else {
67       drm_->DisableCrtc(crtc_request.crtc_id());
68     }
69   }
70 
71   if (status)
72     UpdateCrtcAndPlaneStatesAfterModeset(commit_request);
73 
74   return status;
75 }
76 
Commit(HardwareDisplayPlaneList * plane_list,scoped_refptr<PageFlipRequest> page_flip_request,std::unique_ptr<gfx::GpuFence> * out_fence)77 bool HardwareDisplayPlaneManagerLegacy::Commit(
78     HardwareDisplayPlaneList* plane_list,
79     scoped_refptr<PageFlipRequest> page_flip_request,
80     std::unique_ptr<gfx::GpuFence>* out_fence) {
81   bool test_only = !page_flip_request;
82   if (test_only) {
83     for (HardwareDisplayPlane* plane : plane_list->plane_list) {
84       plane->set_in_use(false);
85     }
86     plane_list->plane_list.clear();
87     plane_list->legacy_page_flips.clear();
88     return true;
89   }
90   if (plane_list->plane_list.empty())  // No assigned planes, nothing to do.
91     return true;
92 
93   bool ret = true;
94   for (const auto& flip : plane_list->legacy_page_flips) {
95     if (!drm_->PageFlip(flip.crtc_id, flip.framebuffer, page_flip_request)) {
96       // 1) Permission Denied is a legitimate error.
97       // 2) EBUSY or ENODEV are possible if we're page flipping a disconnected
98       // CRTC. Pretend we're fine since a hotplug event is supposed to be on
99       // its way.
100       // NOTE: We could be getting EBUSY if we're trying to page flip a CRTC
101       // that has a pending page flip, however the contract is that the caller
102       // will never attempt this (since the caller should be waiting for the
103       // page flip completion message).
104       if (errno != EACCES && errno != EBUSY && errno != ENODEV) {
105         PLOG(ERROR) << "Cannot page flip: crtc=" << flip.crtc_id
106                     << " framebuffer=" << flip.framebuffer;
107         ret = false;
108       }
109     }
110   }
111 
112   if (ret) {
113     plane_list->plane_list.swap(plane_list->old_plane_list);
114     plane_list->plane_list.clear();
115     plane_list->legacy_page_flips.clear();
116   } else {
117     ResetCurrentPlaneList(plane_list);
118   }
119 
120   return ret;
121 }
122 
DisableOverlayPlanes(HardwareDisplayPlaneList * plane_list)123 bool HardwareDisplayPlaneManagerLegacy::DisableOverlayPlanes(
124     HardwareDisplayPlaneList* plane_list) {
125   // We're never going to ship legacy pageflip with overlays enabled.
126   DCHECK(std::find_if(plane_list->old_plane_list.begin(),
127                       plane_list->old_plane_list.end(),
128                       [](HardwareDisplayPlane* plane) {
129                         return plane->type() == DRM_PLANE_TYPE_OVERLAY;
130                       }) == plane_list->old_plane_list.end());
131   return true;
132 }
133 
SetColorCorrectionOnAllCrtcPlanes(uint32_t crtc_id,ScopedDrmColorCtmPtr ctm_blob_data)134 bool HardwareDisplayPlaneManagerLegacy::SetColorCorrectionOnAllCrtcPlanes(
135     uint32_t crtc_id,
136     ScopedDrmColorCtmPtr ctm_blob_data) {
137   NOTREACHED()
138       << "HardwareDisplayPlaneManagerLegacy doesn't support per plane CTM";
139   return false;
140 }
141 
ValidatePrimarySize(const DrmOverlayPlane & primary,const drmModeModeInfo & mode)142 bool HardwareDisplayPlaneManagerLegacy::ValidatePrimarySize(
143     const DrmOverlayPlane& primary,
144     const drmModeModeInfo& mode) {
145   DCHECK(primary.buffer.get());
146 
147   return primary.buffer->size() == gfx::Size(mode.hdisplay, mode.vdisplay);
148 }
149 
RequestPlanesReadyCallback(DrmOverlayPlaneList planes,base::OnceCallback<void (DrmOverlayPlaneList planes)> callback)150 void HardwareDisplayPlaneManagerLegacy::RequestPlanesReadyCallback(
151     DrmOverlayPlaneList planes,
152     base::OnceCallback<void(DrmOverlayPlaneList planes)> callback) {
153   base::ThreadPool::PostTaskAndReplyWithResult(
154       FROM_HERE,
155       {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
156       base::BindOnce(&WaitForPlaneFences, std::move(planes)),
157       std::move(callback));
158 }
159 
InitializePlanes()160 bool HardwareDisplayPlaneManagerLegacy::InitializePlanes() {
161   ScopedDrmPlaneResPtr plane_resources = drm_->GetPlaneResources();
162   if (!plane_resources) {
163     PLOG(ERROR) << "Failed to get plane resources.";
164     return false;
165   }
166 
167   for (uint32_t i = 0; i < plane_resources->count_planes; ++i) {
168     std::unique_ptr<HardwareDisplayPlane> plane(
169         CreatePlane(plane_resources->planes[i]));
170 
171     if (!plane->Initialize(drm_))
172       continue;
173 
174     // Overlays are not supported on the legacy path, so ignore all overlay
175     // planes.
176     if (plane->type() == DRM_PLANE_TYPE_OVERLAY)
177       continue;
178 
179     planes_.push_back(std::move(plane));
180   }
181 
182   return true;
183 }
184 
SetPlaneData(HardwareDisplayPlaneList * plane_list,HardwareDisplayPlane * hw_plane,const DrmOverlayPlane & overlay,uint32_t crtc_id,const gfx::Rect & src_rect)185 bool HardwareDisplayPlaneManagerLegacy::SetPlaneData(
186     HardwareDisplayPlaneList* plane_list,
187     HardwareDisplayPlane* hw_plane,
188     const DrmOverlayPlane& overlay,
189     uint32_t crtc_id,
190     const gfx::Rect& src_rect) {
191   // Legacy modesetting rejects transforms.
192   if (overlay.plane_transform != gfx::OVERLAY_TRANSFORM_NONE)
193     return false;
194 
195   if (plane_list->legacy_page_flips.empty() ||
196       plane_list->legacy_page_flips.back().crtc_id != crtc_id) {
197     plane_list->legacy_page_flips.emplace_back(
198         crtc_id, overlay.buffer->opaque_framebuffer_id());
199   } else {
200     return false;
201   }
202 
203   return true;
204 }
205 
IsCompatible(HardwareDisplayPlane * plane,const DrmOverlayPlane & overlay,uint32_t crtc_index) const206 bool HardwareDisplayPlaneManagerLegacy::IsCompatible(
207     HardwareDisplayPlane* plane,
208     const DrmOverlayPlane& overlay,
209     uint32_t crtc_index) const {
210   if (plane->type() == DRM_PLANE_TYPE_CURSOR ||
211       !plane->CanUseForCrtc(crtc_index))
212     return false;
213 
214   // When using legacy kms we always scanout only one plane (the primary),
215   // and we always use the opaque fb. Refer to SetPlaneData above.
216   const uint32_t format = overlay.buffer->opaque_framebuffer_pixel_format();
217   return plane->IsSupportedFormat(format);
218 }
219 
CommitColorMatrix(const CrtcProperties & crtc_props)220 bool HardwareDisplayPlaneManagerLegacy::CommitColorMatrix(
221     const CrtcProperties& crtc_props) {
222   return drm_->SetObjectProperty(crtc_props.id, DRM_MODE_OBJECT_CRTC,
223                                  crtc_props.ctm.id, crtc_props.ctm.value);
224 }
225 
CommitGammaCorrection(const CrtcProperties & crtc_props)226 bool HardwareDisplayPlaneManagerLegacy::CommitGammaCorrection(
227     const CrtcProperties& crtc_props) {
228   DCHECK(crtc_props.degamma_lut.id || crtc_props.gamma_lut.id);
229 
230   if (crtc_props.degamma_lut.id) {
231     int ret = drm_->SetObjectProperty(crtc_props.id, DRM_MODE_OBJECT_CRTC,
232                                       crtc_props.degamma_lut.id,
233                                       crtc_props.degamma_lut.value);
234     if (ret < 0) {
235       LOG(ERROR) << "Failed to set DEGAMMA_LUT property for crtc="
236                  << crtc_props.id;
237       return false;
238     }
239   }
240 
241   if (crtc_props.gamma_lut.id) {
242     int ret = drm_->SetObjectProperty(crtc_props.id, DRM_MODE_OBJECT_CRTC,
243                                       crtc_props.gamma_lut.id,
244                                       crtc_props.gamma_lut.value);
245     if (ret < 0) {
246       LOG(ERROR) << "Failed to set GAMMA_LUT property for crtc="
247                  << crtc_props.id;
248       return false;
249     }
250   }
251 
252   return true;
253 }
254 
255 }  // namespace ui
256