[PATCH v5 07/19] media: vsp1: dl: Support one-shot entries in the display list
Brian Starkey
Brian.Starkey at arm.com
Fri Feb 22 15:06:19 UTC 2019
On Fri, Feb 22, 2019 at 04:46:29PM +0200, Laurent Pinchart wrote:
> Hi Brian,
>
> On Fri, Feb 22, 2019 at 02:30:03PM +0000, Brian Starkey wrote:
> > On Thu, Feb 21, 2019 at 12:32:00PM +0200, Laurent Pinchart wrote:
> > > One-shot entries are used as an alternative to committing a complete new
> > > display list when a couple of registers need to be written for one frame
> > > and then reset to another value for all subsequent frames. This will be
> > > used to implement writeback support that will need to enable writeback
> > > for the duration of a single frame.
> > >
> > > Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas at ideasonboard.com>
> > > ---
> > > drivers/media/platform/vsp1/vsp1_dl.c | 78 +++++++++++++++++++++++++++
> > > drivers/media/platform/vsp1/vsp1_dl.h | 3 ++
> > > 2 files changed, 81 insertions(+)
> > >
> > > diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c
> > > index 886b3a69d329..7b4d252bfde7 100644
> > > --- a/drivers/media/platform/vsp1/vsp1_dl.c
> > > +++ b/drivers/media/platform/vsp1/vsp1_dl.c
> > > @@ -115,6 +115,12 @@ struct vsp1_dl_body {
> > >
> > > unsigned int num_entries;
> > > unsigned int max_entries;
> > > +
> > > + unsigned int num_patches;
> > > + struct {
> > > + struct vsp1_dl_entry *entry;
> > > + u32 data;
> > > + } patches[2];
> > > };
> > >
> > > /**
> > > @@ -361,6 +367,7 @@ void vsp1_dl_body_put(struct vsp1_dl_body *dlb)
> > > return;
> > >
> > > dlb->num_entries = 0;
> > > + dlb->num_patches = 0;
> > >
> > > spin_lock_irqsave(&dlb->pool->lock, flags);
> > > list_add_tail(&dlb->free, &dlb->pool->free);
> > > @@ -388,6 +395,47 @@ void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data)
> > > dlb->num_entries++;
> > > }
> > >
> > > +/**
> > > + * vsp1_dl_body_write_oneshot - Write a register to a display list body for a
> > > + * single frame
> > > + * @dlb: The body
> > > + * @reg: The register address
> > > + * @value: The register value
> > > + * @reset_value: The value to reset the register to at the next vblank
> > > + *
> > > + * Display lists in continuous mode are re-used by the hardware for successive
> > > + * frames until a new display list is committed. Changing the VSP configuration
> > > + * normally requires creating and committing a new display list. This function
> > > + * offers an alternative race-free way by writing a @value to the @register in
> > > + * the display list body for a single frame, specifying in @reset_value the
> > > + * value to reset the register to one vblank after the display list is
> > > + * committed.
> > > + *
> > > + * The maximum number of one-shot entries is limited to 2 per display list body,
> > > + * and one-shot entries are counted in the total number of entries specified
> > > + * when the body is allocated by vsp1_dl_body_alloc().
> > > + */
> > > +void vsp1_dl_body_write_oneshot(struct vsp1_dl_body *dlb, u32 reg, u32 value,
> > > + u32 reset_value)
> > > +{
> > > + if (WARN_ONCE(dlb->num_entries >= dlb->max_entries,
> > > + "DLB size exceeded (max %u)", dlb->max_entries))
> > > + return;
> > > +
> > > + if (WARN_ONCE(dlb->num_patches >= ARRAY_SIZE(dlb->patches),
> > > + "DLB patches size exceeded (max %zu)",
> > > + ARRAY_SIZE(dlb->patches)))
> > > + return;
> > > +
> > > + dlb->patches[dlb->num_patches].entry = &dlb->entries[dlb->num_entries];
> > > + dlb->patches[dlb->num_patches].data = reset_value;
> > > + dlb->num_patches++;
> > > +
> > > + dlb->entries[dlb->num_entries].addr = reg;
> > > + dlb->entries[dlb->num_entries].data = value;
> > > + dlb->num_entries++;
> > > +}
> > > +
> > > /* -----------------------------------------------------------------------------
> > > * Display List Extended Command Management
> > > */
> > > @@ -652,6 +700,7 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl)
> > > * has at least one body, thus we reinitialise the entries list.
> > > */
> > > dl->body0->num_entries = 0;
> > > + dl->body0->num_patches = 0;
> > >
> > > list_add_tail(&dl->list, &dl->dlm->free);
> > > }
> > > @@ -930,6 +979,35 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, unsigned int dl_flags)
> > > * Display List Manager
> > > */
> > >
> > > +/**
> > > + * vsp1_dlm_irq_display_start - Display list handler for the display start
> > > + * interrupt
> > > + * @dlm: the display list manager
> > > + *
> > > + * Apply all one-shot patches registered for the active display list.
> > > + */
> > > +void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm)
> > > +{
> > > + struct vsp1_dl_body *dlb;
> > > + struct vsp1_dl_list *dl;
> > > + unsigned int i;
> > > +
> > > + spin_lock(&dlm->lock);
> > > +
> > > + dl = dlm->active;
> > > + if (!dl)
> > > + goto done;
> > > +
> > > + list_for_each_entry(dlb, &dl->bodies, list) {
> > > + for (i = 0; i < dlb->num_patches; ++i)
> > > + dlb->patches[i].entry->data = dlb->patches[i].data;
> > > + dlb->num_patches = 0;
> > > + }
> > > +
> > > +done:
> > > + spin_unlock(&dlm->lock);
> > > +}
> > > +
> >
> > We've got some HW which doesn't support one-shot writeback, and use a
> > similar trick to try and disable writeback immediately after the flip.
> >
> > We ran into issues where the "start" interrupt wouldn't run in time to
> > make sure the writeback disable was committed before the next frame.
> > We have to keep track of whether the disable really happened in time,
> > before we release the output buffer.
> >
> > Might you have a similar problem here?
>
> We may, but there's no provision at the hardware level to check if the
> configuration updated happened in time. I could add some safety checks
> but I believe they would be racy in the best case :-(
We managed to find (what I believe to be...) a non-racy way, but it
will of course depend a lot on the HW behaviour, so I'll leave it to
your best judgement.
We basically have a "configuration committed" interrupt which we can
use to set a flag indicating writeback was disabled.
>
> Note that we have the duration of a complete frame to disable writeback,
> as we receive an interrupt when the frame starts, and have until vblank
> to update the configuration. It's thus slightly better than having to
> disable writeback between vblank and the start of the next frame.
Yeah... we have a whole frame too. I'm struggling to find our wiki
page with the data, but anecdotally there's some (out-of-tree) drivers
which keep interrupts masked for a _really long_ time. It's nice if
you don't have to care about those :-)
-Brian
>
> > > /**
> > > * vsp1_dlm_irq_frame_end - Display list handler for the frame end interrupt
> > > * @dlm: the display list manager
> > > diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h
> > > index e0fdb145e6ed..f845607abc4c 100644
> > > --- a/drivers/media/platform/vsp1/vsp1_dl.h
> > > +++ b/drivers/media/platform/vsp1/vsp1_dl.h
> > > @@ -54,6 +54,7 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
> > > unsigned int prealloc);
> > > void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm);
> > > void vsp1_dlm_reset(struct vsp1_dl_manager *dlm);
> > > +void vsp1_dlm_irq_display_start(struct vsp1_dl_manager *dlm);
> > > unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm);
> > > struct vsp1_dl_body *vsp1_dlm_dl_body_get(struct vsp1_dl_manager *dlm);
> > >
> > > @@ -71,6 +72,8 @@ struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool);
> > > void vsp1_dl_body_put(struct vsp1_dl_body *dlb);
> > >
> > > void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data);
> > > +void vsp1_dl_body_write_oneshot(struct vsp1_dl_body *dlb, u32 reg, u32 value,
> > > + u32 reset_value);
> > > int vsp1_dl_list_add_body(struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb);
> > > int vsp1_dl_list_add_chain(struct vsp1_dl_list *head, struct vsp1_dl_list *dl);
> > >
>
> --
> Regards,
>
> Laurent Pinchart
More information about the dri-devel
mailing list