From 890ca8df5a75b3bfdab86bec03aa60cff90a573e Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 8 Jul 2017 10:22:27 +0100 Subject: drm/armada: disable planes at next blanking period Disable planes at the next blanking period rather than immediately. In order to achieve this, we need to delay the clearing of dcrtc->plane until after the next blanking period, so move that into a separate work function. To avoid races, we also need to move its assignment in the overlay code. Signed-off-by: Russell King --- drivers/gpu/drm/armada/armada_crtc.c | 59 ++++++++++++++++++++++++--------- drivers/gpu/drm/armada/armada_overlay.c | 23 ++++--------- 2 files changed, 50 insertions(+), 32 deletions(-) diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c index 98fb955f6889..c38a1409a14e 100644 --- a/drivers/gpu/drm/armada/armada_crtc.c +++ b/drivers/gpu/drm/armada/armada_crtc.c @@ -293,6 +293,19 @@ static void armada_drm_crtc_complete_frame_work(struct armada_crtc *dcrtc, spin_unlock_irqrestore(&dcrtc->irq_lock, flags); } +static void armada_drm_crtc_complete_disable_work(struct armada_crtc *dcrtc, + struct armada_plane_work *work) +{ + unsigned long flags; + + if (dcrtc->plane == work->plane) + dcrtc->plane = NULL; + + spin_lock_irqsave(&dcrtc->irq_lock, flags); + armada_drm_crtc_update_regs(dcrtc, work->regs); + spin_unlock_irqrestore(&dcrtc->irq_lock, flags); +} + static struct armada_plane_work * armada_drm_crtc_alloc_plane_work(struct drm_plane *plane) { @@ -392,8 +405,11 @@ static void armada_drm_crtc_prepare(struct drm_crtc *crtc) * the new mode parameters. */ plane = dcrtc->plane; - if (plane) + if (plane) { drm_plane_force_disable(plane); + WARN_ON(!armada_drm_plane_work_wait(drm_to_armada_plane(plane), + HZ)); + } } /* The mode_config.mutex will be held for this call */ @@ -1120,28 +1136,22 @@ int armada_drm_plane_disable(struct drm_plane *plane, { struct armada_plane *dplane = drm_to_armada_plane(plane); struct armada_crtc *dcrtc; + struct armada_plane_work *work; + unsigned int idx = 0; u32 sram_para1, enable_mask; if (!plane->crtc) return 0; /* - * Drop our reference on any framebuffer attached to this plane. - * We don't need to NULL this out as drm_plane_force_disable(), - * and __setplane_internal() will do so for an overlay plane, and - * __drm_helper_disable_unused_functions() will do so for the - * primary plane. + * Arrange to power down most RAMs and FIFOs if this is the primary + * plane, otherwise just the YUV FIFOs for the overlay plane. */ - if (plane->fb) - drm_framebuffer_put(plane->fb); - - /* Power down most RAMs and FIFOs if this is the primary plane */ if (plane->type == DRM_PLANE_TYPE_PRIMARY) { sram_para1 = CFG_PDWN256x32 | CFG_PDWN256x24 | CFG_PDWN256x8 | CFG_PDWN32x32 | CFG_PDWN64x66; enable_mask = CFG_GRA_ENA; } else { - /* Power down the Y/U/V FIFOs */ sram_para1 = CFG_PDWN16x66 | CFG_PDWN32x66; enable_mask = CFG_DMA_ENA; } @@ -1150,14 +1160,33 @@ int armada_drm_plane_disable(struct drm_plane *plane, dcrtc = drm_to_armada_crtc(plane->crtc); + /* + * Try to disable the plane and drop our ref on the framebuffer + * at the next frame update. If we fail for any reason, disable + * the plane immediately. + */ + work = &dplane->works[dplane->next_work]; + work->fn = armada_drm_crtc_complete_disable_work; + work->cancel = armada_drm_crtc_complete_disable_work; + work->old_fb = plane->fb; + + armada_reg_queue_mod(work->regs, idx, + 0, enable_mask, LCD_SPU_DMA_CTRL0); + armada_reg_queue_mod(work->regs, idx, + sram_para1, 0, LCD_SPU_SRAM_PARA1); + armada_reg_queue_end(work->regs, idx); + /* Wait for any preceding work to complete, but don't wedge */ if (WARN_ON(!armada_drm_plane_work_wait(dplane, HZ))) armada_drm_plane_work_cancel(dcrtc, dplane); - spin_lock_irq(&dcrtc->irq_lock); - armada_updatel(0, enable_mask, dcrtc->base + LCD_SPU_DMA_CTRL0); - armada_updatel(sram_para1, 0, dcrtc->base + LCD_SPU_SRAM_PARA1); - spin_unlock_irq(&dcrtc->irq_lock); + if (armada_drm_plane_work_queue(dcrtc, work)) { + work->fn(dcrtc, work); + if (work->old_fb) + drm_framebuffer_unreference(work->old_fb); + } + + dplane->next_work = !dplane->next_work; return 0; } diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c index bad966ae6758..0fe3f2db8ff5 100644 --- a/drivers/gpu/drm/armada/armada_overlay.c +++ b/drivers/gpu/drm/armada/armada_overlay.c @@ -140,11 +140,6 @@ armada_ovl_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, dplane->base.state.src_x != state.src.x1 >> 16 || dplane->base.state.src_y != state.src.y1 >> 16; - if (!dcrtc->plane) { - dcrtc->plane = plane; - armada_ovl_update_attr(&dplane->prop, dcrtc); - } - /* FIXME: overlay on an interlaced display */ /* Just updating the position/size? */ if (!fb_changed && dplane->base.state.ctrl0 == ctrl0) { @@ -173,6 +168,11 @@ armada_ovl_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, if (armada_drm_plane_work_wait(&dplane->base, HZ / 25) == 0) armada_drm_plane_work_cancel(dcrtc, &dplane->base); + if (!dcrtc->plane) { + dcrtc->plane = plane; + armada_ovl_update_attr(&dplane->prop, dcrtc); + } + if (fb_changed) { u32 addrs[3]; @@ -255,17 +255,6 @@ armada_ovl_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, return 0; } -static int armada_ovl_plane_disable(struct drm_plane *plane, - struct drm_modeset_acquire_ctx *ctx) -{ - armada_drm_plane_disable(plane, ctx); - - if (plane->crtc) - drm_to_armada_crtc(plane->crtc)->plane = NULL; - - return 0; -} - static void armada_ovl_plane_destroy(struct drm_plane *plane) { struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane); @@ -345,7 +334,7 @@ static int armada_ovl_plane_set_property(struct drm_plane *plane, static const struct drm_plane_funcs armada_ovl_plane_funcs = { .update_plane = armada_ovl_plane_update, - .disable_plane = armada_ovl_plane_disable, + .disable_plane = armada_drm_plane_disable, .destroy = armada_ovl_plane_destroy, .set_property = armada_ovl_plane_set_property, }; -- cgit