• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #undef NDEBUG /* Required for assert to work */
18 
19 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
20 #define LOG_TAG "hwc-drm-atomic-state-manager"
21 
22 #include "DrmAtomicStateManager.h"
23 
24 #include <drm/drm_mode.h>
25 #include <pthread.h>
26 #include <sched.h>
27 #include <sync/sync.h>
28 #include <utils/Trace.h>
29 
30 #include <array>
31 #include <cassert>
32 #include <cstdlib>
33 #include <ctime>
34 #include <sstream>
35 #include <vector>
36 
37 #include "drm/DrmCrtc.h"
38 #include "drm/DrmDevice.h"
39 #include "drm/DrmPlane.h"
40 #include "drm/DrmUnique.h"
41 #include "utils/log.h"
42 
43 namespace android {
44 
45 // NOLINTNEXTLINE (readability-function-cognitive-complexity): Fixme
CommitFrame(AtomicCommitArgs & args)46 auto DrmAtomicStateManager::CommitFrame(AtomicCommitArgs &args) -> int {
47   ATRACE_CALL();
48 
49   if (args.active && *args.active == active_frame_state_.crtc_active_state) {
50     /* Don't set the same state twice */
51     args.active.reset();
52   }
53 
54   if (!args.HasInputs()) {
55     /* nothing to do */
56     return 0;
57   }
58 
59   if (!active_frame_state_.crtc_active_state) {
60     /* Force activate display */
61     args.active = true;
62   }
63 
64   auto new_frame_state = NewFrameState();
65 
66   auto *drm = pipe_->device;
67   auto *connector = pipe_->connector->Get();
68   auto *crtc = pipe_->crtc->Get();
69 
70   auto pset = MakeDrmModeAtomicReqUnique();
71   if (!pset) {
72     ALOGE("Failed to allocate property set");
73     return -ENOMEM;
74   }
75 
76   int out_fence = -1;
77   if (!crtc->GetOutFencePtrProperty().AtomicSet(*pset, uint64_t(&out_fence))) {
78     return -EINVAL;
79   }
80 
81   bool nonblock = true;
82 
83   if (args.active) {
84     nonblock = false;
85     new_frame_state.crtc_active_state = *args.active;
86     if (!crtc->GetActiveProperty().AtomicSet(*pset, *args.active ? 1 : 0) ||
87         !connector->GetCrtcIdProperty().AtomicSet(*pset, crtc->GetId())) {
88       return -EINVAL;
89     }
90   }
91 
92   if (args.display_mode) {
93     new_frame_state.mode_blob = args.display_mode.value().CreateModeBlob(*drm);
94 
95     if (!new_frame_state.mode_blob) {
96       ALOGE("Failed to create mode_blob");
97       return -EINVAL;
98     }
99 
100     if (!crtc->GetModeProperty().AtomicSet(*pset, *new_frame_state.mode_blob)) {
101       return -EINVAL;
102     }
103   }
104 
105   auto unused_planes = new_frame_state.used_planes;
106 
107   if (args.composition) {
108     new_frame_state.used_planes.clear();
109 
110     for (auto &joining : args.composition->plan) {
111       DrmPlane *plane = joining.plane->Get();
112       LayerData &layer = joining.layer;
113 
114       new_frame_state.used_framebuffers.emplace_back(layer.fb);
115       new_frame_state.used_planes.emplace_back(joining.plane);
116 
117       /* Remove from 'unused' list, since plane is re-used */
118       auto &v = unused_planes;
119       v.erase(std::remove(v.begin(), v.end(), joining.plane), v.end());
120 
121       if (plane->AtomicSetState(*pset, layer, joining.z_pos, crtc->GetId()) !=
122           0) {
123         return -EINVAL;
124       }
125     }
126   }
127 
128   if (args.composition) {
129     for (auto &plane : unused_planes) {
130       if (plane->Get()->AtomicDisablePlane(*pset) != 0) {
131         return -EINVAL;
132       }
133     }
134   }
135 
136   uint32_t flags = DRM_MODE_ATOMIC_ALLOW_MODESET;
137 
138   if (args.test_only) {
139     return drmModeAtomicCommit(drm->GetFd(), pset.get(),
140                                flags | DRM_MODE_ATOMIC_TEST_ONLY, drm);
141   }
142 
143   if (last_present_fence_) {
144     ATRACE_NAME("WaitPriorFramePresented");
145 
146     constexpr int kTimeoutMs = 500;
147     int err = sync_wait(last_present_fence_.Get(), kTimeoutMs);
148     if (err != 0) {
149       ALOGE("sync_wait(fd=%i) returned: %i (errno: %i)",
150             last_present_fence_.Get(), err, errno);
151     }
152 
153     CleanupPriorFrameResources();
154   }
155 
156   if (nonblock) {
157     flags |= DRM_MODE_ATOMIC_NONBLOCK;
158   }
159 
160   int err = drmModeAtomicCommit(drm->GetFd(), pset.get(), flags, drm);
161 
162   if (err != 0) {
163     ALOGE("Failed to commit pset ret=%d\n", err);
164     return err;
165   }
166 
167   if (nonblock) {
168     last_present_fence_ = UniqueFd::Dup(out_fence);
169     staged_frame_state_ = std::move(new_frame_state);
170     frames_staged_++;
171     ptt_->Notify();
172   } else {
173     active_frame_state_ = std::move(new_frame_state);
174   }
175 
176   if (args.display_mode) {
177     /* TODO(nobody): we still need this for synthetic vsync, remove after
178      * vsync reworked */
179     connector->SetActiveMode(*args.display_mode);
180   }
181 
182   args.out_fence = UniqueFd(out_fence);
183 
184   return 0;
185 }
186 
PresentTrackerThread(DrmAtomicStateManager * st_man)187 PresentTrackerThread::PresentTrackerThread(DrmAtomicStateManager *st_man)
188     : st_man_(st_man),
189       mutex_(&st_man_->pipe_->device->GetResMan().GetMainLock()) {
190   pt_ = std::thread(&PresentTrackerThread::PresentTrackerThreadFn, this);
191 }
192 
~PresentTrackerThread()193 PresentTrackerThread::~PresentTrackerThread() {
194   ALOGI("PresentTrackerThread successfully destroyed");
195 }
196 
PresentTrackerThreadFn()197 void PresentTrackerThread::PresentTrackerThreadFn() {
198   /* object should be destroyed on thread exit */
199   auto self = std::unique_ptr<PresentTrackerThread>(this);
200 
201   int tracking_at_the_moment = -1;
202 
203   for (;;) {
204     UniqueFd present_fence;
205 
206     {
207       std::unique_lock lk(*mutex_);
208       cv_.wait(lk, [&] {
209         return st_man_ == nullptr ||
210                st_man_->frames_staged_ > tracking_at_the_moment;
211       });
212 
213       if (st_man_ == nullptr) {
214         break;
215       }
216 
217       tracking_at_the_moment = st_man_->frames_staged_;
218 
219       present_fence = UniqueFd::Dup(st_man_->last_present_fence_.Get());
220       if (!present_fence) {
221         continue;
222       }
223     }
224 
225     {
226       ATRACE_NAME("AsyncWaitForBuffersSwap");
227       constexpr int kTimeoutMs = 500;
228       int err = sync_wait(present_fence.Get(), kTimeoutMs);
229       if (err != 0) {
230         ALOGE("sync_wait(fd=%i) returned: %i (errno: %i)", present_fence.Get(),
231               err, errno);
232       }
233     }
234 
235     {
236       std::unique_lock lk(*mutex_);
237       if (st_man_ == nullptr) {
238         break;
239       }
240 
241       /* If resources is already cleaned-up by main thread, skip */
242       if (tracking_at_the_moment > st_man_->frames_tracked_) {
243         st_man_->CleanupPriorFrameResources();
244       }
245     }
246   }
247 }
248 
CleanupPriorFrameResources()249 void DrmAtomicStateManager::CleanupPriorFrameResources() {
250   assert(frames_staged_ - frames_tracked_ == 1);
251   assert(last_present_fence_);
252 
253   ATRACE_NAME("CleanupPriorFrameResources");
254   frames_tracked_++;
255   active_frame_state_ = std::move(staged_frame_state_);
256   last_present_fence_ = {};
257 }
258 
ExecuteAtomicCommit(AtomicCommitArgs & args)259 auto DrmAtomicStateManager::ExecuteAtomicCommit(AtomicCommitArgs &args) -> int {
260   int err = CommitFrame(args);
261 
262   if (!args.test_only) {
263     if (err != 0) {
264       ALOGE("Composite failed for pipeline %s",
265             pipe_->connector->Get()->GetName().c_str());
266       // Disable the hw used by the last active composition. This allows us to
267       // signal the release fences from that composition to avoid hanging.
268       AtomicCommitArgs cl_args{};
269       cl_args.composition = std::make_shared<DrmKmsPlan>();
270       if (CommitFrame(cl_args) != 0) {
271         ALOGE("Failed to clean-up active composition for pipeline %s",
272               pipe_->connector->Get()->GetName().c_str());
273       }
274       return err;
275     }
276   }
277 
278   return err;
279 }  // namespace android
280 
ActivateDisplayUsingDPMS()281 auto DrmAtomicStateManager::ActivateDisplayUsingDPMS() -> int {
282   return drmModeConnectorSetProperty(pipe_->device->GetFd(),
283                                      pipe_->connector->Get()->GetId(),
284                                      pipe_->connector->Get()
285                                          ->GetDpmsProperty()
286                                          .id(),
287                                      DRM_MODE_DPMS_ON);
288 }
289 
290 }  // namespace android
291