[PATCH] drm/exynos/fimd: only finish pageflip if START == START_S

Inki Dae inki.dae at samsung.com
Tue Dec 9 04:50:54 PST 2014


On 2014년 11월 26일 03:12, Gustavo Padovan wrote:
> From: Daniel Kurtz <djkurtz at chromium.org>
> 
> A framebuffer gets committed to FIMD's default window like this:
>  exynos_drm_crtc_update()
>   exynos_plane_commit()
>    fimd_win_commit()
> 
> fimd_win_commit() programs BUF_START[0].  At each vblank, FIMD hardware
> copies the value from BUF_START to BUF_START_S (BUF_START's shadow
> register), starts scanning out from BUF_START_S, and asserts its irq.
> 
> This irq is handled by fimd_irq_handler(), which calls
> exynos_drm_crtc_finish_pageflip() to free the old buffer that FIMD just
> finished scanning out, and potentially commit the next pending flip.
> 
> There is a race, however, if fimd_win_commit() programs BUF_START(0)
> between the actual vblank irq, and its corresponding fimd_irq_handler().
> 
>  => FIMD vblank: BUF_START_S[0] := BUF_START[0], and irq asserted
>  | => fimd_win_commit(0) writes new BUF_START[0]
>  |    exynos_drm_crtc_try_do_flip() marks exynos_fb as prepared
>  => fimd_irq_handler()
>     exynos_drm_crtc_finish_pageflip() sees prepared exynos_fb,
>          and unmaps "old" fb
>  ==> but, since BUF_START_S[0] still points to that "old" fb...
>  ==> FIMD iommu fault
> 
> This patch ensures that fimd_irq_handler() only calls
> exynos_drm_crtc_finish_pageflip() if any previously scheduled flip
> has really completed.
> 
> This works because exynos_drm_crtc's flip fifo ensures that
> fimd_win_commit() is never called more than once per
> exynos_drm_crtc_finish_pageflip().
> 
> Signed-off-by: Daniel Kurtz <djkurtz at chromium.org>
> Reviewed-by: Sean Paul <seanpaul at chromium.org>
> Signed-off-by: Gustavo Padovan <gustavo.padovan at collabora.co.uk>
> ---
>  drivers/gpu/drm/exynos/exynos_drm_fimd.c | 26 ++++++++++++++++++++++++++
>  include/video/samsung_fimd.h             |  1 +
>  2 files changed, 27 insertions(+)
> 
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
> index e5810d1..afb0586 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
> @@ -55,6 +55,7 @@
>  #define VIDOSD_D(win)		(VIDOSD_BASE + 0x0C + (win) * 16)
>  
>  #define VIDWx_BUF_START(win, buf)	(VIDW_BUF_START(buf) + (win) * 8)
> +#define VIDWx_BUF_START_S(win, buf)     (VIDW_BUF_START_S(buf) + (win) * 8)
>  #define VIDWx_BUF_END(win, buf)		(VIDW_BUF_END(buf) + (win) * 8)
>  #define VIDWx_BUF_SIZE(win, buf)	(VIDW_BUF_SIZE(buf) + (win) * 4)
>  
> @@ -1039,6 +1040,7 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
>  {
>  	struct fimd_context *ctx = (struct fimd_context *)dev_id;
>  	u32 val, clear_bit;
> +	u32 start, start_s;
>  
>  	val = readl(ctx->regs + VIDINTCON1);
>  
> @@ -1066,6 +1068,30 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
>  		}
>  	}
>  
> +	/*
> +	 * Ensure finish_pageflip is called iff a pending flip has completed.
> +	 * This works around a race between a page_flip request and the latency
> +	 * between vblank interrupt and this irq_handler:
> +	 *   => FIMD vblank: BUF_START_S[0] := BUF_START[0], and asserts irq
> +	 *   | => fimd_win_commit(0) writes new BUF_START[0]
> +	 *   |    exynos_drm_crtc_try_do_flip() marks exynos_fb as prepared
> +	 *   => fimd_irq_handler()
> +	 *       exynos_drm_crtc_finish_pageflip() sees prepared exynos_fb,
> +	 *           and unmaps "old" fb
> +	 *   ==> but, since BUF_START_S[0] still points to that "old" fb...
> +	 *   ==> FIMD iommu fault
> +	 */
> +	start = readl(ctx->regs + VIDWx_BUF_START(0, 0));
> +	start_s = readl(ctx->regs + VIDWx_BUF_START_S(0, 0));
> +	if (start == start_s)
> +		exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
> +
> +	/* set wait vsync event to zero and wake up queue. */
> +	if (atomic_read(&ctx->wait_vsync_event)) {
> +		atomic_set(&ctx->wait_vsync_event, 0);
> +		wake_up(&ctx->wait_vsync_queue);
> +	}

Your patch makes codes duplicated. Above codes you added are called
already in case of using RGB interface. So move all your codes into RGB
interface routine if these codes are only valid in case of RGB interface.

Thanks,
Inki Dae

> +
>  out:
>  	return IRQ_HANDLED;
>  }
> diff --git a/include/video/samsung_fimd.h b/include/video/samsung_fimd.h
> index a20e4a3..f81d081 100644
> --- a/include/video/samsung_fimd.h
> +++ b/include/video/samsung_fimd.h
> @@ -291,6 +291,7 @@
>  
>  /* Video buffer addresses */
>  #define VIDW_BUF_START(_buff)			(0xA0 + ((_buff) * 8))
> +#define VIDW_BUF_START_S(_buff)                 (0x40A0 + ((_buff) * 8))
>  #define VIDW_BUF_START1(_buff)			(0xA4 + ((_buff) * 8))
>  #define VIDW_BUF_END(_buff)			(0xD0 + ((_buff) * 8))
>  #define VIDW_BUF_END1(_buff)			(0xD4 + ((_buff) * 8))
> 



More information about the dri-devel mailing list