| .. | .. |
|---|
| 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 | } |
|---|