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