Merge branch 'drm-armada-devel-4.15' of git://git.armlinux.org.uk/~rmk/linux-arm...
authorDave Airlie <airlied@redhat.com>
Thu, 4 Jan 2018 23:43:46 +0000 (09:43 +1000)
committerDave Airlie <airlied@redhat.com>
Thu, 4 Jan 2018 23:51:43 +0000 (09:51 +1000)
This series builds upon the set of fixes previously submitted to move
Armada DRM closer to atomic modeset.  We're nowhere near yet, but this
series helps to get us closer by unifying some of the differences
between the primary and overlay planes.

New features added allows userspace to disable the primary plane if
overlay is full screen and there's nothing obscuring the colorkey -
this saves having to fetch an entire buffer containing nothing but
colorkey when displaying full screen video.

[airlied: fixup for atomic plane helper rename:
a01cb8ba3f6282934cff65e89ab36b18b14cbe27
Author: Ville Syrjälä <ville.syrjala@linux.intel.com>
Date:   Wed Nov 1 22:16:19 2017 +0200

    drm: Move drm_plane_helper_check_state() into drm_atomic_helper.c
]

* 'drm-armada-devel-4.15' of git://git.armlinux.org.uk/~rmk/linux-arm: (29 commits)
  drm/armada: expand overlay trace entry
  drm/armada: implement primary plane update
  drm/armada: extract register generation from armada_drm_primary_set()
  drm/armada: wait for previous work when moving overlay window
  drm/armada: move overlay plane register update generation
  drm/armada: re-organise overlay register update generation
  drm/armada: disable planes at next blanking period
  drm/armada: avoid work allocation
  drm/armada: allow armada_drm_plane_work_queue() to silently fail
  drm/armada: use drm_plane_helper_check_state()
  drm/armada: only enable HSMOOTH if scaling horizontally
  drm/armada: move writes of LCD_SPU_SRAM_PARA1 under lock
  drm/armada: move regs into armada_plane_work
  drm/armada: move event sending into armada_plane_work
  drm/armada: move fb retirement into armada_plane_work
  drm/armada: move overlay plane work out from under spinlock
  drm/armada: clear plane enable bit when disabling
  drm/armada: clean up armada_drm_crtc_plane_disable()
  drm/armada: allow the primary plane to be disabled
  drm/armada: wait and cancel any pending frame work at disable
  ...

1  2 
drivers/gpu/drm/armada/armada_crtc.c
drivers/gpu/drm/armada/armada_overlay.c

@@@ -13,6 -13,6 +13,7 @@@
  #include <drm/drmP.h>
  #include <drm/drm_crtc_helper.h>
  #include <drm/drm_plane_helper.h>
++#include <drm/drm_atomic_helper.h>
  #include "armada_crtc.h"
  #include "armada_drm.h"
  #include "armada_fb.h"
@@@ -1127,9 -1138,185 +1139,185 @@@ static const struct drm_crtc_funcs arma
        .disable_vblank = armada_drm_crtc_disable_vblank,
  };
  
 -      ret = drm_plane_helper_check_state(&state, &clip, 0, INT_MAX, true,
 -                                          false);
