drm/exynos: implement an optimized page_flip
authorMandeep Singh Baines <msb@chromium.org>
Tue, 4 Sep 2012 19:21:07 +0000 (12:21 -0700)
committerGerrit <chrome-bot@google.com>
Tue, 25 Sep 2012 05:34:02 +0000 (22:34 -0700)
We want to avoid all the mode_set logic and just update the
dma_addr when page-flipping. This is necessary for the parallel
page_flip support. We want to avoid a having to call
crtc_update (which can fail) and then call commit. We need
a single call (which we call from the kds callback) which cannot
fail.

BUG=chrome-os-partner:12170
TEST=VT switch. Log in/out. Suspend/resume. Web GL. Youtube. HDMI.

Change-Id: I21afa7dd15bbff184c7ae759bb26e23d855af372
Signed-off-by: Mandeep Singh Baines <msb@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/32787

drivers/gpu/drm/exynos/exynos_drm_crtc.c
drivers/gpu/drm/exynos/exynos_drm_drv.h
drivers/gpu/drm/exynos/exynos_drm_encoder.c
drivers/gpu/drm/exynos/exynos_drm_encoder.h
drivers/gpu/drm/exynos/exynos_drm_fimd.c
drivers/gpu/drm/exynos/exynos_drm_hdmi.c
drivers/gpu/drm/exynos/exynos_drm_hdmi.h
drivers/gpu/drm/exynos/exynos_mixer.c

index 6397372..0b0ee90 100644 (file)
@@ -104,6 +104,30 @@ int exynos_drm_overlay_update(struct exynos_drm_overlay *overlay,
        return 0;
 }
 
+static void exynos_drm_crtc_page_flip_apply(struct drm_crtc *crtc)
+{
+       struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
+       struct exynos_drm_overlay *overlay = &exynos_crtc->overlay;
+       struct drm_framebuffer *fb = crtc->fb;
+       int nr = exynos_drm_format_num_buffers(fb->pixel_format);
+       int i;
+
+       for (i = 0; i < nr; i++) {
+               struct exynos_drm_gem_buf *buffer;
+
+               buffer = exynos_drm_fb_buffer(fb, i);
+               overlay->dma_addr[i] = buffer->dma_addr;
+               overlay->vaddr[i] = buffer->kvaddr;
+
+               DRM_DEBUG_KMS("buffer: %d, vaddr = 0x%lx, dma_addr = 0x%lx\n",
+                               i, (unsigned long)overlay->vaddr[i],
+                               (unsigned long)overlay->dma_addr[i]);
+       }
+
+       exynos_drm_fn_encoder(crtc, overlay,
+                       exynos_drm_encoder_crtc_page_flip);
+}
+
 static int exynos_drm_crtc_update(struct drm_crtc *crtc)
 {
        struct exynos_drm_crtc *exynos_crtc;
@@ -284,7 +308,6 @@ void exynos_drm_kds_callback(void *callback_parameter, void *callback_extra_para
 {
        struct drm_crtc *crtc = (struct drm_crtc *)callback_parameter;
        struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
-       struct drm_device *dev = crtc->dev;
        struct drm_framebuffer *fb = callback_extra_parameter;
        struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb);
 
@@ -297,17 +320,7 @@ void exynos_drm_kds_callback(void *callback_parameter, void *callback_extra_para
                exynos_fb->dma_buf = NULL;
        }
 
-       mutex_lock(&dev->struct_mutex);
-
-       /*
-        * the values related to a buffer of the drm framebuffer
-        * to be applied should be set at here. because these values
-        * first, are set to shadow registers and then to
-        * real registers at vsync front porch period.
-        */
-       exynos_drm_crtc_apply(crtc);
-
-       mutex_unlock(&dev->struct_mutex);
+       exynos_drm_crtc_page_flip_apply(crtc);
 
        BUG_ON(atomic_read(&exynos_crtc->flip_pending));
        atomic_set(&exynos_crtc->flip_pending, 1);
@@ -326,7 +339,6 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
 #ifdef CONFIG_DMA_SHARED_BUFFER_USES_KDS
        struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb);
        struct exynos_drm_gem_obj *gem_ob = (struct exynos_drm_gem_obj *)exynos_fb->exynos_gem_obj[0];
-       struct drm_framebuffer *old_fb;
 #endif
        DRM_DEBUG_KMS("%s\n", __FILE__);
 
@@ -365,14 +377,7 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
 
        mutex_lock(&dev->struct_mutex);
 
-       old_fb = crtc->fb;
        crtc->fb = fb;
-       ret = exynos_drm_crtc_update(crtc);
-       if (ret) {
-               crtc->fb = old_fb;
-               mutex_unlock(&dev->struct_mutex);
-               goto fail_update;
-       }
 
        mutex_unlock(&dev->struct_mutex);
 
@@ -396,8 +401,6 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
        trace_exynos_flip_complete(exynos_crtc->pipe);
        return ret;
 
-fail_update:
-       drm_vblank_put(dev, exynos_crtc->pipe);
 fail_vblank:
 #ifdef CONFIG_DMA_SHARED_BUFFER_USES_KDS
        exynos_crtc->event = NULL;
index a8704a7..8d2709b 100644 (file)
@@ -74,6 +74,8 @@ enum exynos_drm_output_type {
 struct exynos_drm_overlay_ops {
        void (*mode_set)(struct device *subdrv_dev,
                         struct exynos_drm_overlay *overlay);
+       void (*page_flip)(struct device *subdrv_dev,
+                        struct exynos_drm_overlay *overlay);
        void (*commit)(struct device *subdrv_dev, int zpos);
        void (*disable)(struct device *subdrv_dev, int zpos);
 };
index 6e9ac7b..82a20d3 100644 (file)
@@ -443,3 +443,14 @@ void exynos_drm_encoder_crtc_disable(struct drm_encoder *encoder, void *data)
        if (overlay_ops && overlay_ops->disable)
                overlay_ops->disable(manager->dev, zpos);
 }
+
+void exynos_drm_encoder_crtc_page_flip(struct drm_encoder *encoder, void *data)
+{
+       struct exynos_drm_manager *manager =
+               to_exynos_encoder(encoder)->manager;
+       struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
+       struct exynos_drm_overlay *overlay = data;
+
+       if (overlay_ops && overlay_ops->page_flip)
+               overlay_ops->page_flip(manager->dev, overlay);
+}
index eb7d231..f206c20 100644 (file)
@@ -47,6 +47,7 @@ void exynos_drm_encoder_dpms_from_crtc(struct drm_encoder *encoder,
                                        void *data);
 void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data);
 void exynos_drm_encoder_crtc_mode_set(struct drm_encoder *encoder, void *data);
