[Freedreno] [PATCH 00/23] drm: Eliminate plane->fb/crtc usage for atomic drivers

Noralf Trønnes noralf at tronnes.org
Thu Mar 22 23:28:34 UTC 2018


Den 22.03.2018 19.49, skrev Ville Syrjälä:
> On Thu, Mar 22, 2018 at 05:51:35PM +0100, Noralf Trønnes wrote:
>> tinydrm is also using plane->fb:
>>
>> $ grep -r "plane\.fb" drivers/gpu/drm/tinydrm/
>> drivers/gpu/drm/tinydrm/repaper.c:      if (tdev->pipe.plane.fb != fb)
>> drivers/gpu/drm/tinydrm/mipi-dbi.c:     if (tdev->pipe.plane.fb != fb)
>> drivers/gpu/drm/tinydrm/mipi-dbi.c:     struct drm_framebuffer *fb =
>> mipi->tinydrm.pipe.plane.fb;
> Oh dear, and naturally it's the most annoying one of the bunch. So we
> want to take the plane lock in the fb.dirty() hook to look at the fb,
> but mipi-dbi.c calls that directly from the bowels of its
> .atomic_enable() hook. So that would deadlock pretty neatly if the
> commit happens while already holding the lock.
>
> So we'd either need to plumb an acquire context into fb.dirty(),
> or maybe have tinydrm provide a lower level lockless dirty() hook
> that gets called by both (there are just too many layers in this
> particular cake to immediately see where such a hook would be best
> placed). Or maybe there's some other solution I didn't think of.
>
> Looking at this I'm also left wondering how this is supposed
> handle fb.dirty() getting called concurrently with a modeset.
> The dirty_mutex only seems to offer any protection for
> fb.dirty() against another fb.dirty()...
>

I have been waiting for the 'page-flip with damage'[1] work to come to
fruition so I could handle all flushing during atomic commit.
The idea is to use the same damage rect for a generic dirtyfb callback.

Maybe a temporary tinydrm specific solution is possible until that work
lands, but I don't know enough about how to implement such a dirtyfb
callback.

I imagine it could look something like this:

  struct tinydrm_device {
+    struct drm_clip_rect dirtyfb_rect;
  };

static int tinydrm_fb_dirty(struct drm_framebuffer *fb,
                 struct drm_file *file_priv,
                 unsigned int flags, unsigned int color,
                 struct drm_clip_rect *clips,
                 unsigned int num_clips)
{
     struct tinydrm_device *tdev = fb->dev->dev_private;
     struct drm_framebuffer *planefb;

     /* Grab whatever lock needed to check this */
     planefb = tdev->pipe.plane.state->fb;

     /* fbdev can flush even when we're not interested */
     if (fb != planefb)
         return 0;

     /* Protect dirtyfb_rect with a lock */
     tinydrm_merge_clips(&tdev->dirty_rectfb, clips, num_clips, flags,
                 fb->width, fb->height);

     /*
      * Somehow do an atomic commit that results in the atomic update
      * callback being called
      */
     ret = drm_atomic_commit(state);
...
}

static void mipi_dbi_flush(struct drm_framebuffer *fb,
                struct drm_clip_rect *clip)
{
     /* Flush out framebuffer */
}

void mipi_dbi_pipe_update(struct drm_simple_display_pipe *pipe,
               struct drm_plane_state *old_state)
{
     struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
     struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
     struct drm_framebuffer *fb = pipe->plane.state->fb;
     struct drm_crtc *crtc = &tdev->pipe.crtc;

     if (!fb || (fb == old_state->fb && !tdev->dirty_rect.x2))
         goto out_vblank;

     /* Don't flush if the controller isn't initialized yet */
     if (!mipi->enabled)
         goto out_vblank;

     if (fb != old_state->fb) { /* Page flip or new, flush all */
         mipi_dbi_flush(fb, NULL);
     } else { /* dirtyfb ioctl */
         mipi_dbi_flush(fb, &tdev->dirtyfb_rect);
         memset(&tdev->dirtyfb_rect, 0, sizeof(tdev->dirtyfb_rect));
     }

out_vblank:
     if (crtc->state->event) {
         spin_lock_irq(&crtc->dev->event_lock);
         drm_crtc_send_vblank_event(crtc, crtc->state->event);
         spin_unlock_irq(&crtc->dev->event_lock);
         crtc->state->event = NULL;
     }
}

This is called from the driver pipe enable callback after the controller
has been initialized:

  void mipi_dbi_pipe_enable(struct drm_simple_display_pipe *pipe,
                struct drm_crtc_state *crtc_state)
  {
      struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
      struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
-     struct drm_framebuffer *fb = pipe->plane.fb;
+    struct drm_framebuffer *fb = pipe->plane.state->fb;

      DRM_DEBUG_KMS("\n");

      mipi->enabled = true;
-     if (fb)
-         fb->funcs->dirty(fb, NULL, 0, 0, NULL, 0);
+    mipi_dbi_flush(fb, NULL);
      tinydrm_enable_backlight(mipi->backlight);
  }

I can make patches if this makes any sense and you can help me with the
dirtyfb implementation.

Noralf.

[1] 
https://lists.freedesktop.org/archives/dri-devel/2017-December/161003.html



More information about the Freedreno mailing list