[PATCH] drm/msm: Avoid dirtyfb stalls on video mode displays (v2)
Dmitry Baryshkov
dmitry.baryshkov at linaro.org
Fri Feb 25 01:03:27 UTC 2022
On Wed, 23 Feb 2022 at 22:11, Rob Clark <robdclark at gmail.com> wrote:
>
> From: Rob Clark <robdclark at chromium.org>
>
> Someone on IRC once asked an innocent enough sounding question: Why
> with xf86-video-modesetting is es2gears limited at 120fps.
>
> So I broke out the perfetto tracing mesa MR and took a look. It turns
> out the problem was drm_atomic_helper_dirtyfb(), which would end up
> waiting for vblank.. es2gears would rapidly push two frames to Xorg,
> which would blit them to screen and in idle hook (I assume) call the
> DIRTYFB ioctl. Which in turn would do an atomic update to flush the
> dirty rects, which would stall until the next vblank. And then the
> whole process would repeat.
>
> But this is a bit silly, we only need dirtyfb for command mode DSI
> panels. So track in plane state whether dirtyfb is required, and
> track in the fb how many attached planes require dirtyfb so that we
> can skip it when not required. (Note, mdp4 does not actually have
> cmd mode support.)
>
> Signed-off-by: Rob Clark <robdclark at chromium.org>
I like the way it ended up being implemented. A really nice hack!
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov at linaro.org>
> ---
> drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | 20 ++++++++++-
> drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c | 5 +--
> drivers/gpu/drm/msm/disp/dpu1/dpu_plane.h | 3 ++
> drivers/gpu/drm/msm/disp/mdp4/mdp4_plane.c | 19 ++++++++--
> drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c | 8 +++++
> drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.h | 5 +++
> drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c | 21 +++++++++--
> drivers/gpu/drm/msm/msm_atomic.c | 15 --------
> drivers/gpu/drm/msm/msm_drv.h | 6 ++--
> drivers/gpu/drm/msm/msm_fb.c | 41 ++++++++++++++++++----
> 10 files changed, 110 insertions(+), 33 deletions(-)
>
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> index 662b7bc9c219..7763558ef566 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> @@ -1046,6 +1046,20 @@ struct plane_state {
> u32 pipe_id;
> };
>
> +static bool dpu_crtc_needs_dirtyfb(struct drm_crtc_state *cstate)
> +{
> + struct drm_crtc *crtc = cstate->crtc;
> + struct drm_encoder *encoder;
> +
> + drm_for_each_encoder_mask (encoder, crtc->dev, cstate->encoder_mask) {
> + if (dpu_encoder_get_intf_mode(encoder) == INTF_MODE_CMD) {
> + return true;
> + }
> + }
> +
> + return false;
> +}
> +
> static int dpu_crtc_atomic_check(struct drm_crtc *crtc,
> struct drm_atomic_state *state)
> {
> @@ -1066,6 +1080,7 @@ static int dpu_crtc_atomic_check(struct drm_crtc *crtc,
> const struct drm_plane_state *pipe_staged[SSPP_MAX];
> int left_zpos_cnt = 0, right_zpos_cnt = 0;
> struct drm_rect crtc_rect = { 0 };
> + bool needs_dirtyfb = dpu_crtc_needs_dirtyfb(crtc_state);
>
> pstates = kzalloc(sizeof(*pstates) * DPU_STAGE_MAX * 4, GFP_KERNEL);
>
> @@ -1097,6 +1112,7 @@ static int dpu_crtc_atomic_check(struct drm_crtc *crtc,
>
> /* get plane state for all drm planes associated with crtc state */
> drm_atomic_crtc_state_for_each_plane_state(plane, pstate, crtc_state) {
> + struct dpu_plane_state *dpu_pstate = to_dpu_plane_state(pstate);
> struct drm_rect dst, clip = crtc_rect;
>
> if (IS_ERR_OR_NULL(pstate)) {
> @@ -1108,11 +1124,13 @@ static int dpu_crtc_atomic_check(struct drm_crtc *crtc,
> if (cnt >= DPU_STAGE_MAX * 4)
> continue;
>
> - pstates[cnt].dpu_pstate = to_dpu_plane_state(pstate);
> + pstates[cnt].dpu_pstate = dpu_pstate;
> pstates[cnt].drm_pstate = pstate;
> pstates[cnt].stage = pstate->normalized_zpos;
> pstates[cnt].pipe_id = dpu_plane_pipe(plane);
>
> + dpu_pstate->needs_dirtyfb = needs_dirtyfb;
> +
> if (pipe_staged[pstates[cnt].pipe_id]) {
> multirect_plane[multirect_count].r0 =
> pipe_staged[pstates[cnt].pipe_id];
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
> index ca75089c9d61..6565682fe227 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
> @@ -902,7 +902,7 @@ static int dpu_plane_prepare_fb(struct drm_plane *plane,
>
> if (pstate->aspace) {
> ret = msm_framebuffer_prepare(new_state->fb,
> - pstate->aspace);
> + pstate->aspace, pstate->needs_dirtyfb);
> if (ret) {
> DPU_ERROR("failed to prepare framebuffer\n");
> return ret;
> @@ -933,7 +933,8 @@ static void dpu_plane_cleanup_fb(struct drm_plane *plane,
>
> DPU_DEBUG_PLANE(pdpu, "FB[%u]\n", old_state->fb->base.id);
>
> - msm_framebuffer_cleanup(old_state->fb, old_pstate->aspace);
> + msm_framebuffer_cleanup(old_state->fb, old_pstate->aspace,
> + old_pstate->needs_dirtyfb);
> }
>
> static bool dpu_plane_validate_src(struct drm_rect *src,
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.h
> index 9d51dad5c6a5..50781e2d3577 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.h
> @@ -25,6 +25,7 @@
> * @pending: whether the current update is still pending
> * @plane_fetch_bw: calculated BW per plane
> * @plane_clk: calculated clk per plane
> + * @needs_dirtyfb: whether attached CRTC needs pixel data explicitly flushed
> */
> struct dpu_plane_state {
> struct drm_plane_state base;
> @@ -37,6 +38,8 @@ struct dpu_plane_state {
>
> u64 plane_fetch_bw;
> u64 plane_clk;
> +
> + bool needs_dirtyfb;
> };
>
> /**
> diff --git a/drivers/gpu/drm/msm/disp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/disp/mdp4/mdp4_plane.c
> index 49bdabea8ed5..3e20f72d75ef 100644
> --- a/drivers/gpu/drm/msm/disp/mdp4/mdp4_plane.c
> +++ b/drivers/gpu/drm/msm/disp/mdp4/mdp4_plane.c
> @@ -7,6 +7,7 @@
> #include <drm/drm_atomic.h>
> #include <drm/drm_damage_helper.h>
> #include <drm/drm_fourcc.h>
> +#include <drm/drm_gem_atomic_helper.h>
>
> #include "mdp4_kms.h"
>
> @@ -90,6 +91,20 @@ static const struct drm_plane_funcs mdp4_plane_funcs = {
> .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
> };
>
> +static int mdp4_plane_prepare_fb(struct drm_plane *plane,
> + struct drm_plane_state *new_state)
> +{
> + struct msm_drm_private *priv = plane->dev->dev_private;
> + struct msm_kms *kms = priv->kms;
> +
> + if (!new_state->fb)
> + return 0;
> +
> + drm_gem_plane_helper_prepare_fb(plane, new_state);
> +
> + return msm_framebuffer_prepare(new_state->fb, kms->aspace, false);
> +}
> +
> static void mdp4_plane_cleanup_fb(struct drm_plane *plane,
> struct drm_plane_state *old_state)
> {
> @@ -102,7 +117,7 @@ static void mdp4_plane_cleanup_fb(struct drm_plane *plane,
> return;
>
> DBG("%s: cleanup: FB[%u]", mdp4_plane->name, fb->base.id);
> - msm_framebuffer_cleanup(fb, kms->aspace);
> + msm_framebuffer_cleanup(fb, kms->aspace, false);
> }
>
>
> @@ -130,7 +145,7 @@ static void mdp4_plane_atomic_update(struct drm_plane *plane,
> }
>
> static const struct drm_plane_helper_funcs mdp4_plane_helper_funcs = {
> - .prepare_fb = msm_atomic_prepare_fb,
> + .prepare_fb = mdp4_plane_prepare_fb,
> .cleanup_fb = mdp4_plane_cleanup_fb,
> .atomic_check = mdp4_plane_atomic_check,
> .atomic_update = mdp4_plane_atomic_update,
> diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c
> index bb7d066618e6..b966cd69f99d 100644
> --- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c
> +++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c
> @@ -690,6 +690,8 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc,
> {
> struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
> crtc);
> + struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc_state);
> + struct mdp5_interface *intf = mdp5_cstate->pipeline.intf;
> struct mdp5_kms *mdp5_kms = get_kms(crtc);
> struct drm_plane *plane;
> struct drm_device *dev = crtc->dev;
> @@ -706,12 +708,18 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc,
> DBG("%s: check", crtc->name);
>
> drm_atomic_crtc_state_for_each_plane_state(plane, pstate, crtc_state) {
> + struct mdp5_plane_state *mdp5_pstate =
> + to_mdp5_plane_state(pstate);
> +
> if (!pstate->visible)
> continue;
>
> pstates[cnt].plane = plane;
> pstates[cnt].state = to_mdp5_plane_state(pstate);
>
> + mdp5_pstate->needs_dirtyfb =
> + intf->mode == MDP5_INTF_DSI_MODE_COMMAND;
> +
> /*
> * if any plane on this crtc uses 2 hwpipes, then we need
> * the crtc to have a right hwmixer.
> diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.h b/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.h
> index ac269a6802df..29bf11f08601 100644
> --- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.h
> +++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.h
> @@ -100,6 +100,11 @@ struct mdp5_plane_state {
>
> /* assigned by crtc blender */
> enum mdp_mixer_stage_id stage;
> +
> + /* whether attached CRTC needs pixel data explicitly flushed to
> + * display (ex. DSI command mode display)
> + */
> + bool needs_dirtyfb;
> };
> #define to_mdp5_plane_state(x) \
> container_of(x, struct mdp5_plane_state, base)
> diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c
> index c6b69afcbac8..b176338ab59b 100644
> --- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c
> +++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c
> @@ -8,6 +8,7 @@
> #include <drm/drm_atomic.h>
> #include <drm/drm_damage_helper.h>
> #include <drm/drm_fourcc.h>
> +#include <drm/drm_gem_atomic_helper.h>
> #include <drm/drm_print.h>
>
> #include "mdp5_kms.h"
> @@ -140,18 +141,34 @@ static const struct drm_plane_funcs mdp5_plane_funcs = {
> .atomic_print_state = mdp5_plane_atomic_print_state,
> };
>
> +static int mdp5_plane_prepare_fb(struct drm_plane *plane,
> + struct drm_plane_state *new_state)
> +{
> + struct msm_drm_private *priv = plane->dev->dev_private;
> + struct msm_kms *kms = priv->kms;
> + bool needs_dirtyfb = to_mdp5_plane_state(new_state)->needs_dirtyfb;
> +
> + if (!new_state->fb)
> + return 0;
> +
> + drm_gem_plane_helper_prepare_fb(plane, new_state);
> +
> + return msm_framebuffer_prepare(new_state->fb, kms->aspace, needs_dirtyfb);
> +}
> +
> static void mdp5_plane_cleanup_fb(struct drm_plane *plane,
> struct drm_plane_state *old_state)
> {
> struct mdp5_kms *mdp5_kms = get_kms(plane);
> struct msm_kms *kms = &mdp5_kms->base.base;
> struct drm_framebuffer *fb = old_state->fb;
> + bool needed_dirtyfb = to_mdp5_plane_state(old_state)->needs_dirtyfb;
>
> if (!fb)
> return;
>
> DBG("%s: cleanup: FB[%u]", plane->name, fb->base.id);
> - msm_framebuffer_cleanup(fb, kms->aspace);
> + msm_framebuffer_cleanup(fb, kms->aspace, needed_dirtyfb);
> }
>
> static int mdp5_plane_atomic_check_with_state(struct drm_crtc_state *crtc_state,
> @@ -437,7 +454,7 @@ static void mdp5_plane_atomic_async_update(struct drm_plane *plane,
> }
>
> static const struct drm_plane_helper_funcs mdp5_plane_helper_funcs = {
> - .prepare_fb = msm_atomic_prepare_fb,
> + .prepare_fb = mdp5_plane_prepare_fb,
> .cleanup_fb = mdp5_plane_cleanup_fb,
> .atomic_check = mdp5_plane_atomic_check,
> .atomic_update = mdp5_plane_atomic_update,
> diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c
> index 27c9ae563f2f..1686fbb611fd 100644
> --- a/drivers/gpu/drm/msm/msm_atomic.c
> +++ b/drivers/gpu/drm/msm/msm_atomic.c
> @@ -5,7 +5,6 @@
> */
>
> #include <drm/drm_atomic_uapi.h>
> -#include <drm/drm_gem_atomic_helper.h>
> #include <drm/drm_vblank.h>
>
> #include "msm_atomic_trace.h"
> @@ -13,20 +12,6 @@
> #include "msm_gem.h"
> #include "msm_kms.h"
>
> -int msm_atomic_prepare_fb(struct drm_plane *plane,
> - struct drm_plane_state *new_state)
> -{
> - struct msm_drm_private *priv = plane->dev->dev_private;
> - struct msm_kms *kms = priv->kms;
> -
> - if (!new_state->fb)
> - return 0;
> -
> - drm_gem_plane_helper_prepare_fb(plane, new_state);
> -
> - return msm_framebuffer_prepare(new_state->fb, kms->aspace);
> -}
> -
> /*
> * Helpers to control vblanks while we flush.. basically just to ensure
> * that vblank accounting is switched on, so we get valid seqn/timestamp
> diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
> index 57b0cd6f917e..9f68aa685ed7 100644
> --- a/drivers/gpu/drm/msm/msm_drv.h
> +++ b/drivers/gpu/drm/msm/msm_drv.h
> @@ -239,8 +239,6 @@ struct msm_format {
>
> struct msm_pending_timer;
>
> -int msm_atomic_prepare_fb(struct drm_plane *plane,
> - struct drm_plane_state *new_state);
> int msm_atomic_init_pending_timer(struct msm_pending_timer *timer,
> struct msm_kms *kms, int crtc_idx);
> void msm_atomic_destroy_pending_timer(struct msm_pending_timer *timer);
> @@ -299,9 +297,9 @@ int msm_gem_prime_pin(struct drm_gem_object *obj);
> void msm_gem_prime_unpin(struct drm_gem_object *obj);
>
> int msm_framebuffer_prepare(struct drm_framebuffer *fb,
> - struct msm_gem_address_space *aspace);
> + struct msm_gem_address_space *aspace, bool needs_dirtyfb);
> void msm_framebuffer_cleanup(struct drm_framebuffer *fb,
> - struct msm_gem_address_space *aspace);
> + struct msm_gem_address_space *aspace, bool needed_dirtyfb);
> uint32_t msm_framebuffer_iova(struct drm_framebuffer *fb,
> struct msm_gem_address_space *aspace, int plane);
> struct drm_gem_object *msm_framebuffer_bo(struct drm_framebuffer *fb, int plane);
> diff --git a/drivers/gpu/drm/msm/msm_fb.c b/drivers/gpu/drm/msm/msm_fb.c
> index 4d34df5354e0..96b379a08327 100644
> --- a/drivers/gpu/drm/msm/msm_fb.c
> +++ b/drivers/gpu/drm/msm/msm_fb.c
> @@ -18,16 +18,36 @@
> struct msm_framebuffer {
> struct drm_framebuffer base;
> const struct msm_format *format;
> +
> + /* Count of # of attached planes which need dirtyfb: */
> + refcount_t dirtyfb;
> };
> #define to_msm_framebuffer(x) container_of(x, struct msm_framebuffer, base)
>
> static struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev,
> const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos);
>
> +static int msm_framebuffer_dirtyfb(struct drm_framebuffer *fb,
> + struct drm_file *file_priv, unsigned int flags,
> + unsigned int color, struct drm_clip_rect *clips,
> + unsigned int num_clips)
> +{
> + struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
> +
> + /* If this fb is not used on any display requiring pixel data to be
> + * flushed, then skip dirtyfb
> + */
> + if (refcount_read(&msm_fb->dirtyfb) == 0)
> + return 0;
> +
> + return drm_atomic_helper_dirtyfb(fb, file_priv, flags, color,
> + clips, num_clips);
> +}
> +
> static const struct drm_framebuffer_funcs msm_framebuffer_funcs = {
> .create_handle = drm_gem_fb_create_handle,
> .destroy = drm_gem_fb_destroy,
> - .dirty = drm_atomic_helper_dirtyfb,
> + .dirty = msm_framebuffer_dirtyfb,
> };
>
> #ifdef CONFIG_DEBUG_FS
> @@ -48,17 +68,19 @@ void msm_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m)
> }
> #endif
>
> -/* prepare/pin all the fb's bo's for scanout. Note that it is not valid
> - * to prepare an fb more multiple different initiator 'id's. But that
> - * should be fine, since only the scanout (mdpN) side of things needs
> - * this, the gpu doesn't care about fb's.
> +/* prepare/pin all the fb's bo's for scanout.
> */
> int msm_framebuffer_prepare(struct drm_framebuffer *fb,
> - struct msm_gem_address_space *aspace)
> + struct msm_gem_address_space *aspace,
> + bool needs_dirtyfb)
> {
> + struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
> int ret, i, n = fb->format->num_planes;
> uint64_t iova;
>
> + if (needs_dirtyfb)
> + refcount_inc(&msm_fb->dirtyfb);
> +
> for (i = 0; i < n; i++) {
> ret = msm_gem_get_and_pin_iova(fb->obj[i], aspace, &iova);
> drm_dbg_state(fb->dev, "FB[%u]: iova[%d]: %08llx (%d)", fb->base.id, i, iova, ret);
> @@ -70,10 +92,15 @@ int msm_framebuffer_prepare(struct drm_framebuffer *fb,
> }
>
> void msm_framebuffer_cleanup(struct drm_framebuffer *fb,
> - struct msm_gem_address_space *aspace)
> + struct msm_gem_address_space *aspace,
> + bool needed_dirtyfb)
> {
> + struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
> int i, n = fb->format->num_planes;
>
> + if (needed_dirtyfb)
> + refcount_dec(&msm_fb->dirtyfb);
> +
> for (i = 0; i < n; i++)
> msm_gem_unpin_iova(fb->obj[i], aspace);
> }
> --
> 2.34.1
>
--
With best wishes
Dmitry
More information about the dri-devel
mailing list