+void exynos_drm_encoder_crtc_page_flip(struct drm_encoder *encoder, void *data);
 void exynos_drm_encoder_crtc_disable(struct drm_encoder *encoder, void *data);
 
 #endif
index 356518a..0ce9a56 100644 (file)
@@ -494,6 +494,54 @@ static void mie_set_6bit_dithering(struct fimd_context *ctx)
        }
 }
 
+static void fimd_win_page_flip(struct device *dev,
+                              struct exynos_drm_overlay *overlay)
+{
+       struct fimd_context *ctx = get_fimd_context(dev);
+       int win = overlay->zpos;
+       struct fimd_win_data *win_data;
+       unsigned long orig_shadow, val, size, offset;
+
+       if (ctx->suspended)
+               return;
+
+       if (win == DEFAULT_ZPOS)
+               win = ctx->default_win;
+
+       if (win < 0 || win > WINDOWS_NR)
+               return;
+
+       win_data = &ctx->win_data[win];
+
+       offset = overlay->fb_x * (overlay->bpp >> 3);
+       offset += overlay->fb_y * overlay->fb_pitch;
+
+       win_data->dma_addr = overlay->dma_addr[0] + offset;
+       win_data->vaddr = overlay->vaddr[0] + offset;
+
+       /* protect windows */
+       val = orig_shadow = readl(ctx->regs + SHADOWCON);
+       val |= SHADOWCON_WINx_PROTECT(win);
+       writel(val, ctx->regs + SHADOWCON);
+
+       /* buffer start address */
+       val = (unsigned long)win_data->dma_addr;
+       writel(val, ctx->regs + VIDWx_BUF_START(win, 0));
+
+       /* buffer end address */
+       size = win_data->fb_height * win_data->fb_pitch;
+       val = (unsigned long)(win_data->dma_addr + size);
+       writel(val, ctx->regs + VIDWx_BUF_END(win, 0));
+
+       DRM_DEBUG_KMS("start addr = 0x%lx, end addr = 0x%lx, size = 0x%lx\n",
+                       (unsigned long)win_data->dma_addr, val, size);
+       DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
+                       win_data->ovl_width, win_data->ovl_height);
+
+       /* Unprotect windows */
+       writel(orig_shadow, ctx->regs + SHADOWCON);
+}
+
 static void fimd_win_commit(struct device *dev, int zpos)
 {
        struct fimd_context *ctx = get_fimd_context(dev);
@@ -647,6 +695,7 @@ static void fimd_win_disable(struct device *dev, int zpos)
 
 static struct exynos_drm_overlay_ops fimd_overlay_ops = {
        .mode_set = fimd_win_mode_set,
+       .page_flip = fimd_win_page_flip,
        .commit = fimd_win_commit,
        .disable = fimd_win_disable,
 };
index c042ede..bd0e3bd 100644 (file)
@@ -259,6 +259,17 @@ static void drm_mixer_mode_set(struct device *subdrv_dev,
                mixer_ops->win_mode_set(ctx->mixer_ctx->ctx, overlay);
 }
 
+static void drm_mixer_page_flip(struct device *subdrv_dev,
+               struct exynos_drm_overlay *overlay)
+{
+       struct drm_hdmi_context *ctx = to_context(subdrv_dev);
+
+       DRM_DEBUG_KMS("%s\n", __FILE__);
+
+       if (mixer_ops && mixer_ops->win_page_flip)
+               mixer_ops->win_page_flip(ctx->mixer_ctx->ctx, overlay);
+}
+
 static void drm_mixer_commit(struct device *subdrv_dev, int zpos)
 {
        struct drm_hdmi_context *ctx = to_context(subdrv_dev);
@@ -297,6 +308,7 @@ static void drm_mixer_disable(struct device *subdrv_dev, int zpos)
 
 static struct exynos_drm_overlay_ops drm_hdmi_overlay_ops = {
        .mode_set = drm_mixer_mode_set,
+       .page_flip = drm_mixer_page_flip,
        .commit = drm_mixer_commit,
        .disable = drm_mixer_disable,
 };
index 0afa654..f0504c0 100644 (file)
@@ -69,6 +69,7 @@ struct exynos_mixer_ops {
 
        /* overlay */
        void (*win_mode_set)(void *ctx, struct exynos_drm_overlay *overlay);
+       void (*win_page_flip)(void *ctx, struct exynos_drm_overlay *overlay);
        void (*win_commit)(void *ctx, int zpos);
        void (*win_disable)(void *ctx, int zpos);
 };
index 34354cb..5e0e3fc 100644 (file)
@@ -736,6 +736,54 @@ static void mixer_win_mode_set(void *ctx,
        win_data->scan_flags = overlay->scan_flag;
 }
 
+static void mixer_win_page_flip(void *ctx,
+                             struct exynos_drm_overlay *overlay)
+{
+       struct mixer_context *mixer_ctx = ctx;
+       struct mixer_resources *res = &mixer_ctx->mixer_res;
+       unsigned long flags;
+       struct hdmi_win_data *win_data;
+       dma_addr_t dma_addr;
+       int win = overlay->zpos;
+
+       if (win == DEFAULT_ZPOS)
+               win = MIXER_DEFAULT_WIN;
+
+       if (win < 0 || win > MIXER_WIN_NR) {
+               DRM_ERROR("overlay plane[%d] is wrong\n", win);
+               return;
+       }
+
+       win_data = &mixer_ctx->win_data[win];
+
+       win_data->dma_addr = overlay->dma_addr[0];
+       win_data->vaddr = overlay->vaddr[0];
+       win_data->chroma_dma_addr = overlay->dma_addr[1];
+       win_data->chroma_vaddr = overlay->vaddr[1];
+
+       /* converting dma address base and source offset */
+       dma_addr = win_data->dma_addr
+               + (win_data->fb_x * win_data->bpp >> 3)
+               + (win_data->fb_y * win_data->fb_width * win_data->bpp >> 3);
+
+       spin_lock_irqsave(&res->reg_slock, flags);
+       mixer_vsync_set_update(mixer_ctx, false);
+
+       /* set buffer address to mixer */
+       mixer_reg_write(res, MXR_GRAPHIC_BASE(win), dma_addr);
+
+       /* Only allow one update per vsync */
+       if (!win_data->updated)
+               mixer_layer_update(mixer_ctx);
+
+       win_data->updated = true;
+
+       mixer_run(mixer_ctx);
+
+       mixer_vsync_set_update(mixer_ctx, true);
+       spin_unlock_irqrestore(&res->reg_slock, flags);
+}
+
 static void mixer_win_commit(void *ctx, int zpos)
 {
        struct mixer_context *mctx = ctx;
@@ -1068,6 +1116,7 @@ static struct exynos_mixer_ops mixer_ops = {
 
        /* overlay */
        .win_mode_set           = mixer_win_mode_set,
+       .win_page_flip          = mixer_win_page_flip,
        .win_commit             = mixer_win_commit,
        .win_disable            = mixer_win_disable,
 };