From 881ea5294ca23679d09a06b80397b4b4383d6ecf Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Thu, 27 Jan 2022 19:20:57 +0000 Subject: [PATCH] drm/tegra: Ensure BOs are not freed before display is updated Occasionally, there are cases where the BO is being freed before the display has been updated to use the next BO. If the SMMU is enabled this causes SMMU faults to occur. This problem occurs when the vertical blanking interrupt occurs at the same time the BO used by the display is updated. In this case, the interrupt handler, tegra_dc_irq(), is called to handle the vertial blanking interrupt, which in turn calls drm_crtc_handle_vblank() to discard the previous BO. However, the programming of the display controller did not actually complete before the vertical blanking and the display is still using the previous BO. Hence, the display continues to use the prevoius BO which is then freed. Fix this by disabling the vertical blanking interrupt during the time where the display is updated and then in the Tegra interrupt handler ensure that we only handle interrupts that are currently enabled. Finally, before calling drm_crtc_handle_vblank() in the interrupt handler, check that the programming of the display controller has completed and it is safe to release the BO. JIRA LS-128 Change-Id: I1c9523f2b0a3ea406d651c2d1988e452d412e204 Signed-off-by: Jon Hunter Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2660852 Reviewed-by: Thierry Reding Reviewed-by: Mikko Perttunen Reviewed-by: svc_kernel_abi GVS: Gerrit_Virtual_Submit --- drivers/gpu/drm/tegra/dc.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index fe883209..20af0cf3 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -2289,6 +2289,8 @@ static void tegra_crtc_atomic_begin(struct drm_crtc *crtc, crtc->state->event = NULL; } + + tegra_dc_disable_vblank(crtc); } static void tegra_crtc_atomic_flush(struct drm_crtc *crtc, @@ -2315,6 +2317,8 @@ static void tegra_crtc_atomic_flush(struct drm_crtc *crtc, value = dc_state->planes | GENERAL_ACT_REQ; tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); + + tegra_dc_enable_vblank(crtc); } static bool tegra_plane_is_cursor(const struct drm_plane_state *state) @@ -2527,9 +2531,10 @@ static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = { static irqreturn_t tegra_dc_irq(int irq, void *data) { struct tegra_dc *dc = data; - unsigned long status; + u32 status, value, mask; - status = tegra_dc_readl(dc, DC_CMD_INT_STATUS); + mask = tegra_dc_readl(dc, DC_CMD_INT_MASK); + status = tegra_dc_readl(dc, DC_CMD_INT_STATUS) & mask; tegra_dc_writel(dc, status, DC_CMD_INT_STATUS); if (status & FRAME_END_INT) { @@ -2544,7 +2549,9 @@ static irqreturn_t tegra_dc_irq(int irq, void *data) /* dev_dbg(dc->dev, "%s(): vertical blank\n", __func__); */ - drm_crtc_handle_vblank(&dc->base); + value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); + if (!value) + drm_crtc_handle_vblank(&dc->base); dc->stats.vblank_total++; dc->stats.vblank++; }