linux/drivers/gpu/drm/msm/msm_atomic.c
<<
>>
Prefs
   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_framebuffer_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
  16int 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_fb_prepare_fb(plane, new_state);
  26
  27        return msm_framebuffer_prepare(new_state->fb, kms->aspace);
  28}
  29
  30static void msm_atomic_async_commit(struct msm_kms *kms, int crtc_idx)
  31{
  32        unsigned crtc_mask = BIT(crtc_idx);
  33
  34        trace_msm_atomic_async_commit_start(crtc_mask);
  35
  36        mutex_lock(&kms->commit_lock);
  37
  38        if (!(kms->pending_crtc_mask & crtc_mask)) {
  39                mutex_unlock(&kms->commit_lock);
  40                goto out;
  41        }
  42
  43        kms->pending_crtc_mask &= ~crtc_mask;
  44
  45        kms->funcs->enable_commit(kms);
  46
  47        /*
  48         * Flush hardware updates:
  49         */
  50        trace_msm_atomic_flush_commit(crtc_mask);
  51        kms->funcs->flush_commit(kms, crtc_mask);
  52        mutex_unlock(&kms->commit_lock);
  53
  54        /*
  55         * Wait for flush to complete:
  56         */
  57        trace_msm_atomic_wait_flush_start(crtc_mask);
  58        kms->funcs->wait_flush(kms, crtc_mask);
  59        trace_msm_atomic_wait_flush_finish(crtc_mask);
  60
  61        mutex_lock(&kms->commit_lock);
  62        kms->funcs->complete_commit(kms, crtc_mask);
  63        mutex_unlock(&kms->commit_lock);
  64        kms->funcs->disable_commit(kms);
  65
  66out:
  67        trace_msm_atomic_async_commit_finish(crtc_mask);
  68}
  69
  70static enum hrtimer_restart msm_atomic_pending_timer(struct hrtimer *t)
  71{
  72        struct msm_pending_timer *timer = container_of(t,
  73                        struct msm_pending_timer, timer);
  74        struct msm_drm_private *priv = timer->kms->dev->dev_private;
  75
  76        queue_work(priv->wq, &timer->work);
  77
  78        return HRTIMER_NORESTART;
  79}
  80
  81static void msm_atomic_pending_work(struct work_struct *work)
  82{
  83        struct msm_pending_timer *timer = container_of(work,
  84                        struct msm_pending_timer, work);
  85
  86        msm_atomic_async_commit(timer->kms, timer->crtc_idx);
  87}
  88
  89void msm_atomic_init_pending_timer(struct msm_pending_timer *timer,
  90                struct msm_kms *kms, int crtc_idx)
  91{
  92        timer->kms = kms;
  93        timer->crtc_idx = crtc_idx;
  94        hrtimer_init(&timer->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
  95        timer->timer.function = msm_atomic_pending_timer;
  96        INIT_WORK(&timer->work, msm_atomic_pending_work);
  97}
  98
  99static bool can_do_async(struct drm_atomic_state *state,
 100                struct drm_crtc **async_crtc)
 101{
 102        struct drm_connector_state *connector_state;
 103        struct drm_connector *connector;
 104        struct drm_crtc_state *crtc_state;
 105        struct drm_crtc *crtc;
 106        int i, num_crtcs = 0;
 107
 108        if (!(state->legacy_cursor_update || state->async_update))
 109                return false;
 110
 111        /* any connector change, means slow path: */
 112        for_each_new_connector_in_state(state, connector, connector_state, i)
 113                return false;
 114
 115        for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
 116                if (drm_atomic_crtc_needs_modeset(crtc_state))
 117                        return false;
 118                if (++num_crtcs > 1)
 119                        return false;
 120                *async_crtc = crtc;
 121        }
 122
 123        return true;
 124}
 125
 126/* Get bitmask of crtcs that will need to be flushed.  The bitmask
 127 * can be used with for_each_crtc_mask() iterator, to iterate
 128 * effected crtcs without needing to preserve the atomic state.
 129 */
 130static unsigned get_crtc_mask(struct drm_atomic_state *state)
 131{
 132        struct drm_crtc_state *crtc_state;
 133        struct drm_crtc *crtc;
 134        unsigned i, mask = 0;
 135
 136        for_each_new_crtc_in_state(state, crtc, crtc_state, i)
 137                mask |= drm_crtc_mask(crtc);
 138
 139        return mask;
 140}
 141
 142void msm_atomic_commit_tail(struct drm_atomic_state *state)
 143{
 144        struct drm_device *dev = state->dev;
 145        struct msm_drm_private *priv = dev->dev_private;
 146        struct msm_kms *kms = priv->kms;
 147        struct drm_crtc *async_crtc = NULL;
 148        unsigned crtc_mask = get_crtc_mask(state);
 149        bool async = kms->funcs->vsync_time &&
 150                        can_do_async(state, &async_crtc);
 151
 152        trace_msm_atomic_commit_tail_start(async, crtc_mask);
 153
 154        kms->funcs->enable_commit(kms);
 155
 156        /*
 157         * Ensure any previous (potentially async) commit has
 158         * completed:
 159         */
 160        trace_msm_atomic_wait_flush_start(crtc_mask);
 161        kms->funcs->wait_flush(kms, crtc_mask);
 162        trace_msm_atomic_wait_flush_finish(crtc_mask);
 163
 164        mutex_lock(&kms->commit_lock);
 165
 166        /*
 167         * Now that there is no in-progress flush, prepare the
 168         * current update:
 169         */
 170        kms->funcs->prepare_commit(kms, state);
 171
 172        /*
 173         * Push atomic updates down to hardware:
 174         */
 175        drm_atomic_helper_commit_modeset_disables(dev, state);
 176        drm_atomic_helper_commit_planes(dev, state, 0);
 177        drm_atomic_helper_commit_modeset_enables(dev, state);
 178
 179        if (async) {
 180                struct msm_pending_timer *timer =
 181                        &kms->pending_timers[drm_crtc_index(async_crtc)];
 182
 183                /* async updates are limited to single-crtc updates: */
 184                WARN_ON(crtc_mask != drm_crtc_mask(async_crtc));
 185
 186                /*
 187                 * Start timer if we don't already have an update pending
 188                 * on this crtc:
 189                 */
 190                if (!(kms->pending_crtc_mask & crtc_mask)) {
 191                        ktime_t vsync_time, wakeup_time;
 192
 193                        kms->pending_crtc_mask |= crtc_mask;
 194
 195                        vsync_time = kms->funcs->vsync_time(kms, async_crtc);
 196                        wakeup_time = ktime_sub(vsync_time, ms_to_ktime(1));
 197
 198                        hrtimer_start(&timer->timer, wakeup_time,
 199                                        HRTIMER_MODE_ABS);
 200                }
 201
 202                kms->funcs->disable_commit(kms);
 203                mutex_unlock(&kms->commit_lock);
 204
 205                /*
 206                 * At this point, from drm core's perspective, we
 207                 * are done with the atomic update, so we can just
 208                 * go ahead and signal that it is done:
 209                 */
 210                drm_atomic_helper_commit_hw_done(state);
 211                drm_atomic_helper_cleanup_planes(dev, state);
 212
 213                trace_msm_atomic_commit_tail_finish(async, crtc_mask);
 214
 215                return;
 216        }
 217
 218        /*
 219         * If there is any async flush pending on updated crtcs, fold
 220         * them into the current flush.
 221         */
 222        kms->pending_crtc_mask &= ~crtc_mask;
 223
 224        /*
 225         * Flush hardware updates:
 226         */
 227        trace_msm_atomic_flush_commit(crtc_mask);
 228        kms->funcs->flush_commit(kms, crtc_mask);
 229        mutex_unlock(&kms->commit_lock);
 230
 231        /*
 232         * Wait for flush to complete:
 233         */
 234        trace_msm_atomic_wait_flush_start(crtc_mask);
 235        kms->funcs->wait_flush(kms, crtc_mask);
 236        trace_msm_atomic_wait_flush_finish(crtc_mask);
 237
 238        mutex_lock(&kms->commit_lock);
 239        kms->funcs->complete_commit(kms, crtc_mask);
 240        mutex_unlock(&kms->commit_lock);
 241        kms->funcs->disable_commit(kms);
 242
 243        drm_atomic_helper_commit_hw_done(state);
 244        drm_atomic_helper_cleanup_planes(dev, state);
 245
 246        trace_msm_atomic_commit_tail_finish(async, crtc_mask);
 247}
 248