From: Gustavo Padovan gustavo.padovan@collabora.co.uk
Hi,
The first patch in this series add support to async page flips, but it doesn't improve the refresh rate when using vidi+fimd.
The second patch fixes an issue on vidi that solves the refresh rate issue when only vidi is used. For the case where vidi + fimd is used it improves the refresh rate from 20Hz to 28Hz. There is room to improve this more, but I think these patches can already be pushed upstream.
It is important to remember that the low refresh rate issue only happens when vidi is used along with another crtc. It doesn't happen in a setup that has only FIMD and/or Mixer, thus most of use cases out there are not affected.
Gustavo
--- Gustavo Padovan (3): drm/exynos: add atomic asynchronous commit drm/exynos: vidi: always handle vblank on planes updates drm/exynos: pass the correct pipe number
drivers/gpu/drm/exynos/exynos_drm_crtc.c | 2 +- drivers/gpu/drm/exynos/exynos_drm_drv.c | 6 ++++ drivers/gpu/drm/exynos/exynos_drm_drv.h | 6 ++++ drivers/gpu/drm/exynos/exynos_drm_fb.c | 51 +++++++++++++++++++++++++------- drivers/gpu/drm/exynos/exynos_drm_vidi.c | 3 +- 5 files changed, 55 insertions(+), 13 deletions(-)
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 schedule a work task to run the update later. It also waits to an update to finishes to schedule a new one.
Signed-off-by: Gustavo Padovan gustavo.padovan@collabora.co.uk --- drivers/gpu/drm/exynos/exynos_drm_drv.c | 6 ++++ drivers/gpu/drm/exynos/exynos_drm_drv.h | 6 ++++ drivers/gpu/drm/exynos/exynos_drm_fb.c | 51 ++++++++++++++++++++++++++------- 3 files changed, 52 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 591bdec..aafb419 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -47,6 +47,9 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags) if (!private) return -ENOMEM;
+ INIT_WORK(&private->work, exynos_drm_atomic_work); + private->dev = dev; + dev_set_drvdata(dev->dev, dev); dev->dev_private = (void *)private;
@@ -127,6 +130,8 @@ err_free_private:
static int exynos_drm_unload(struct drm_device *dev) { + struct exynos_drm_private *private = dev->dev_private; + exynos_drm_device_subdrv_remove(dev);
exynos_drm_fbdev_fini(dev); @@ -137,6 +142,7 @@ static int exynos_drm_unload(struct drm_device *dev) drm_mode_config_cleanup(dev); drm_release_iommu_mapping(dev);
+ flush_work(&private->work); kfree(dev->dev_private); dev->dev_private = NULL;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index a384569..e94d527 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -243,6 +243,10 @@ struct exynos_drm_private { unsigned long da_space_size;
unsigned int pipe; + + struct drm_device *dev; + struct work_struct work; + struct drm_atomic_state *state; };
/* @@ -295,6 +299,8 @@ static inline int exynos_dpi_remove(struct exynos_drm_display *display) } #endif
+void exynos_drm_atomic_work(struct work_struct *work); + /* This function creates a encoder and a connector, and initializes them. */ int exynos_drm_create_enc_conn(struct drm_device *dev, struct exynos_drm_display *display); diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c index 789db6f..d1b28fe 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fb.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c @@ -267,20 +267,18 @@ 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) +static void exynos_atomic_commit_schedule(struct drm_device *dev, + struct drm_atomic_state *state) { - int ret; - - ret = drm_atomic_helper_prepare_planes(dev, state); - if (ret) - return ret; - - /* This is the point of no return */ + struct exynos_drm_private *private = dev->dev_private;
- drm_atomic_helper_swap_state(dev, state); + private->state = state; + schedule_work(&private->work); +}
+static void exynos_atomic_commit_complete(struct drm_device *dev, + struct drm_atomic_state *state) +{ drm_atomic_helper_commit_modeset_disables(dev, state);
drm_atomic_helper_commit_modeset_enables(dev, state); @@ -300,6 +298,37 @@ static int exynos_atomic_commit(struct drm_device *dev, drm_atomic_helper_cleanup_planes(dev, state);
drm_atomic_state_free(state); +} + +void exynos_drm_atomic_work(struct work_struct *work) +{ + struct exynos_drm_private *private = container_of(work, + struct exynos_drm_private, work); + + exynos_atomic_commit_complete(private->dev, private->state); +} + +static int exynos_atomic_commit(struct drm_device *dev, + struct drm_atomic_state *state, + bool async) +{ + struct exynos_drm_private *private = dev->dev_private; + int ret; + + ret = drm_atomic_helper_prepare_planes(dev, state); + if (ret) + return ret; + + /* This is the point of no return */ + + flush_work(&private->work); + + drm_atomic_helper_swap_state(dev, state); + + if (async) + exynos_atomic_commit_schedule(dev, state); + else + exynos_atomic_commit_complete(dev, state);
return 0; }
From: Gustavo Padovan gustavo.padovan@collabora.co.uk
We need to call drm_handle_vblank() after each successful plane update to update vblank counter and send the necessary events.
Signed-off-by: Gustavo Padovan gustavo.padovan@collabora.co.uk --- drivers/gpu/drm/exynos/exynos_drm_vidi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c index 3413393..4706d45 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c @@ -193,10 +193,11 @@ static void vidi_fake_vblank_handler(struct work_struct *work) /* refresh rate is about 50Hz. */ usleep_range(16000, 20000);
+ drm_handle_vblank(ctx->drm_dev, ctx->pipe); + mutex_lock(&ctx->lock);
if (ctx->direct_vblank) { - drm_handle_vblank(ctx->drm_dev, ctx->pipe); ctx->direct_vblank = false; mutex_unlock(&ctx->lock); return;
From: Gustavo Padovan gustavo.padovan@collabora.co.uk
Instead of giving -1 to as arg to drm_send_vblank_event() pass the correct pipe number to it.
Signed-off-by: Gustavo Padovan gustavo.padovan@collabora.co.uk --- drivers/gpu/drm/exynos/exynos_drm_crtc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index 644b4b7..22b9ca0 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -203,7 +203,7 @@ void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe) spin_lock_irqsave(&dev->event_lock, flags); if (exynos_crtc->event) {
- drm_send_vblank_event(dev, -1, exynos_crtc->event); + drm_send_vblank_event(dev, pipe, exynos_crtc->event); drm_vblank_put(dev, pipe); wake_up(&exynos_crtc->pending_flip_queue);
dri-devel@lists.freedesktop.org