From: Gustavo Padovan gustavo.padovan@collabora.co.uk
Hi,
This patchset adds a couple of changes to improve atomic modesetting:
* add check for the START shadow register for FIMD to only finish the update when the screen was actually updated. * add asynchronous atomic commit, so now page flips can be run asynchronously. It also add infrastructure to serialize commits for the same CRTC and wait all plane updates to finish. * enable the DRIVER_ATOMIC feature to enable userspace to use atomic IOCTL with exynos.
v2: rename prepare_plane/cleanup_plane to atomic_begin/atomic_flush
Please review.
Gustavo
Gustavo Padovan (11): drm/exynos: don't track enabled state at exynos_crtc drm/exynos: fimd: unify call to exynos_drm_crtc_finish_pageflip() drm/exynos: add begin and flush phases for planes drm/exynos: fimd: move window protect code to atomic_begin/flush drm/exynos: check for pending fb before finish update drm/exynos: add macro to get the address of START_S reg drm/exynos: fimd: only finish update if START == START_S drm/exynos: add atomic asynchronous commit drm/exynos: wait all planes updates to finish drm/exynos: remove wait queue for pending page flip drm/exynos: Enable atomic modesetting feature
drivers/gpu/drm/exynos/exynos5433_drm_decon.c | 10 +- drivers/gpu/drm/exynos/exynos7_drm_decon.c | 10 +- drivers/gpu/drm/exynos/exynos_drm_crtc.c | 69 +++++------ drivers/gpu/drm/exynos/exynos_drm_crtc.h | 4 +- drivers/gpu/drm/exynos/exynos_drm_drv.c | 158 +++++++++++++++++++++++++- drivers/gpu/drm/exynos/exynos_drm_drv.h | 24 +++- drivers/gpu/drm/exynos/exynos_drm_fb.c | 35 ------ drivers/gpu/drm/exynos/exynos_drm_fimd.c | 81 ++++++++----- drivers/gpu/drm/exynos/exynos_drm_plane.c | 2 + drivers/gpu/drm/exynos/exynos_drm_vidi.c | 10 +- drivers/gpu/drm/exynos/exynos_mixer.c | 10 +- include/video/samsung_fimd.h | 1 + 12 files changed, 309 insertions(+), 105 deletions(-)
From: Gustavo Padovan gustavo.padovan@collabora.co.uk
struct drm_crtc already stores the enabled state of the crtc thus we don't need to replicate enabled in exynos_drm_crtc.
Signed-off-by: Gustavo Padovan gustavo.padovan@collabora.co.uk --- drivers/gpu/drm/exynos/exynos_drm_crtc.c | 16 ---------------- drivers/gpu/drm/exynos/exynos_drm_drv.h | 1 - 2 files changed, 17 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index c478997..94eb831 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -25,14 +25,9 @@ static void exynos_drm_crtc_enable(struct drm_crtc *crtc) { struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
- if (exynos_crtc->enabled) - return; - if (exynos_crtc->ops->enable) exynos_crtc->ops->enable(exynos_crtc);
- exynos_crtc->enabled = true; - drm_crtc_vblank_on(crtc); }
@@ -40,9 +35,6 @@ static void exynos_drm_crtc_disable(struct drm_crtc *crtc) { struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
- if (!exynos_crtc->enabled) - return; - /* wait for the completion of page flip. */ if (!wait_event_timeout(exynos_crtc->pending_flip_queue, (exynos_crtc->event == NULL), HZ/20)) @@ -52,8 +44,6 @@ static void exynos_drm_crtc_disable(struct drm_crtc *crtc)
if (exynos_crtc->ops->disable) exynos_crtc->ops->disable(exynos_crtc); - - exynos_crtc->enabled = false; }
static bool @@ -172,9 +162,6 @@ int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe) struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(private->crtc[pipe]);
- if (!exynos_crtc->enabled) - return -EPERM; - if (exynos_crtc->ops->enable_vblank) return exynos_crtc->ops->enable_vblank(exynos_crtc);
@@ -187,9 +174,6 @@ void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe) struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(private->crtc[pipe]);
- if (!exynos_crtc->enabled) - return; - if (exynos_crtc->ops->disable_vblank) exynos_crtc->ops->disable_vblank(exynos_crtc); } diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 6b8a30f..a993aac 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -136,7 +136,6 @@ struct exynos_drm_crtc { struct drm_crtc base; enum exynos_drm_output_type type; unsigned int pipe; - bool enabled; wait_queue_head_t pending_flip_queue; struct drm_pending_vblank_event *event; const struct exynos_drm_crtc_ops *ops;
From: Gustavo Padovan gustavo.padovan@collabora.co.uk
Only set/clear the update bit in the CRTC's .atomic_begin()/flush() so all planes are really committed at the same time.
Signed-off-by: Gustavo Padovan gustavo.padovan@collabora.co.uk --- drivers/gpu/drm/exynos/exynos_drm_fimd.c | 57 +++++++++++++++++++------------- 1 file changed, 34 insertions(+), 23 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 30c1409..48d4fbe 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -591,6 +591,16 @@ static void fimd_shadow_protect_win(struct fimd_context *ctx, { u32 reg, bits, val;
+ /* + * SHADOWCON/PRTCON register is used for enabling timing. + * + * for example, once only width value of a register is set, + * if the dma is started then fimd hardware could malfunction so + * with protect window setting, the register fields with prefix '_F' + * wouldn't be updated at vsync also but updated once unprotect window + * is set. + */ + if (ctx->driver_data->has_shadowcon) { reg = SHADOWCON; bits = SHADOWCON_WINx_PROTECT(win); @@ -607,6 +617,28 @@ static void fimd_shadow_protect_win(struct fimd_context *ctx, writel(val, ctx->regs + reg); }
+static void fimd_atomic_begin(struct exynos_drm_crtc *crtc, + struct exynos_drm_plane *plane) +{ + struct fimd_context *ctx = crtc->ctx; + + if (ctx->suspended) + return; + + fimd_shadow_protect_win(ctx, plane->zpos, true); +} + +static void fimd_atomic_flush(struct exynos_drm_crtc *crtc, + struct exynos_drm_plane *plane) +{ + struct fimd_context *ctx = crtc->ctx; + + if (ctx->suspended) + return; + + fimd_shadow_protect_win(ctx, plane->zpos, false); +} + static void fimd_update_plane(struct exynos_drm_crtc *crtc, struct exynos_drm_plane *plane) { @@ -622,20 +654,6 @@ static void fimd_update_plane(struct exynos_drm_crtc *crtc, if (ctx->suspended) return;
- /* - * SHADOWCON/PRTCON register is used for enabling timing. - * - * for example, once only width value of a register is set, - * if the dma is started then fimd hardware could malfunction so - * with protect window setting, the register fields with prefix '_F' - * wouldn't be updated at vsync also but updated once unprotect window - * is set. - */ - - /* protect windows */ - fimd_shadow_protect_win(ctx, win, true); - - offset = plane->src_x * bpp; offset += plane->src_y * pitch;
@@ -707,9 +725,6 @@ static void fimd_update_plane(struct exynos_drm_crtc *crtc, if (ctx->driver_data->has_shadowcon) fimd_enable_shadow_channel_path(ctx, win, true);
- /* Enable DMA channel and unprotect windows */ - fimd_shadow_protect_win(ctx, win, false); - if (ctx->i80_if) atomic_set(&ctx->win_updated, 1); } @@ -723,16 +738,10 @@ static void fimd_disable_plane(struct exynos_drm_crtc *crtc, if (ctx->suspended) return;
- /* protect windows */ - fimd_shadow_protect_win(ctx, win, true); - fimd_enable_video_output(ctx, win, false);
if (ctx->driver_data->has_shadowcon) fimd_enable_shadow_channel_path(ctx, win, false); - - /* unprotect windows */ - fimd_shadow_protect_win(ctx, win, false); }
static void fimd_enable(struct exynos_drm_crtc *crtc) @@ -875,8 +884,10 @@ static const struct exynos_drm_crtc_ops fimd_crtc_ops = { .enable_vblank = fimd_enable_vblank, .disable_vblank = fimd_disable_vblank, .wait_for_vblank = fimd_wait_for_vblank, + .atomic_begin = fimd_atomic_begin, .update_plane = fimd_update_plane, .disable_plane = fimd_disable_plane, + .atomic_flush = fimd_atomic_flush, .te_handler = fimd_te_handler, .clock_enable = fimd_dp_clock_enable, };
From: Gustavo Padovan gustavo.padovan@collabora.co.uk
Unify handling of finished plane update to prepare for a following patch that will check for the START and START_S regs to really make sure that the plane was updated.
Signed-off-by: Gustavo Padovan gustavo.padovan@collabora.co.uk --- drivers/gpu/drm/exynos/exynos_drm_fimd.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 5def6bc..30c1409 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -896,15 +896,15 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id) if (ctx->pipe < 0 || !ctx->drm_dev) goto out;
- if (ctx->i80_if) { - exynos_drm_crtc_finish_pageflip(ctx->crtc); + if (!ctx->i80_if) + drm_crtc_handle_vblank(&ctx->crtc->base); + + exynos_drm_crtc_finish_pageflip(ctx->crtc);
+ if (ctx->i80_if) { /* Exits triggering mode */ atomic_set(&ctx->triggering, 0); } else { - drm_crtc_handle_vblank(&ctx->crtc->base); - exynos_drm_crtc_finish_pageflip(ctx->crtc); - /* set wait vsync event to zero and wake up queue. */ if (atomic_read(&ctx->wait_vsync_event)) { atomic_set(&ctx->wait_vsync_event, 0);
From: Gustavo Padovan gustavo.padovan@collabora.co.uk
.atomic_begin() and .atomic_flush() allows to perform extra operations before and after the update of planes. For FIMD for example this will be used to enable disable the shadow protection bit.
Signed-off-by: Gustavo Padovan gustavo.padovan@collabora.co.uk
--- v2: rename prepare_plane/cleanup_plane to atomic_begin/atomic_flush --- drivers/gpu/drm/exynos/exynos_drm_crtc.c | 19 +++++++++++++++++++ drivers/gpu/drm/exynos/exynos_drm_drv.h | 6 ++++++ 2 files changed, 25 insertions(+)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index 94eb831..54485b7 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -73,16 +73,35 @@ static void exynos_crtc_atomic_begin(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) { struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); + struct drm_plane *plane;
if (crtc->state->event) { WARN_ON(drm_crtc_vblank_get(crtc) != 0); exynos_crtc->event = crtc->state->event; } + + drm_atomic_crtc_for_each_plane(plane, crtc) { + struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane); + + if (exynos_crtc->ops->atomic_begin) + exynos_crtc->ops->atomic_begin(exynos_crtc, + exynos_plane); + } }
static void exynos_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) { + struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); + struct drm_plane *plane; + + drm_atomic_crtc_for_each_plane(plane, crtc) { + struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane); + + if (exynos_crtc->ops->atomic_flush) + exynos_crtc->ops->atomic_flush(exynos_crtc, + exynos_plane); + } }
static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = { diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index a993aac..28afecc 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -87,6 +87,8 @@ struct exynos_drm_plane { * @disable_vblank: specific driver callback for disabling vblank interrupt. * @wait_for_vblank: wait for vblank interrupt to make sure that * hardware overlay is updated. + * @atomic_begin: prepare a window to receive a update + * @atomic_flush: mark the end of a window update * @update_plane: apply hardware specific overlay data to registers. * @disable_plane: disable hardware specific overlay. * @te_handler: trigger to transfer video image at the tearing effect @@ -107,10 +109,14 @@ struct exynos_drm_crtc_ops { int (*enable_vblank)(struct exynos_drm_crtc *crtc); void (*disable_vblank)(struct exynos_drm_crtc *crtc); void (*wait_for_vblank)(struct exynos_drm_crtc *crtc); + void (*atomic_begin)(struct exynos_drm_crtc *crtc, + struct exynos_drm_plane *plane); void (*update_plane)(struct exynos_drm_crtc *crtc, struct exynos_drm_plane *plane); void (*disable_plane)(struct exynos_drm_crtc *crtc, struct exynos_drm_plane *plane); + void (*atomic_flush)(struct exynos_drm_crtc *crtc, + struct exynos_drm_plane *plane); void (*te_handler)(struct exynos_drm_crtc *crtc); void (*clock_enable)(struct exynos_drm_crtc *crtc, bool enable); };
From: Gustavo Padovan gustavo.padovan@collabora.co.uk
Only set/clear the update bit in the CRTC's .atomic_begin()/flush() so all planes are really committed at the same time.
Signed-off-by: Gustavo Padovan gustavo.padovan@collabora.co.uk
--- v2: rename prepare_plane/cleanup_plane to atomic_begin/atomic_flush --- drivers/gpu/drm/exynos/exynos_drm_fimd.c | 57 +++++++++++++++++++------------- 1 file changed, 34 insertions(+), 23 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 30c1409..005a996 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -591,6 +591,16 @@ static void fimd_shadow_protect_win(struct fimd_context *ctx, { u32 reg, bits, val;
+ /* + * SHADOWCON/PRTCON register is used for enabling timing. + * + * for example, once only width value of a register is set, + * if the dma is started then fimd hardware could malfunction so + * with protect window setting, the register fields with prefix '_F' + * wouldn't be updated at vsync also but updated once unprotect window + * is set. + */ + if (ctx->driver_data->has_shadowcon) { reg = SHADOWCON; bits = SHADOWCON_WINx_PROTECT(win); @@ -607,6 +617,28 @@ static void fimd_shadow_protect_win(struct fimd_context *ctx, writel(val, ctx->regs + reg); }
+static void fimd_atomic_begin(struct exynos_drm_crtc *crtc, + struct exynos_drm_plane *plane) +{ + struct fimd_context *ctx = crtc->ctx; + + if (ctx->suspended) + return; + + fimd_shadow_protect_win(ctx, plane->zpos, true); +} + +static void fimd_atomic_flush(struct exynos_drm_crtc *crtc, + struct exynos_drm_plane *plane) +{ + struct fimd_context *ctx = crtc->ctx; + + if (ctx->suspended) + return; + + fimd_shadow_protect_win(ctx, plane->zpos, false); +} + static void fimd_update_plane(struct exynos_drm_crtc *crtc, struct exynos_drm_plane *plane) { @@ -622,20 +654,6 @@ static void fimd_update_plane(struct exynos_drm_crtc *crtc, if (ctx->suspended) return;
- /* - * SHADOWCON/PRTCON register is used for enabling timing. - * - * for example, once only width value of a register is set, - * if the dma is started then fimd hardware could malfunction so - * with protect window setting, the register fields with prefix '_F' - * wouldn't be updated at vsync also but updated once unprotect window - * is set. - */ - - /* protect windows */ - fimd_shadow_protect_win(ctx, win, true); - - offset = plane->src_x * bpp; offset += plane->src_y * pitch;
@@ -707,9 +725,6 @@ static void fimd_update_plane(struct exynos_drm_crtc *crtc, if (ctx->driver_data->has_shadowcon) fimd_enable_shadow_channel_path(ctx, win, true);
- /* Enable DMA channel and unprotect windows */ - fimd_shadow_protect_win(ctx, win, false); - if (ctx->i80_if) atomic_set(&ctx->win_updated, 1); } @@ -723,16 +738,10 @@ static void fimd_disable_plane(struct exynos_drm_crtc *crtc, if (ctx->suspended) return;
- /* protect windows */ - fimd_shadow_protect_win(ctx, win, true); - fimd_enable_video_output(ctx, win, false);
if (ctx->driver_data->has_shadowcon) fimd_enable_shadow_channel_path(ctx, win, false); - - /* unprotect windows */ - fimd_shadow_protect_win(ctx, win, false); }
static void fimd_enable(struct exynos_drm_crtc *crtc) @@ -875,8 +884,10 @@ static const struct exynos_drm_crtc_ops fimd_crtc_ops = { .enable_vblank = fimd_enable_vblank, .disable_vblank = fimd_disable_vblank, .wait_for_vblank = fimd_wait_for_vblank, + .atomic_begin = fimd_atomic_begin, .update_plane = fimd_update_plane, .disable_plane = fimd_disable_plane, + .atomic_flush = fimd_atomic_flush, .te_handler = fimd_te_handler, .clock_enable = fimd_dp_clock_enable, };
From: Gustavo Padovan gustavo.padovan@collabora.co.uk
The current code was ignoring the end of update for all overlay planes, caring only for the primary plane update in case of pageflip.
This change adds a change to start to check for pending updates for all planes through exynos_plane->pending_fb. At the start of plane update the pending_fb is set with the fb to be shown on the screen. Then only when to fb is already presented in the screen we set pending_fb to NULL to signal that the update was finished.
Signed-off-by: Gustavo Padovan gustavo.padovan@collabora.co.uk
fixup! drm/exynos: check for pending fb before finish update --- drivers/gpu/drm/exynos/exynos5433_drm_decon.c | 10 +++++++++- drivers/gpu/drm/exynos/exynos7_drm_decon.c | 10 +++++++++- drivers/gpu/drm/exynos/exynos_drm_crtc.c | 7 ++++--- drivers/gpu/drm/exynos/exynos_drm_crtc.h | 3 ++- drivers/gpu/drm/exynos/exynos_drm_drv.h | 1 + drivers/gpu/drm/exynos/exynos_drm_fimd.c | 10 +++++++++- drivers/gpu/drm/exynos/exynos_drm_plane.c | 2 ++ drivers/gpu/drm/exynos/exynos_drm_vidi.c | 10 +++++++++- drivers/gpu/drm/exynos/exynos_mixer.c | 10 +++++++++- 9 files changed, 54 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos5433_drm_decon.c b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c index 484e312..8d65e45 100644 --- a/drivers/gpu/drm/exynos/exynos5433_drm_decon.c +++ b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c @@ -542,13 +542,21 @@ static irqreturn_t decon_lcd_sys_irq_handler(int irq, void *dev_id) { struct decon_context *ctx = dev_id; u32 val; + int win;
if (!test_bit(BIT_CLKS_ENABLED, &ctx->enabled)) goto out;
val = readl(ctx->addr + DECON_VIDINTCON1); if (val & VIDINTCON1_INTFRMDONEPEND) { - exynos_drm_crtc_finish_pageflip(ctx->crtc); + for (win = 0 ; win < WINDOWS_NR ; win++) { + struct exynos_drm_plane *plane = &ctx->planes[win]; + + if (!plane->pending_fb) + continue; + + exynos_drm_crtc_finish_update(ctx->crtc, plane); + }
/* clear */ writel(VIDINTCON1_INTFRMDONEPEND, diff --git a/drivers/gpu/drm/exynos/exynos7_drm_decon.c b/drivers/gpu/drm/exynos/exynos7_drm_decon.c index 0792654..7651499 100644 --- a/drivers/gpu/drm/exynos/exynos7_drm_decon.c +++ b/drivers/gpu/drm/exynos/exynos7_drm_decon.c @@ -623,6 +623,7 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id) { struct decon_context *ctx = (struct decon_context *)dev_id; u32 val, clear_bit; + int win;
val = readl(ctx->regs + VIDINTCON1);
@@ -636,7 +637,14 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id)
if (!ctx->i80_if) { drm_crtc_handle_vblank(&ctx->crtc->base); - exynos_drm_crtc_finish_pageflip(ctx->crtc); + for (win = 0 ; win < WINDOWS_NR ; win++) { + struct exynos_drm_plane *plane = &ctx->planes[win]; + + if (!plane->pending_fb) + continue; + + exynos_drm_crtc_finish_update(ctx->crtc, plane); + }
/* set wait vsync event to zero and wake up queue. */ if (atomic_read(&ctx->wait_vsync_event)) { diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index 54485b7..582e041 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -197,18 +197,19 @@ void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe) exynos_crtc->ops->disable_vblank(exynos_crtc); }
-void exynos_drm_crtc_finish_pageflip(struct exynos_drm_crtc *exynos_crtc) +void exynos_drm_crtc_finish_update(struct exynos_drm_crtc *exynos_crtc, + struct exynos_drm_plane *exynos_plane) { struct drm_crtc *crtc = &exynos_crtc->base; unsigned long flags;
+ exynos_plane->pending_fb = NULL; + spin_lock_irqsave(&crtc->dev->event_lock, flags); if (exynos_crtc->event) { - drm_crtc_send_vblank_event(crtc, exynos_crtc->event); drm_crtc_vblank_put(crtc); wake_up(&exynos_crtc->pending_flip_queue); - }
exynos_crtc->event = NULL; diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.h b/drivers/gpu/drm/exynos/exynos_drm_crtc.h index 9e7027d..8bedfde 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.h +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.h @@ -25,7 +25,8 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev, void *context); int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe); void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe); -void exynos_drm_crtc_finish_pageflip(struct exynos_drm_crtc *exynos_crtc); +void exynos_drm_crtc_finish_update(struct exynos_drm_crtc *exynos_crtc, + struct exynos_drm_plane *exynos_plane); void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb);
/* This function gets pipe value to crtc device matched with out_type. */ diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 28afecc..8116803 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -74,6 +74,7 @@ struct exynos_drm_plane { unsigned int v_ratio; dma_addr_t dma_addr[MAX_FB_BUFFER]; unsigned int zpos; + struct drm_framebuffer *pending_fb; };
/* diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 005a996..fc26c3e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -896,6 +896,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; + int win;
val = readl(ctx->regs + VIDINTCON1);
@@ -910,7 +911,14 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id) if (!ctx->i80_if) drm_crtc_handle_vblank(&ctx->crtc->base);
- exynos_drm_crtc_finish_pageflip(ctx->crtc); + for (win = 0 ; win < WINDOWS_NR ; win++) { + struct exynos_drm_plane *plane = &ctx->planes[win]; + + if (!plane->pending_fb) + continue; + + exynos_drm_crtc_finish_update(ctx->crtc, plane); + }
if (ctx->i80_if) { /* Exits triggering mode */ diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c index d9a68fd..fad7dfc 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c @@ -168,6 +168,8 @@ static void exynos_plane_atomic_update(struct drm_plane *plane, state->src_x >> 16, state->src_y >> 16, state->src_w >> 16, state->src_h >> 16);
+ exynos_plane->pending_fb = state->fb; + if (exynos_crtc->ops->update_plane) exynos_crtc->ops->update_plane(exynos_crtc, exynos_plane); } diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c index 581af35..b6d00dd 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c @@ -179,6 +179,7 @@ static void vidi_fake_vblank_handler(struct work_struct *work) { struct vidi_context *ctx = container_of(work, struct vidi_context, work); + int win;
if (ctx->pipe < 0) return; @@ -197,7 +198,14 @@ static void vidi_fake_vblank_handler(struct work_struct *work)
mutex_unlock(&ctx->lock);
- exynos_drm_crtc_finish_pageflip(ctx->crtc); + for (win = 0 ; win < WINDOWS_NR ; win++) { + struct exynos_drm_plane *plane = &ctx->planes[win]; + + if (!plane->pending_fb) + continue; + + exynos_drm_crtc_finish_update(ctx->crtc, plane); + } }
static int vidi_show_connection(struct device *dev, diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index e68340c..d7e7811 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -716,6 +716,7 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg) struct mixer_context *ctx = arg; struct mixer_resources *res = &ctx->mixer_res; u32 val, base, shadow; + int win;
spin_lock(&res->reg_slock);
@@ -742,7 +743,14 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg) }
drm_crtc_handle_vblank(&ctx->crtc->base); - exynos_drm_crtc_finish_pageflip(ctx->crtc); + for (win = 0 ; win < MIXER_WIN_NR ; win++) { + struct exynos_drm_plane *plane = &ctx->planes[win]; + + if (!plane->pending_fb) + continue; + + exynos_drm_crtc_finish_update(ctx->crtc, plane); + }
/* set wait vsync event to zero and wake up queue. */ if (atomic_read(&ctx->wait_vsync_event)) {
From: Gustavo Padovan gustavo.padovan@collabora.co.uk
This macro is need to get the value of the START shadow register, that will tell if an framebuffer is currently displayed on the screen or not.
Signed-off-by: Gustavo Padovan gustavo.padovan@collabora.co.uk --- include/video/samsung_fimd.h | 1 + 1 file changed, 1 insertion(+)
diff --git a/include/video/samsung_fimd.h b/include/video/samsung_fimd.h index 0530e5a..d8fc96e 100644 --- a/include/video/samsung_fimd.h +++ b/include/video/samsung_fimd.h @@ -296,6 +296,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))
From: Gustavo Padovan gustavo.padovan@collabora.co.uk
fimd_update_plane() programs BUF_START[win] and during the update BUF_START[win] is copied to BUF_START_S[win] (its shadow register) and starts scanning out, then it raises a irq.
The fimd_irq_handler, in the case we have a pending_fb, will check the fb value was copied to START_S register and finish the update in case of success.
Based on patch from Daniel Kurtz djkurtz@chromium.org
Signed-off-by: Gustavo Padovan gustavo.padovan@collabora.co.uk --- drivers/gpu/drm/exynos/exynos_drm_fimd.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index fc26c3e..d96044f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -59,6 +59,7 @@ #define VIDWnALPHA1(win) (VIDW_ALPHA + 0x04 + (win) * 8)
#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)
@@ -895,7 +896,7 @@ static const struct exynos_drm_crtc_ops fimd_crtc_ops = { 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 val, clear_bit, start, start_s; int win;
val = readl(ctx->regs + VIDINTCON1); @@ -917,7 +918,10 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id) if (!plane->pending_fb) continue;
- exynos_drm_crtc_finish_update(ctx->crtc, plane); + start = readl(ctx->regs + VIDWx_BUF_START(win, 0)); + start_s = readl(ctx->regs + VIDWx_BUF_START_S(win, 0)); + if (start == start_s) + exynos_drm_crtc_finish_update(ctx->crtc, plane); }
if (ctx->i80_if) {
From: Gustavo Padovan gustavo.padovan@collabora.co.uk
The atomic modesetting interfaces supports async commits that should be implemented by the drivers. If drm core requests an async commit exynos_atomic_commit() will now schedule a work task to run the update later.
It also serializes commits that needs to run on the same crtc, putting the following commit to wait until the current one is finished.
Signed-off-by: Gustavo Padovan gustavo.padovan@collabora.co.uk --- drivers/gpu/drm/exynos/exynos_drm_drv.c | 113 ++++++++++++++++++++++++++++++++ drivers/gpu/drm/exynos/exynos_drm_drv.h | 11 ++++ drivers/gpu/drm/exynos/exynos_drm_fb.c | 35 ---------- 3 files changed, 124 insertions(+), 35 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 72b88c7..fc207f8 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -13,6 +13,8 @@
#include <linux/pm_runtime.h> #include <drm/drmP.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> #include <drm/drm_crtc_helper.h>
#include <linux/component.h> @@ -36,6 +38,56 @@ #define DRIVER_MAJOR 1 #define DRIVER_MINOR 0
+struct exynos_atomic_commit { + struct work_struct work; + struct drm_device *dev; + struct drm_atomic_state *state; + u32 crtcs; +}; + +static void exynos_atomic_commit_complete(struct exynos_atomic_commit *commit) +{ + struct drm_device *dev = commit->dev; + struct exynos_drm_private *priv = dev->dev_private; + struct drm_atomic_state *state = commit->state; + + drm_atomic_helper_commit_modeset_disables(dev, state); + + drm_atomic_helper_commit_modeset_enables(dev, state); + + /* + * Exynos can't update planes with CRTCs and encoders disabled, + * its updates routines, specially for FIMD, requires the clocks + * to be enabled. So it is necessary to handle the modeset operations + * *before* the commit_planes() step, this way it will always + * have the relevant clocks enabled to perform the update. + */ + + drm_atomic_helper_commit_planes(dev, state); + + drm_atomic_helper_wait_for_vblanks(dev, state); + + drm_atomic_helper_cleanup_planes(dev, state); + + drm_atomic_state_free(state); + + spin_lock(&priv->lock); + priv->pending &= ~commit->crtcs; + spin_unlock(&priv->lock); + + wake_up_all(&priv->wait); + + kfree(commit); +} + +static void exynos_drm_atomic_work(struct work_struct *work) +{ + struct exynos_atomic_commit *commit = container_of(work, + struct exynos_atomic_commit, work); + + exynos_atomic_commit_complete(commit); +} + static int exynos_drm_load(struct drm_device *dev, unsigned long flags) { struct exynos_drm_private *private; @@ -47,6 +99,9 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags) if (!private) return -ENOMEM;
+ init_waitqueue_head(&private->wait); + spin_lock_init(&private->lock); + dev_set_drvdata(dev->dev, dev); dev->dev_private = (void *)private;
@@ -149,6 +204,64 @@ static int exynos_drm_unload(struct drm_device *dev) return 0; }
+static int commit_is_pending(struct exynos_drm_private *priv, u32 crtcs) +{ + bool pending; + + spin_lock(&priv->lock); + pending = priv->pending & crtcs; + spin_unlock(&priv->lock); + + return pending; +} + +int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state, + bool async) +{ + struct exynos_drm_private *priv = dev->dev_private; + struct exynos_atomic_commit *commit; + int i, ret; + + commit = kzalloc(sizeof(*commit), GFP_KERNEL); + if (!commit) + return -ENOMEM; + + ret = drm_atomic_helper_prepare_planes(dev, state); + if (ret) { + kfree(commit); + return ret; + } + + /* This is the point of no return */ + + INIT_WORK(&commit->work, exynos_drm_atomic_work); + commit->dev = dev; + commit->state = state; + + /* Wait until all affected CRTCs have completed previous commits and + * mark them as pending. + */ + for (i = 0; i < dev->mode_config.num_crtc; ++i) { + if (state->crtcs[i]) + commit->crtcs |= 1 << drm_crtc_index(state->crtcs[i]); + } + + wait_event(priv->wait, !commit_is_pending(priv, commit->crtcs)); + + spin_lock(&priv->lock); + priv->pending |= commit->crtcs; + spin_unlock(&priv->lock); + + drm_atomic_helper_swap_state(dev, state); + + if (async) + schedule_work(&commit->work); + else + exynos_atomic_commit_complete(commit); + + return 0; +} + static int exynos_drm_suspend(struct drm_device *dev, pm_message_t state) { struct drm_connector *connector; diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 8116803..b06fbd4 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -170,6 +170,9 @@ struct drm_exynos_file_private { * @da_space_size: size of device address space. * if 0 then default value is used for it. * @pipe: the pipe number for this crtc/manager. + * @pending: the crtcs that have pending updates to finish + * @lock: protect access to @pending + * @wait: wait an atomic commit to finish */ struct exynos_drm_private { struct drm_fb_helper *fb_helper; @@ -185,6 +188,11 @@ struct exynos_drm_private { unsigned long da_space_size;
unsigned int pipe; + + /* for atomic commit */ + u32 pending; + spinlock_t lock; + wait_queue_head_t wait; };
/* @@ -243,6 +251,9 @@ static inline int exynos_dpi_bind(struct drm_device *dev, } #endif
+int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state, + bool async); +
extern struct platform_driver fimd_driver; extern struct platform_driver exynos5433_decon_driver; diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c index 9738f4e..59ebbe5 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fb.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c @@ -267,41 +267,6 @@ static void exynos_drm_output_poll_changed(struct drm_device *dev) exynos_drm_fbdev_init(dev); }
-static int exynos_atomic_commit(struct drm_device *dev, - struct drm_atomic_state *state, - bool async) -{ - int ret; - - ret = drm_atomic_helper_prepare_planes(dev, state); - if (ret) - return ret; - - /* This is the point of no return */ - - drm_atomic_helper_swap_state(dev, state); - - drm_atomic_helper_commit_modeset_disables(dev, state); - - drm_atomic_helper_commit_modeset_enables(dev, state); - - /* - * Exynos can't update planes with CRTCs and encoders disabled, - * its updates routines, specially for FIMD, requires the clocks - * to be enabled. So it is necessary to handle the modeset operations - * *before* the commit_planes() step, this way it will always - * have the relevant clocks enabled to perform the update. - */ - - drm_atomic_helper_commit_planes(dev, state); - - drm_atomic_helper_cleanup_planes(dev, state); - - drm_atomic_state_free(state); - - return 0; -} - static const struct drm_mode_config_funcs exynos_drm_mode_config_funcs = { .fb_create = exynos_user_fb_create, .output_poll_changed = exynos_drm_output_poll_changed,
From: Gustavo Padovan gustavo.padovan@collabora.co.uk
Add infrastructure to wait for all planes updates to finish by using an atomic_t variable to track how many pending updates we are waiting plus a wait_queue for the wait part.
It also changes vblank behaviour and keeps it enabled for all types of updates
Signed-off-by: Gustavo Padovan gustavo.padovan@collabora.co.uk --- drivers/gpu/drm/exynos/exynos_drm_crtc.c | 18 +++++++++---- drivers/gpu/drm/exynos/exynos_drm_crtc.h | 1 + drivers/gpu/drm/exynos/exynos_drm_drv.c | 44 +++++++++++++++++++++++++++++++- drivers/gpu/drm/exynos/exynos_drm_drv.h | 4 +++ 4 files changed, 61 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index 582e041..d6c2c3f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -75,10 +75,7 @@ static void exynos_crtc_atomic_begin(struct drm_crtc *crtc, struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); struct drm_plane *plane;
- if (crtc->state->event) { - WARN_ON(drm_crtc_vblank_get(crtc) != 0); - exynos_crtc->event = crtc->state->event; - } + exynos_crtc->event = crtc->state->event;
drm_atomic_crtc_for_each_plane(plane, crtc) { struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane); @@ -156,6 +153,8 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev, exynos_crtc->ops = ops; exynos_crtc->ctx = ctx;
+ init_waitqueue_head(&exynos_crtc->wait_update); + crtc = &exynos_crtc->base;
private->crtc[pipe] = crtc; @@ -197,6 +196,13 @@ void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe) exynos_crtc->ops->disable_vblank(exynos_crtc); }
+void exynos_drm_crtc_wait_pending_update(struct exynos_drm_crtc *exynos_crtc) +{ + wait_event_timeout(exynos_crtc->wait_update, + (atomic_read(&exynos_crtc->pending_update) == 0), + msecs_to_jiffies(50)); +} + void exynos_drm_crtc_finish_update(struct exynos_drm_crtc *exynos_crtc, struct exynos_drm_plane *exynos_plane) { @@ -205,10 +211,12 @@ void exynos_drm_crtc_finish_update(struct exynos_drm_crtc *exynos_crtc,
exynos_plane->pending_fb = NULL;
+ if (atomic_dec_and_test(&exynos_crtc->pending_update)) + wake_up(&exynos_crtc->wait_update); + spin_lock_irqsave(&crtc->dev->event_lock, flags); if (exynos_crtc->event) { drm_crtc_send_vblank_event(crtc, exynos_crtc->event); - drm_crtc_vblank_put(crtc); wake_up(&exynos_crtc->pending_flip_queue); }
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.h b/drivers/gpu/drm/exynos/exynos_drm_crtc.h index 8bedfde..f87d4ab 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.h +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.h @@ -25,6 +25,7 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev, void *context); int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe); void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe); +void exynos_drm_crtc_wait_pending_update(struct exynos_drm_crtc *exynos_crtc); void exynos_drm_crtc_finish_update(struct exynos_drm_crtc *exynos_crtc, struct exynos_drm_plane *exynos_plane); void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb); diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index fc207f8..881f178 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -45,11 +45,37 @@ struct exynos_atomic_commit { u32 crtcs; };
+static void exynos_atomic_wait_for_commit(struct drm_atomic_state *state) +{ + struct drm_crtc_state *crtc_state; + struct drm_crtc *crtc; + int i, ret; + + for_each_crtc_in_state(state, crtc, crtc_state, i) { + struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); + + if (!crtc->state->enable) + continue; + + ret = drm_crtc_vblank_get(crtc); + if (ret) + continue; + + exynos_drm_crtc_wait_pending_update(exynos_crtc); + drm_crtc_vblank_put(crtc); + } +} + static void exynos_atomic_commit_complete(struct exynos_atomic_commit *commit) { struct drm_device *dev = commit->dev; struct exynos_drm_private *priv = dev->dev_private; struct drm_atomic_state *state = commit->state; + struct drm_plane *plane; + struct drm_crtc *crtc; + struct drm_plane_state *plane_state; + struct drm_crtc_state *crtc_state; + int i;
drm_atomic_helper_commit_modeset_disables(dev, state);
@@ -63,9 +89,25 @@ static void exynos_atomic_commit_complete(struct exynos_atomic_commit *commit) * have the relevant clocks enabled to perform the update. */
+ for_each_crtc_in_state(state, crtc, crtc_state, i) { + struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); + + atomic_set(&exynos_crtc->pending_update, 0); + } + + for_each_plane_in_state(state, plane, plane_state, i) { + struct exynos_drm_crtc *exynos_crtc = + to_exynos_crtc(plane->crtc); + + if (!plane->crtc) + continue; + + atomic_inc(&exynos_crtc->pending_update); + } + drm_atomic_helper_commit_planes(dev, state);
- drm_atomic_helper_wait_for_vblanks(dev, state); + exynos_atomic_wait_for_commit(state);
drm_atomic_helper_cleanup_planes(dev, state);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index b06fbd4..7193d94 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -136,6 +136,8 @@ struct exynos_drm_crtc_ops { * this pipe value. * @enabled: if the crtc is enabled or not * @event: vblank event that is currently queued for flip + * @wait_update: wait all pending planes updates to finish + * @pending_update: number of pending plane updates in this crtc * @ops: pointer to callbacks for exynos drm specific functionality * @ctx: A pointer to the crtc's implementation specific context */ @@ -145,6 +147,8 @@ struct exynos_drm_crtc { unsigned int pipe; wait_queue_head_t pending_flip_queue; struct drm_pending_vblank_event *event; + wait_queue_head_t wait_update; + atomic_t pending_update; const struct exynos_drm_crtc_ops *ops; void *ctx; };
From: Gustavo Padovan gustavo.padovan@collabora.co.uk
Exynos atomic commit procedures already does this job of waiting for pending updates to finish, that means using pending_flip_queue is pointless now because the disable CRTC procedure will never happen during a page_flip.
Signed-off-by: Gustavo Padovan gustavo.padovan@collabora.co.uk --- drivers/gpu/drm/exynos/exynos_drm_crtc.c | 11 +---------- drivers/gpu/drm/exynos/exynos_drm_drv.h | 1 - 2 files changed, 1 insertion(+), 11 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index d6c2c3f..0872aa2f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -35,11 +35,6 @@ static void exynos_drm_crtc_disable(struct drm_crtc *crtc) { struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
- /* wait for the completion of page flip. */ - if (!wait_event_timeout(exynos_crtc->pending_flip_queue, - (exynos_crtc->event == NULL), HZ/20)) - exynos_crtc->event = NULL; - drm_crtc_vblank_off(crtc);
if (exynos_crtc->ops->disable) @@ -146,8 +141,6 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev, if (!exynos_crtc) return ERR_PTR(-ENOMEM);
- init_waitqueue_head(&exynos_crtc->pending_flip_queue); - exynos_crtc->pipe = pipe; exynos_crtc->type = type; exynos_crtc->ops = ops; @@ -215,10 +208,8 @@ void exynos_drm_crtc_finish_update(struct exynos_drm_crtc *exynos_crtc, wake_up(&exynos_crtc->wait_update);
spin_lock_irqsave(&crtc->dev->event_lock, flags); - if (exynos_crtc->event) { + if (exynos_crtc->event) drm_crtc_send_vblank_event(crtc, exynos_crtc->event); - wake_up(&exynos_crtc->pending_flip_queue); - }
exynos_crtc->event = NULL; spin_unlock_irqrestore(&crtc->dev->event_lock, flags); diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 7193d94..b7ba21d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -145,7 +145,6 @@ struct exynos_drm_crtc { struct drm_crtc base; enum exynos_drm_output_type type; unsigned int pipe; - wait_queue_head_t pending_flip_queue; struct drm_pending_vblank_event *event; wait_queue_head_t wait_update; atomic_t pending_update;
From: Gustavo Padovan gustavo.padovan@collabora.co.uk
Now that atomic modesetting is implemented for exynos enable the DRIVER_ATOMIC flag on the driver's features.
Signed-off-by: Gustavo Padovan gustavo.padovan@collabora.co.uk --- drivers/gpu/drm/exynos/exynos_drm_drv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 881f178..c882fd3 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -438,7 +438,8 @@ static const struct file_operations exynos_drm_driver_fops = { };
static struct drm_driver exynos_drm_driver = { - .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME, + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME + | DRIVER_ATOMIC, .load = exynos_drm_load, .unload = exynos_drm_unload, .open = exynos_drm_open,
dri-devel@lists.freedesktop.org