Before freeing a framebuffer, we call complete_scanout encoder
function which just calls wait for vblank of all the encoders.
This is not a very optimized method since a framebuffer might not
be in use by a crtc and we end up waiting for vblank unnecessarily.

Instead, this patch modifies the wait_for_vblank interface to
complete_scanout interface. In this function, each crtc must
check if the fb is currently being used. If it is being used,
it must wait for vsync (or even disable a window if necessary)
and ensure that the buffer is no longer used by crtc before returning.
So if a crtc is not actually reading from the buffer, the complete
scanout function will just return and not wait for vsync.

Signed-off-by: Prathyush K <prathyush.k at samsung.com>
---
 drivers/gpu/drm/exynos/exynos_drm_drv.h     |  7 ++++---
 drivers/gpu/drm/exynos/exynos_drm_encoder.c | 23 +++++++++++------------
 drivers/gpu/drm/exynos/exynos_drm_encoder.h |  4 +++-
 drivers/gpu/drm/exynos/exynos_drm_fb.c      | 13 +++++++++++--
 4 files changed, 29 insertions(+), 18 deletions(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h 
b/drivers/gpu/drm/exynos/exynos_drm_drv.h
index b9e51bc..d351daf 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
@@ -167,8 +167,7 @@ struct exynos_drm_display_ops {
  * @commit: set current hw specific display mode to hw.
  * @enable_vblank: specific driver callback for enabling vblank interrupt.
  * @disable_vblank: specific driver callback for disabling vblank interrupt.
- * @wait_for_vblank: wait for vblank interrupt to make sure that
- *     hardware overlay is updated.
+ * @complete_scanout: complete scan of buffer so that it can be freed.
  */
 struct exynos_drm_manager_ops {
        void (*dpms)(struct device *subdrv_dev, int mode);
@@ -183,7 +182,9 @@ struct exynos_drm_manager_ops {
        void (*commit)(struct device *subdrv_dev);
        int (*enable_vblank)(struct device *subdrv_dev);
        void (*disable_vblank)(struct device *subdrv_dev);
-       void (*wait_for_vblank)(struct device *subdrv_dev);
+       void (*complete_scanout)(struct device *subdrv_dev,
+                                       dma_addr_t dma_addr,
+                                       unsigned long size);
 };

 /*
diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.c 
b/drivers/gpu/drm/exynos/exynos_drm_encoder.c
index c63721f..7e772ed 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_encoder.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.c
@@ -220,28 +220,27 @@ static void exynos_drm_encoder_commit(struct drm_encoder 
*encoder)
        exynos_encoder->dpms = DRM_MODE_DPMS_ON;
 }

-void exynos_drm_encoder_complete_scanout(struct drm_framebuffer *fb)
+void exynos_drm_encoder_complete_scanout(struct drm_device *drm_dev,
+                                        dma_addr_t dma_addr,
+                                        unsigned long size)
 {
        struct exynos_drm_encoder *exynos_encoder;
        struct exynos_drm_manager_ops *ops;
-       struct drm_device *dev = fb->dev;
        struct drm_encoder *encoder;
+       struct device *dev;

-       /*
-        * make sure that overlay data are updated to real hardware
-        * for all encoders.
-        */
-       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+       /* make sure that current framebuffer is not in use for all encoders */
+       list_for_each_entry(encoder, &drm_dev->mode_config.encoder_list, head) {
                exynos_encoder = to_exynos_encoder(encoder);
                ops = exynos_encoder->manager->ops;
+               dev = exynos_encoder->manager->dev;

                /*
-                * wait for vblank interrupt
-                * - this makes sure that overlay data are updated to
-                *      real hardware.
+                * complete_scanout
+                * - this makes sure that framebuffer is not in use.
                 */
-               if (ops->wait_for_vblank)
-                       ops->wait_for_vblank(exynos_encoder->manager->dev);
+               if (ops->complete_scanout)
+                       ops->complete_scanout(dev, dma_addr, size);
        }
 }

diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.h 
b/drivers/gpu/drm/exynos/exynos_drm_encoder.h
index 89e2fb0..b85211b 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_encoder.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.h
@@ -32,6 +32,8 @@ void exynos_drm_encoder_plane_mode_set(struct drm_encoder 
*encoder, void *data);
 void exynos_drm_encoder_plane_commit(struct drm_encoder *encoder, void *data);
 void exynos_drm_encoder_plane_enable(struct drm_encoder *encoder, void *data);
 void exynos_drm_encoder_plane_disable(struct drm_encoder *encoder, void *data);
-void exynos_drm_encoder_complete_scanout(struct drm_framebuffer *fb);
+void exynos_drm_encoder_complete_scanout(struct drm_device *drm_dev,
+                                        dma_addr_t dma_addr,
+                                        unsigned long size);

 #endif
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c 
b/drivers/gpu/drm/exynos/exynos_drm_fb.c
index 294c051..1ef684a 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fb.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c
@@ -69,11 +69,20 @@ static void exynos_drm_fb_destroy(struct drm_framebuffer 
*fb)
 {
        struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb);
        unsigned int i;
+       dma_addr_t dma_addr;
+       unsigned long size;

        DRM_DEBUG_KMS("%s\n", __FILE__);

-       /* make sure that overlay data are updated before relesing fb. */
-       exynos_drm_encoder_complete_scanout(fb);
+       for (i = 0; i < ARRAY_SIZE(exynos_fb->exynos_gem_obj); i++) {
+               if (exynos_fb->exynos_gem_obj[i] == NULL)
+                       continue;
+
+               dma_addr = exynos_fb->exynos_gem_obj[i]->buffer->dma_addr;
+               size = exynos_fb->exynos_gem_obj[i]->buffer->size;
+
+               exynos_drm_encoder_complete_scanout(fb->dev, dma_addr, size);
+       }

        drm_framebuffer_cleanup(fb);

-- 
1.8.0

Reply via email to