+ static void armada_drm_primary_update_state(struct drm_plane_state *state,
+       struct armada_regs *regs)
+ {
+       struct armada_plane *dplane = drm_to_armada_plane(state->plane);
+       struct armada_crtc *dcrtc = drm_to_armada_crtc(state->crtc);
+       struct armada_framebuffer *dfb = drm_fb_to_armada_fb(state->fb);
+       bool was_disabled;
+       unsigned int idx = 0;
+       u32 val;
+       val = CFG_GRA_FMT(dfb->fmt) | CFG_GRA_MOD(dfb->mod);
+       if (dfb->fmt > CFG_420)
+               val |= CFG_PALETTE_ENA;
+       if (state->visible)
+               val |= CFG_GRA_ENA;
+       if (drm_rect_width(&state->src) >> 16 != drm_rect_width(&state->dst))
+               val |= CFG_GRA_HSMOOTH;
+       was_disabled = !(dplane->state.ctrl0 & CFG_GRA_ENA);
+       if (was_disabled)
+               armada_reg_queue_mod(regs, idx,
+                                    0, CFG_PDWN64x66, LCD_SPU_SRAM_PARA1);
+       dplane->state.ctrl0 = val;
+       dplane->state.src_hw = (drm_rect_height(&state->src) & 0xffff0000) |
+                               drm_rect_width(&state->src) >> 16;
+       dplane->state.dst_hw = drm_rect_height(&state->dst) << 16 |
+                              drm_rect_width(&state->dst);
+       dplane->state.dst_yx = state->dst.y1 << 16 | state->dst.x1;
+       armada_drm_gra_plane_regs(regs + idx, &dfb->fb, &dplane->state,
+                                 state->src.x1 >> 16, state->src.y1 >> 16,
+                                 dcrtc->interlaced);
+       dplane->state.vsync_update = !was_disabled;
+       dplane->state.changed = true;
+ }
+ static int armada_drm_primary_update(struct drm_plane *plane,
+       struct drm_crtc *crtc, struct drm_framebuffer *fb,
+       int crtc_x, int crtc_y, unsigned int crtc_w, unsigned int crtc_h,
+       uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h,
+       struct drm_modeset_acquire_ctx *ctx)
+ {
+       struct armada_plane *dplane = drm_to_armada_plane(plane);
+       struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+       struct armada_plane_work *work;
+       struct drm_plane_state state = {
+               .plane = plane,
+               .crtc = crtc,
+               .fb = fb,
+               .src_x = src_x,
+               .src_y = src_y,
+               .src_w = src_w,
+               .src_h = src_h,
+               .crtc_x = crtc_x,
+               .crtc_y = crtc_y,
+               .crtc_w = crtc_w,
+               .crtc_h = crtc_h,
+               .rotation = DRM_MODE_ROTATE_0,
+       };
+       const struct drm_rect clip = {
+               .x2 = crtc->mode.hdisplay,
+               .y2 = crtc->mode.vdisplay,
+       };
+       int ret;
++      ret = drm_atomic_helper_check_plane_state(&state, crtc->state, &clip, 0,
++                                                INT_MAX, true, false);
+       if (ret)
+               return ret;
+       work = &dplane->works[dplane->next_work];
+       work->fn = armada_drm_crtc_complete_frame_work;
+       if (plane->fb != fb) {
+               /*
+                * Take a reference on the new framebuffer - we want to
+                * hold on to it while the hardware is displaying it.
+                */
+               drm_framebuffer_reference(fb);
+               work->old_fb = plane->fb;
+       } else {
+               work->old_fb = NULL;
+       }
+       armada_drm_primary_update_state(&state, work->regs);
+       if (!dplane->state.changed)
+               return 0;
+       /* Wait for pending work to complete */
+       if (armada_drm_plane_work_wait(dplane, HZ / 10) == 0)
+               armada_drm_plane_work_cancel(dcrtc, dplane);
+       if (!dplane->state.vsync_update) {
+               work->fn(dcrtc, work);
+               if (work->old_fb)
+                       drm_framebuffer_unreference(work->old_fb);
+               return 0;
+       }
+       /* Queue it for update on the next interrupt if we are enabled */
+       ret = armada_drm_plane_work_queue(dcrtc, work);
+       if (ret) {
+               work->fn(dcrtc, work);
+               if (work->old_fb)
+                       drm_framebuffer_unreference(work->old_fb);
+       }
+       dplane->next_work = !dplane->next_work;
+       return 0;
+ }
+ int armada_drm_plane_disable(struct drm_plane *plane,
+                            struct drm_modeset_acquire_ctx *ctx)
+ {
+       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;
+       /*
+        * 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->type == DRM_PLANE_TYPE_PRIMARY) {
+               sram_para1 = CFG_PDWN256x32 | CFG_PDWN256x24 | CFG_PDWN256x8 |
+                            CFG_PDWN32x32 | CFG_PDWN64x66;
+               enable_mask = CFG_GRA_ENA;
+       } else {
+               sram_para1 = CFG_PDWN16x66 | CFG_PDWN32x66;
+               enable_mask = CFG_DMA_ENA;
+       }
+       dplane->state.ctrl0 &= ~enable_mask;
+       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);
+       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;
+ }
  static const struct drm_plane_funcs armada_primary_plane_funcs = {
-       .update_plane   = drm_primary_helper_update,
-       .disable_plane  = drm_primary_helper_disable,
+       .update_plane   = armada_drm_primary_update,
+       .disable_plane  = armada_drm_plane_disable,
        .destroy        = drm_primary_helper_destroy,
  };
  
@@@ -7,7 -7,7 +7,7 @@@
   * published by the Free Software Foundation.
   */
  #include <drm/drmP.h>
--#include <drm/drm_plane_helper.h>
++#include <drm/drm_atomic_helper.h>
  #include "armada_crtc.h"
  #include "armada_drm.h"
  #include "armada_fb.h"
@@@ -249,36 -173,93 +173,93 @@@ static void armada_ovl_plane_update_sta
                        CFG_DMA_MOD(CFG_SWAPRB | CFG_SWAPUV | CFG_SWAPYU |
                        CFG_YUV2RGB) | CFG_DMA_ENA,
                        LCD_SPU_DMA_CTRL0);
