.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright (C) 2014 Red Hat |
---|
3 | 4 | * Author: Rob Clark <robdclark@gmail.com> |
---|
4 | | - * |
---|
5 | | - * This program is free software; you can redistribute it and/or modify it |
---|
6 | | - * under the terms of the GNU General Public License version 2 as published by |
---|
7 | | - * the Free Software Foundation. |
---|
8 | | - * |
---|
9 | | - * This program is distributed in the hope that it will be useful, but WITHOUT |
---|
10 | | - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
---|
11 | | - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
---|
12 | | - * more details. |
---|
13 | | - * |
---|
14 | | - * You should have received a copy of the GNU General Public License along with |
---|
15 | | - * this program. If not, see <http://www.gnu.org/licenses/>. |
---|
16 | 5 | */ |
---|
17 | 6 | |
---|
| 7 | +#include <drm/drm_atomic_uapi.h> |
---|
| 8 | +#include <drm/drm_gem_framebuffer_helper.h> |
---|
| 9 | +#include <drm/drm_vblank.h> |
---|
| 10 | + |
---|
| 11 | +#include "msm_atomic_trace.h" |
---|
18 | 12 | #include "msm_drv.h" |
---|
19 | 13 | #include "msm_gem.h" |
---|
20 | 14 | #include "msm_kms.h" |
---|
21 | | - |
---|
22 | | -static void msm_atomic_wait_for_commit_done(struct drm_device *dev, |
---|
23 | | - struct drm_atomic_state *old_state) |
---|
24 | | -{ |
---|
25 | | - struct drm_crtc *crtc; |
---|
26 | | - struct drm_crtc_state *new_crtc_state; |
---|
27 | | - struct msm_drm_private *priv = old_state->dev->dev_private; |
---|
28 | | - struct msm_kms *kms = priv->kms; |
---|
29 | | - int i; |
---|
30 | | - |
---|
31 | | - for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) { |
---|
32 | | - if (!new_crtc_state->active) |
---|
33 | | - continue; |
---|
34 | | - |
---|
35 | | - if (drm_crtc_vblank_get(crtc)) |
---|
36 | | - continue; |
---|
37 | | - |
---|
38 | | - kms->funcs->wait_for_crtc_commit_done(kms, crtc); |
---|
39 | | - |
---|
40 | | - drm_crtc_vblank_put(crtc); |
---|
41 | | - } |
---|
42 | | -} |
---|
43 | 15 | |
---|
44 | 16 | int msm_atomic_prepare_fb(struct drm_plane *plane, |
---|
45 | 17 | struct drm_plane_state *new_state) |
---|
46 | 18 | { |
---|
47 | 19 | struct msm_drm_private *priv = plane->dev->dev_private; |
---|
48 | 20 | struct msm_kms *kms = priv->kms; |
---|
49 | | - struct drm_gem_object *obj; |
---|
50 | | - struct msm_gem_object *msm_obj; |
---|
51 | | - struct dma_fence *fence; |
---|
52 | 21 | |
---|
53 | 22 | if (!new_state->fb) |
---|
54 | 23 | return 0; |
---|
55 | 24 | |
---|
56 | | - obj = msm_framebuffer_bo(new_state->fb, 0); |
---|
57 | | - msm_obj = to_msm_bo(obj); |
---|
58 | | - fence = reservation_object_get_excl_rcu(msm_obj->resv); |
---|
59 | | - |
---|
60 | | - drm_atomic_set_fence_for_plane(new_state, fence); |
---|
| 25 | + drm_gem_fb_prepare_fb(plane, new_state); |
---|
61 | 26 | |
---|
62 | 27 | return msm_framebuffer_prepare(new_state->fb, kms->aspace); |
---|
| 28 | +} |
---|
| 29 | + |
---|
| 30 | +/* |
---|
| 31 | + * Helpers to control vblanks while we flush.. basically just to ensure |
---|
| 32 | + * that vblank accounting is switched on, so we get valid seqn/timestamp |
---|
| 33 | + * on pageflip events (if requested) |
---|
| 34 | + */ |
---|
| 35 | + |
---|
| 36 | +static void vblank_get(struct msm_kms *kms, unsigned crtc_mask) |
---|
| 37 | +{ |
---|
| 38 | + struct drm_crtc *crtc; |
---|
| 39 | + |
---|
| 40 | + for_each_crtc_mask(kms->dev, crtc, crtc_mask) { |
---|
| 41 | + if (!crtc->state->active) |
---|
| 42 | + continue; |
---|
| 43 | + drm_crtc_vblank_get(crtc); |
---|
| 44 | + } |
---|
| 45 | +} |
---|
| 46 | + |
---|
| 47 | +static void vblank_put(struct msm_kms *kms, unsigned crtc_mask) |
---|
| 48 | +{ |
---|
| 49 | + struct drm_crtc *crtc; |
---|
| 50 | + |
---|
| 51 | + for_each_crtc_mask(kms->dev, crtc, crtc_mask) { |
---|
| 52 | + if (!crtc->state->active) |
---|
| 53 | + continue; |
---|
| 54 | + drm_crtc_vblank_put(crtc); |
---|
| 55 | + } |
---|
| 56 | +} |
---|
| 57 | + |
---|
| 58 | +static void msm_atomic_async_commit(struct msm_kms *kms, int crtc_idx) |
---|
| 59 | +{ |
---|
| 60 | + unsigned crtc_mask = BIT(crtc_idx); |
---|
| 61 | + |
---|
| 62 | + trace_msm_atomic_async_commit_start(crtc_mask); |
---|
| 63 | + |
---|
| 64 | + mutex_lock(&kms->commit_lock); |
---|
| 65 | + |
---|
| 66 | + if (!(kms->pending_crtc_mask & crtc_mask)) { |
---|
| 67 | + mutex_unlock(&kms->commit_lock); |
---|
| 68 | + goto out; |
---|
| 69 | + } |
---|
| 70 | + |
---|
| 71 | + kms->pending_crtc_mask &= ~crtc_mask; |
---|
| 72 | + |
---|
| 73 | + kms->funcs->enable_commit(kms); |
---|
| 74 | + |
---|
| 75 | + vblank_get(kms, crtc_mask); |
---|
| 76 | + |
---|
| 77 | + /* |
---|
| 78 | + * Flush hardware updates: |
---|
| 79 | + */ |
---|
| 80 | + trace_msm_atomic_flush_commit(crtc_mask); |
---|
| 81 | + kms->funcs->flush_commit(kms, crtc_mask); |
---|
| 82 | + mutex_unlock(&kms->commit_lock); |
---|
| 83 | + |
---|
| 84 | + /* |
---|
| 85 | + * Wait for flush to complete: |
---|
| 86 | + */ |
---|
| 87 | + trace_msm_atomic_wait_flush_start(crtc_mask); |
---|
| 88 | + kms->funcs->wait_flush(kms, crtc_mask); |
---|
| 89 | + trace_msm_atomic_wait_flush_finish(crtc_mask); |
---|
| 90 | + |
---|
| 91 | + vblank_put(kms, crtc_mask); |
---|
| 92 | + |
---|
| 93 | + mutex_lock(&kms->commit_lock); |
---|
| 94 | + kms->funcs->complete_commit(kms, crtc_mask); |
---|
| 95 | + mutex_unlock(&kms->commit_lock); |
---|
| 96 | + kms->funcs->disable_commit(kms); |
---|
| 97 | + |
---|
| 98 | +out: |
---|
| 99 | + trace_msm_atomic_async_commit_finish(crtc_mask); |
---|
| 100 | +} |
---|
| 101 | + |
---|
| 102 | +static enum hrtimer_restart msm_atomic_pending_timer(struct hrtimer *t) |
---|
| 103 | +{ |
---|
| 104 | + struct msm_pending_timer *timer = container_of(t, |
---|
| 105 | + struct msm_pending_timer, timer); |
---|
| 106 | + struct msm_drm_private *priv = timer->kms->dev->dev_private; |
---|
| 107 | + |
---|
| 108 | + queue_work(priv->wq, &timer->work); |
---|
| 109 | + |
---|
| 110 | + return HRTIMER_NORESTART; |
---|
| 111 | +} |
---|
| 112 | + |
---|
| 113 | +static void msm_atomic_pending_work(struct work_struct *work) |
---|
| 114 | +{ |
---|
| 115 | + struct msm_pending_timer *timer = container_of(work, |
---|
| 116 | + struct msm_pending_timer, work); |
---|
| 117 | + |
---|
| 118 | + msm_atomic_async_commit(timer->kms, timer->crtc_idx); |
---|
| 119 | +} |
---|
| 120 | + |
---|
| 121 | +void msm_atomic_init_pending_timer(struct msm_pending_timer *timer, |
---|
| 122 | + struct msm_kms *kms, int crtc_idx) |
---|
| 123 | +{ |
---|
| 124 | + timer->kms = kms; |
---|
| 125 | + timer->crtc_idx = crtc_idx; |
---|
| 126 | + hrtimer_init(&timer->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); |
---|
| 127 | + timer->timer.function = msm_atomic_pending_timer; |
---|
| 128 | + INIT_WORK(&timer->work, msm_atomic_pending_work); |
---|
| 129 | +} |
---|
| 130 | + |
---|
| 131 | +static bool can_do_async(struct drm_atomic_state *state, |
---|
| 132 | + struct drm_crtc **async_crtc) |
---|
| 133 | +{ |
---|
| 134 | + struct drm_connector_state *connector_state; |
---|
| 135 | + struct drm_connector *connector; |
---|
| 136 | + struct drm_crtc_state *crtc_state; |
---|
| 137 | + struct drm_crtc *crtc; |
---|
| 138 | + int i, num_crtcs = 0; |
---|
| 139 | + |
---|
| 140 | + if (!(state->legacy_cursor_update || state->async_update)) |
---|
| 141 | + return false; |
---|
| 142 | + |
---|
| 143 | + /* any connector change, means slow path: */ |
---|
| 144 | + for_each_new_connector_in_state(state, connector, connector_state, i) |
---|
| 145 | + return false; |
---|
| 146 | + |
---|
| 147 | + for_each_new_crtc_in_state(state, crtc, crtc_state, i) { |
---|
| 148 | + if (drm_atomic_crtc_needs_modeset(crtc_state)) |
---|
| 149 | + return false; |
---|
| 150 | + if (++num_crtcs > 1) |
---|
| 151 | + return false; |
---|
| 152 | + *async_crtc = crtc; |
---|
| 153 | + } |
---|
| 154 | + |
---|
| 155 | + return true; |
---|
| 156 | +} |
---|
| 157 | + |
---|
| 158 | +/* Get bitmask of crtcs that will need to be flushed. The bitmask |
---|
| 159 | + * can be used with for_each_crtc_mask() iterator, to iterate |
---|
| 160 | + * effected crtcs without needing to preserve the atomic state. |
---|
| 161 | + */ |
---|
| 162 | +static unsigned get_crtc_mask(struct drm_atomic_state *state) |
---|
| 163 | +{ |
---|
| 164 | + struct drm_crtc_state *crtc_state; |
---|
| 165 | + struct drm_crtc *crtc; |
---|
| 166 | + unsigned i, mask = 0; |
---|
| 167 | + |
---|
| 168 | + for_each_new_crtc_in_state(state, crtc, crtc_state, i) |
---|
| 169 | + mask |= drm_crtc_mask(crtc); |
---|
| 170 | + |
---|
| 171 | + return mask; |
---|
63 | 172 | } |
---|
64 | 173 | |
---|
65 | 174 | void msm_atomic_commit_tail(struct drm_atomic_state *state) |
---|
.. | .. |
---|
67 | 176 | struct drm_device *dev = state->dev; |
---|
68 | 177 | struct msm_drm_private *priv = dev->dev_private; |
---|
69 | 178 | struct msm_kms *kms = priv->kms; |
---|
| 179 | + struct drm_crtc *async_crtc = NULL; |
---|
| 180 | + unsigned crtc_mask = get_crtc_mask(state); |
---|
| 181 | + bool async = kms->funcs->vsync_time && |
---|
| 182 | + can_do_async(state, &async_crtc); |
---|
70 | 183 | |
---|
| 184 | + trace_msm_atomic_commit_tail_start(async, crtc_mask); |
---|
| 185 | + |
---|
| 186 | + kms->funcs->enable_commit(kms); |
---|
| 187 | + |
---|
| 188 | + /* |
---|
| 189 | + * Ensure any previous (potentially async) commit has |
---|
| 190 | + * completed: |
---|
| 191 | + */ |
---|
| 192 | + trace_msm_atomic_wait_flush_start(crtc_mask); |
---|
| 193 | + kms->funcs->wait_flush(kms, crtc_mask); |
---|
| 194 | + trace_msm_atomic_wait_flush_finish(crtc_mask); |
---|
| 195 | + |
---|
| 196 | + mutex_lock(&kms->commit_lock); |
---|
| 197 | + |
---|
| 198 | + /* |
---|
| 199 | + * Now that there is no in-progress flush, prepare the |
---|
| 200 | + * current update: |
---|
| 201 | + */ |
---|
71 | 202 | kms->funcs->prepare_commit(kms, state); |
---|
72 | 203 | |
---|
| 204 | + /* |
---|
| 205 | + * Push atomic updates down to hardware: |
---|
| 206 | + */ |
---|
73 | 207 | drm_atomic_helper_commit_modeset_disables(dev, state); |
---|
74 | | - |
---|
75 | 208 | drm_atomic_helper_commit_planes(dev, state, 0); |
---|
76 | | - |
---|
77 | 209 | drm_atomic_helper_commit_modeset_enables(dev, state); |
---|
78 | 210 | |
---|
79 | | - if (kms->funcs->commit) { |
---|
80 | | - DRM_DEBUG_ATOMIC("triggering commit\n"); |
---|
81 | | - kms->funcs->commit(kms, state); |
---|
| 211 | + if (async) { |
---|
| 212 | + struct msm_pending_timer *timer = |
---|
| 213 | + &kms->pending_timers[drm_crtc_index(async_crtc)]; |
---|
| 214 | + |
---|
| 215 | + /* async updates are limited to single-crtc updates: */ |
---|
| 216 | + WARN_ON(crtc_mask != drm_crtc_mask(async_crtc)); |
---|
| 217 | + |
---|
| 218 | + /* |
---|
| 219 | + * Start timer if we don't already have an update pending |
---|
| 220 | + * on this crtc: |
---|
| 221 | + */ |
---|
| 222 | + if (!(kms->pending_crtc_mask & crtc_mask)) { |
---|
| 223 | + ktime_t vsync_time, wakeup_time; |
---|
| 224 | + |
---|
| 225 | + kms->pending_crtc_mask |= crtc_mask; |
---|
| 226 | + |
---|
| 227 | + vsync_time = kms->funcs->vsync_time(kms, async_crtc); |
---|
| 228 | + wakeup_time = ktime_sub(vsync_time, ms_to_ktime(1)); |
---|
| 229 | + |
---|
| 230 | + hrtimer_start(&timer->timer, wakeup_time, |
---|
| 231 | + HRTIMER_MODE_ABS); |
---|
| 232 | + } |
---|
| 233 | + |
---|
| 234 | + kms->funcs->disable_commit(kms); |
---|
| 235 | + mutex_unlock(&kms->commit_lock); |
---|
| 236 | + |
---|
| 237 | + /* |
---|
| 238 | + * At this point, from drm core's perspective, we |
---|
| 239 | + * are done with the atomic update, so we can just |
---|
| 240 | + * go ahead and signal that it is done: |
---|
| 241 | + */ |
---|
| 242 | + drm_atomic_helper_commit_hw_done(state); |
---|
| 243 | + drm_atomic_helper_cleanup_planes(dev, state); |
---|
| 244 | + |
---|
| 245 | + trace_msm_atomic_commit_tail_finish(async, crtc_mask); |
---|
| 246 | + |
---|
| 247 | + return; |
---|
82 | 248 | } |
---|
83 | 249 | |
---|
84 | | - msm_atomic_wait_for_commit_done(dev, state); |
---|
| 250 | + /* |
---|
| 251 | + * If there is any async flush pending on updated crtcs, fold |
---|
| 252 | + * them into the current flush. |
---|
| 253 | + */ |
---|
| 254 | + kms->pending_crtc_mask &= ~crtc_mask; |
---|
85 | 255 | |
---|
86 | | - kms->funcs->complete_commit(kms, state); |
---|
| 256 | + vblank_get(kms, crtc_mask); |
---|
| 257 | + |
---|
| 258 | + /* |
---|
| 259 | + * Flush hardware updates: |
---|
| 260 | + */ |
---|
| 261 | + trace_msm_atomic_flush_commit(crtc_mask); |
---|
| 262 | + kms->funcs->flush_commit(kms, crtc_mask); |
---|
| 263 | + mutex_unlock(&kms->commit_lock); |
---|
| 264 | + |
---|
| 265 | + /* |
---|
| 266 | + * Wait for flush to complete: |
---|
| 267 | + */ |
---|
| 268 | + trace_msm_atomic_wait_flush_start(crtc_mask); |
---|
| 269 | + kms->funcs->wait_flush(kms, crtc_mask); |
---|
| 270 | + trace_msm_atomic_wait_flush_finish(crtc_mask); |
---|
| 271 | + |
---|
| 272 | + vblank_put(kms, crtc_mask); |
---|
| 273 | + |
---|
| 274 | + mutex_lock(&kms->commit_lock); |
---|
| 275 | + kms->funcs->complete_commit(kms, crtc_mask); |
---|
| 276 | + mutex_unlock(&kms->commit_lock); |
---|
| 277 | + kms->funcs->disable_commit(kms); |
---|
87 | 278 | |
---|
88 | 279 | drm_atomic_helper_commit_hw_done(state); |
---|
89 | | - |
---|
90 | 280 | drm_atomic_helper_cleanup_planes(dev, state); |
---|
| 281 | + |
---|
| 282 | + trace_msm_atomic_commit_tail_finish(async, crtc_mask); |
---|
91 | 283 | } |
---|