From de194561359788871f7d8f5f7797557a2a166b4e Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Fri, 18 Sep 2020 16:59:17 +0200 Subject: [PATCH 1/3] drm/vc4: kms: Assign a FIFO to enabled CRTCs instead of active The HVS has three FIFOs that can be assigned to a number of PixelValves through a mux. However, changing that FIFO requires that we disable and then enable the pixelvalve, so we want to assign FIFOs to all the enabled CRTCs, and not just the active ones. Fixes: 87ebcd42fb7b ("drm/vc4: crtc: Assign output to channel automatically") Signed-off-by: Maxime Ripard Tested-by: Dave Stevenson Reviewed-by: Dave Stevenson Link: https://patchwork.freedesktop.org/patch/msgid/20200918145918.101068-1-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_kms.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c index af3ee3dcdab6..01fa60844695 100644 --- a/drivers/gpu/drm/vc4/vc4_kms.c +++ b/drivers/gpu/drm/vc4/vc4_kms.c @@ -643,7 +643,7 @@ vc4_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); unsigned int matching_channels; - if (!crtc_state->active) + if (!crtc_state->enable) continue; /* From 427c4a0680a28f87bb9c7bbfeac26b39ef8682ad Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 23 Sep 2020 10:40:31 +0200 Subject: [PATCH 2/3] drm/vc4: crtc: Rework a bit the CRTC state code The current CRTC state reset hook in vc4 allocates a vc4_crtc_state structure as a drm_crtc_state, and relies on the fact that vc4_crtc_state embeds drm_crtc_state as its first member, and therefore can be safely cast. However, this is pretty fragile especially since there's no check for this in place, and we're going to need to access vc4_crtc_state member at reset so this looks like a good occasion to make it more robust. Fixes: 6d6e50039187 ("drm/vc4: Allocate the right amount of space for boot-time CRTC state.") Signed-off-by: Maxime Ripard Tested-by: Dave Stevenson Reviewed-by: Dave Stevenson Link: https://patchwork.freedesktop.org/patch/msgid/20200923084032.218619-1-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_crtc.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index a393f93390a2..7ef20adedee5 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -852,11 +852,18 @@ void vc4_crtc_destroy_state(struct drm_crtc *crtc, void vc4_crtc_reset(struct drm_crtc *crtc) { + struct vc4_crtc_state *vc4_crtc_state; + if (crtc->state) vc4_crtc_destroy_state(crtc, crtc->state); - crtc->state = kzalloc(sizeof(struct vc4_crtc_state), GFP_KERNEL); - if (crtc->state) - __drm_atomic_helper_crtc_reset(crtc, crtc->state); + + vc4_crtc_state = kzalloc(sizeof(*vc4_crtc_state), GFP_KERNEL); + if (!vc4_crtc_state) { + crtc->state = NULL; + return; + } + + __drm_atomic_helper_crtc_reset(crtc, &vc4_crtc_state->base); } static const struct drm_crtc_funcs vc4_crtc_funcs = { From 8ba0b6d196315f68c271f549e8585129caefce97 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 23 Sep 2020 10:40:32 +0200 Subject: [PATCH 3/3] drm/vc4: crtc: Keep the previously assigned HVS FIFO The HVS FIFOs are currently assigned each time we have an atomic_check for all the enabled CRTCs. However, if we are running multiple outputs in parallel and we happen to disable the first (by index) CRTC, we end up changing the assigned FIFO of the second CRTC without disabling and reenabling the pixelvalve which ends up in a stall and eventually a VBLANK timeout. In order to fix this, we can create a special value for our assigned channel to mark it as disabled, and if our CRTC already had an assigned channel in its previous state, we keep on using it. Fixes: 87ebcd42fb7b ("drm/vc4: crtc: Assign output to channel automatically") Signed-off-by: Maxime Ripard Tested-by: Dave Stevenson Reviewed-by: Dave Stevenson Link: https://patchwork.freedesktop.org/patch/msgid/20200923084032.218619-2-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_crtc.c | 1 + drivers/gpu/drm/vc4/vc4_drv.h | 2 ++ drivers/gpu/drm/vc4/vc4_kms.c | 22 ++++++++++++++++------ 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 7ef20adedee5..482219fb4db2 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -863,6 +863,7 @@ void vc4_crtc_reset(struct drm_crtc *crtc) return; } + vc4_crtc_state->assigned_channel = VC4_HVS_CHANNEL_DISABLED; __drm_atomic_helper_crtc_reset(crtc, &vc4_crtc_state->base); } diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 8c8d96b6289f..90b911fd2a7f 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -532,6 +532,8 @@ struct vc4_crtc_state { } margins; }; +#define VC4_HVS_CHANNEL_DISABLED ((unsigned int)-1) + static inline struct vc4_crtc_state * to_vc4_crtc_state(struct drm_crtc_state *crtc_state) { diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c index 01fa60844695..149825ff5df8 100644 --- a/drivers/gpu/drm/vc4/vc4_kms.c +++ b/drivers/gpu/drm/vc4/vc4_kms.c @@ -616,7 +616,7 @@ static int vc4_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) { unsigned long unassigned_channels = GENMASK(NUM_CHANNELS - 1, 0); - struct drm_crtc_state *crtc_state; + struct drm_crtc_state *old_crtc_state, *new_crtc_state; struct drm_crtc *crtc; int i, ret; @@ -629,6 +629,8 @@ vc4_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) * modified. */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct drm_crtc_state *crtc_state; + if (!crtc->state->enable) continue; @@ -637,15 +639,23 @@ vc4_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) return PTR_ERR(crtc_state); } - for_each_new_crtc_in_state(state, crtc, crtc_state, i) { - struct vc4_crtc_state *vc4_crtc_state = - to_vc4_crtc_state(crtc_state); + for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { + struct vc4_crtc_state *new_vc4_crtc_state = + to_vc4_crtc_state(new_crtc_state); struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); unsigned int matching_channels; - if (!crtc_state->enable) + if (old_crtc_state->enable && !new_crtc_state->enable) + new_vc4_crtc_state->assigned_channel = VC4_HVS_CHANNEL_DISABLED; + + if (!new_crtc_state->enable) continue; + if (new_vc4_crtc_state->assigned_channel != VC4_HVS_CHANNEL_DISABLED) { + unassigned_channels &= ~BIT(new_vc4_crtc_state->assigned_channel); + continue; + } + /* * The problem we have to solve here is that we have * up to 7 encoders, connected to up to 6 CRTCs. @@ -674,7 +684,7 @@ vc4_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) if (matching_channels) { unsigned int channel = ffs(matching_channels) - 1; - vc4_crtc_state->assigned_channel = channel; + new_vc4_crtc_state->assigned_channel = channel; unassigned_channels &= ~BIT(channel); } else { return -EINVAL;