[PATCH 5.15 324/779] drm/msm: Avoid dirtyfb stalls on video mode displays (v2)
From: Greg Kroah-Hartman
Date: Mon Aug 15 2022 - 14:58:31 EST
From: Rob Clark <robdclark@xxxxxxxxxxxx>
[ Upstream commit 9e4dde28e9cd34ee13a6b7247f0857fb49fd3f19 ]
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@xxxxxxxxxxxx>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@xxxxxxxxxx>
Link: https://lore.kernel.org/r/20220223191118.881321-1-robdclark@xxxxxxxxx
Signed-off-by: Rob Clark <robdclark@xxxxxxxxxxxx>
Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>
---
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 768012243b44..7706a7106122 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
@@ -898,6 +898,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)
{
@@ -918,6 +932,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);
@@ -949,6 +964,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)) {
@@ -960,11 +976,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 c989621209aa..e32fe89c203c 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
@@ -894,7 +894,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;
@@ -925,7 +925,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 34e03ac05f4a..17ff48564c8a 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.h
@@ -28,6 +28,7 @@
* @cdp_cfg: CDP configuration
* @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;
@@ -45,6 +46,8 @@ struct dpu_plane_state {
struct dpu_hw_pipe_cdp_cfg cdp_cfg;
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 0e02e252ff89..31447da0af25 100644
--- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c
+++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c
@@ -696,6 +696,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;
@@ -712,12 +714,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 c0d947bce9e9..9c42776cb9a8 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"
@@ -143,18 +144,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,
@@ -452,7 +469,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 fab09e7c6efc..458f4e4316dd 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 bd5132bb9bde..8488e49817e1 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -247,8 +247,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);
@@ -308,9 +306,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.35.1