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 <jonathanh@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2660852
Reviewed-by: Thierry Reding <treding@nvidia.com>
Reviewed-by: Mikko Perttunen <mperttunen@nvidia.com>
Reviewed-by: svc_kernel_abi <svc_kernel_abi@nvidia.com>
GVS: Gerrit_Virtual_Submit
This commit is contained in:
Jon Hunter
2022-01-27 19:20:57 +00:00
committed by Laxman Dewangan
parent 02b028d02a
commit 881ea5294c

View File

@@ -2289,6 +2289,8 @@ static void tegra_crtc_atomic_begin(struct drm_crtc *crtc,
crtc->state->event = NULL; crtc->state->event = NULL;
} }
tegra_dc_disable_vblank(crtc);
} }
static void tegra_crtc_atomic_flush(struct drm_crtc *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; value = dc_state->planes | GENERAL_ACT_REQ;
tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
value = tegra_dc_readl(dc, 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) 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) static irqreturn_t tegra_dc_irq(int irq, void *data)
{ {
struct tegra_dc *dc = 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); tegra_dc_writel(dc, status, DC_CMD_INT_STATUS);
if (status & FRAME_END_INT) { if (status & FRAME_END_INT) {
@@ -2544,6 +2549,8 @@ static irqreturn_t tegra_dc_irq(int irq, void *data)
/* /*
dev_dbg(dc->dev, "%s(): vertical blank\n", __func__); dev_dbg(dc->dev, "%s(): vertical blank\n", __func__);
*/ */
value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
if (!value)
drm_crtc_handle_vblank(&dc->base); drm_crtc_handle_vblank(&dc->base);
dc->stats.vblank_total++; dc->stats.vblank_total++;
dc->stats.vblank++; dc->stats.vblank++;