1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2014 Red Hat 4 * Author: Rob Clark <robdclark@gmail.com> 5 */ 6 7 #include <drm/drm_atomic_uapi.h> 8 #include <drm/drm_gem_atomic_helper.h> 9 #include <drm/drm_vblank.h> 10 11 #include "msm_atomic_trace.h" 12 #include "msm_drv.h" 13 #include "msm_gem.h" 14 #include "msm_kms.h" 15 16 int msm_atomic_prepare_fb(struct drm_plane *plane, 17 struct drm_plane_state *new_state) 18 { 19 struct msm_drm_private *priv = plane->dev->dev_private; 20 struct msm_kms *kms = priv->kms; 21 22 if (!new_state->fb) 23 return 0; 24 25 drm_gem_plane_helper_prepare_fb(plane, new_state); 26 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 lock_crtcs(struct msm_kms *kms, unsigned int crtc_mask) 59 { 60 int crtc_index; 61 struct drm_crtc *crtc; 62 63 for_each_crtc_mask(kms->dev, crtc, crtc_mask) { 64 crtc_index = drm_crtc_index(crtc); 65 mutex_lock_nested(&kms->commit_lock[crtc_index], crtc_index); 66 } 67 } 68 69 static void unlock_crtcs(struct msm_kms *kms, unsigned int crtc_mask) 70 { 71 struct drm_crtc *crtc; 72 73 for_each_crtc_mask_reverse(kms->dev, crtc, crtc_mask) 74 mutex_unlock(&kms->commit_lock[drm_crtc_index(crtc)]); 75 } 76 77 static void msm_atomic_async_commit(struct msm_kms *kms, int crtc_idx) 78 { 79 unsigned crtc_mask = BIT(crtc_idx); 80 81 trace_msm_atomic_async_commit_start(crtc_mask); 82 83 lock_crtcs(kms, crtc_mask); 84 85 if (!(kms->pending_crtc_mask & crtc_mask)) { 86 unlock_crtcs(kms, crtc_mask); 87 goto out; 88 } 89 90 kms->pending_crtc_mask &= ~crtc_mask; 91 92 kms->funcs->enable_commit(kms); 93 94 vblank_get(kms, crtc_mask); 95 96 /* 97 * Flush hardware updates: 98 */ 99 trace_msm_atomic_flush_commit(crtc_mask); 100 kms->funcs->flush_commit(kms, crtc_mask); 101 102 /* 103 * Wait for flush to complete: 104 */ 105 trace_msm_atomic_wait_flush_start(crtc_mask); 106 kms->funcs->wait_flush(kms, crtc_mask); 107 trace_msm_atomic_wait_flush_finish(crtc_mask); 108 109 vblank_put(kms, crtc_mask); 110 111 kms->funcs->complete_commit(kms, crtc_mask); 112 unlock_crtcs(kms, crtc_mask); 113 kms->funcs->disable_commit(kms); 114 115 out: 116 trace_msm_atomic_async_commit_finish(crtc_mask); 117 } 118 119 static void msm_atomic_pending_work(struct kthread_work *work) 120 { 121 struct msm_pending_timer *timer = container_of(work, 122 struct msm_pending_timer, work.work); 123 124 msm_atomic_async_commit(timer->kms, timer->crtc_idx); 125 } 126 127 int msm_atomic_init_pending_timer(struct msm_pending_timer *timer, 128 struct msm_kms *kms, int crtc_idx) 129 { 130 timer->kms = kms; 131 timer->crtc_idx = crtc_idx; 132 133 timer->worker = kthread_create_worker(0, "atomic-worker-%d", crtc_idx); 134 if (IS_ERR(timer->worker)) { 135 int ret = PTR_ERR(timer->worker); 136 timer->worker = NULL; 137 return ret; 138 } 139 sched_set_fifo(timer->worker->task); 140 141 msm_hrtimer_work_init(&timer->work, timer->worker, 142 msm_atomic_pending_work, 143 CLOCK_MONOTONIC, HRTIMER_MODE_ABS); 144 145 return 0; 146 } 147 148 void msm_atomic_destroy_pending_timer(struct msm_pending_timer *timer) 149 { 150 if (timer->worker) 151 kthread_destroy_worker(timer->worker); 152 } 153 154 static bool can_do_async(struct drm_atomic_state *state, 155 struct drm_crtc **async_crtc) 156 { 157 struct drm_connector_state *connector_state; 158 struct drm_connector *connector; 159 struct drm_crtc_state *crtc_state; 160 struct drm_crtc *crtc; 161 int i, num_crtcs = 0; 162 163 if (!(state->legacy_cursor_update || state->async_update)) 164 return false; 165 166 /* any connector change, means slow path: */ 167 for_each_new_connector_in_state(state, connector, connector_state, i) 168 return false; 169 170 for_each_new_crtc_in_state(state, crtc, crtc_state, i) { 171 if (drm_atomic_crtc_needs_modeset(crtc_state)) 172 return false; 173 if (++num_crtcs > 1) 174 return false; 175 *async_crtc = crtc; 176 } 177 178 return true; 179 } 180 181 /* Get bitmask of crtcs that will need to be flushed. The bitmask 182 * can be used with for_each_crtc_mask() iterator, to iterate 183 * effected crtcs without needing to preserve the atomic state. 184 */ 185 static unsigned get_crtc_mask(struct drm_atomic_state *state) 186 { 187 struct drm_crtc_state *crtc_state; 188 struct drm_crtc *crtc; 189 unsigned i, mask = 0; 190 191 for_each_new_crtc_in_state(state, crtc, crtc_state, i) 192 mask |= drm_crtc_mask(crtc); 193 194 return mask; 195 } 196 197 void msm_atomic_commit_tail(struct drm_atomic_state *state) 198 { 199 struct drm_device *dev = state->dev; 200 struct msm_drm_private *priv = dev->dev_private; 201 struct msm_kms *kms = priv->kms; 202 struct drm_crtc *async_crtc = NULL; 203 unsigned crtc_mask = get_crtc_mask(state); 204 bool async = kms->funcs->vsync_time && 205 can_do_async(state, &async_crtc); 206 207 trace_msm_atomic_commit_tail_start(async, crtc_mask); 208 209 kms->funcs->enable_commit(kms); 210 211 /* 212 * Ensure any previous (potentially async) commit has 213 * completed: 214 */ 215 lock_crtcs(kms, crtc_mask); 216 trace_msm_atomic_wait_flush_start(crtc_mask); 217 kms->funcs->wait_flush(kms, crtc_mask); 218 trace_msm_atomic_wait_flush_finish(crtc_mask); 219 220 /* 221 * Now that there is no in-progress flush, prepare the 222 * current update: 223 */ 224 kms->funcs->prepare_commit(kms, state); 225 226 /* 227 * Push atomic updates down to hardware: 228 */ 229 drm_atomic_helper_commit_modeset_disables(dev, state); 230 drm_atomic_helper_commit_planes(dev, state, 0); 231 drm_atomic_helper_commit_modeset_enables(dev, state); 232 233 if (async) { 234 struct msm_pending_timer *timer = 235 &kms->pending_timers[drm_crtc_index(async_crtc)]; 236 237 /* async updates are limited to single-crtc updates: */ 238 WARN_ON(crtc_mask != drm_crtc_mask(async_crtc)); 239 240 /* 241 * Start timer if we don't already have an update pending 242 * on this crtc: 243 */ 244 if (!(kms->pending_crtc_mask & crtc_mask)) { 245 ktime_t vsync_time, wakeup_time; 246 247 kms->pending_crtc_mask |= crtc_mask; 248 249 vsync_time = kms->funcs->vsync_time(kms, async_crtc); 250 wakeup_time = ktime_sub(vsync_time, ms_to_ktime(1)); 251 252 msm_hrtimer_queue_work(&timer->work, wakeup_time, 253 HRTIMER_MODE_ABS); 254 } 255 256 kms->funcs->disable_commit(kms); 257 unlock_crtcs(kms, crtc_mask); 258 /* 259 * At this point, from drm core's perspective, we 260 * are done with the atomic update, so we can just 261 * go ahead and signal that it is done: 262 */ 263 drm_atomic_helper_commit_hw_done(state); 264 drm_atomic_helper_cleanup_planes(dev, state); 265 266 trace_msm_atomic_commit_tail_finish(async, crtc_mask); 267 268 return; 269 } 270 271 /* 272 * If there is any async flush pending on updated crtcs, fold 273 * them into the current flush. 274 */ 275 kms->pending_crtc_mask &= ~crtc_mask; 276 277 vblank_get(kms, crtc_mask); 278 279 /* 280 * Flush hardware updates: 281 */ 282 trace_msm_atomic_flush_commit(crtc_mask); 283 kms->funcs->flush_commit(kms, crtc_mask); 284 unlock_crtcs(kms, crtc_mask); 285 /* 286 * Wait for flush to complete: 287 */ 288 trace_msm_atomic_wait_flush_start(crtc_mask); 289 kms->funcs->wait_flush(kms, crtc_mask); 290 trace_msm_atomic_wait_flush_finish(crtc_mask); 291 292 vblank_put(kms, crtc_mask); 293 294 lock_crtcs(kms, crtc_mask); 295 kms->funcs->complete_commit(kms, crtc_mask); 296 unlock_crtcs(kms, crtc_mask); 297 kms->funcs->disable_commit(kms); 298 299 drm_atomic_helper_commit_hw_done(state); 300 drm_atomic_helper_cleanup_planes(dev, state); 301 302 trace_msm_atomic_commit_tail_finish(async, crtc_mask); 303 } 304