+               dplane->base.state.vsync_update = true;
        }
-       if (idx) {
-               armada_reg_queue_end(dplane->vbl.regs, idx);
-               armada_drm_plane_work_queue(dcrtc, &dplane->base,
-                                           &dplane->vbl.work);
-       }
-       return 0;
+       dplane->base.state.changed = idx != 0;
+       armada_reg_queue_end(regs, idx);
  }
  
- static int armada_ovl_plane_disable(struct drm_plane *plane,
-                                   struct drm_modeset_acquire_ctx *ctx)
+ static int
+ armada_ovl_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
+       struct drm_framebuffer *fb,
+       int crtc_x, int crtc_y, unsigned crtc_w, unsigned crtc_h,
+       uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h,
+       struct drm_modeset_acquire_ctx *ctx)
  {
        struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane);
-       struct drm_framebuffer *fb;
-       struct armada_crtc *dcrtc;
+       struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+       struct armada_plane_work *work;
+       struct drm_plane_state state = {
+               .plane = plane,
+               .crtc = crtc,
+               .fb = fb,
+               .src_x = src_x,
+               .src_y = src_y,
+               .src_w = src_w,
+               .src_h = src_h,
+               .crtc_x = crtc_x,
+               .crtc_y = crtc_y,
+               .crtc_w = crtc_w,
+               .crtc_h = crtc_h,
+               .rotation = DRM_MODE_ROTATE_0,
+       };
+       const struct drm_rect clip = {
+               .x2 = crtc->mode.hdisplay,
+               .y2 = crtc->mode.vdisplay,
+       };
+       int ret;
  
-       if (!dplane->base.base.crtc)
+       trace_armada_ovl_plane_update(plane, crtc, fb,
+                                crtc_x, crtc_y, crtc_w, crtc_h,
+                                src_x, src_y, src_w, src_h);
 -      ret = drm_plane_helper_check_state(&state, &clip, 0, INT_MAX, true,
 -                                          false);
++      ret = drm_atomic_helper_check_plane_state(&state, crtc->state, &clip, 0,
++                                                INT_MAX, true, false);
+       if (ret)
+               return ret;
+       work = &dplane->base.works[dplane->base.next_work];
+       if (plane->fb != fb) {
+               /*
+                * Take a reference on the new framebuffer - we want to
+                * hold on to it while the hardware is displaying it.
+                */
+               drm_framebuffer_reference(fb);
+               work->old_fb = plane->fb;
+       } else {
+               work->old_fb = NULL;
+       }
+       armada_ovl_plane_update_state(&state, work->regs);
+       if (!dplane->base.state.changed)
                return 0;
  
-       dcrtc = drm_to_armada_crtc(dplane->base.base.crtc);
+       /* Wait for pending work to complete */
+       if (armada_drm_plane_work_wait(&dplane->base, HZ / 25) == 0)
+               armada_drm_plane_work_cancel(dcrtc, &dplane->base);
+       /* Just updating the position/size? */
+       if (!dplane->base.state.vsync_update) {
+               armada_ovl_plane_work(dcrtc, work);
+               return 0;
+       }
  
-       armada_drm_plane_work_cancel(dcrtc, &dplane->base);
-       armada_drm_crtc_plane_disable(dcrtc, plane);
+       if (!dcrtc->plane) {
+               dcrtc->plane = plane;
+               armada_ovl_update_attr(&dplane->prop, dcrtc);
+       }
  
-       dcrtc->plane = NULL;
-       dplane->base.state.ctrl0 = 0;
+       /* Queue it for update on the next interrupt if we are enabled */
+       ret = armada_drm_plane_work_queue(dcrtc, work);
+       if (ret)
+               DRM_ERROR("failed to queue plane work: %d\n", ret);
  
-       fb = xchg(&dplane->old_fb, NULL);
-       if (fb)
-               drm_framebuffer_put(fb);
+       dplane->base.next_work = !dplane->base.next_work;
  
        return 0;
  }