[PATCH v4 7/7] media: vsp1: Provide a writeback video device
Kieran Bingham
kieran.bingham at ideasonboard.com
Sun Feb 17 20:09:39 UTC 2019
Hi Laurent,
On 17/02/2019 20:06, Kieran Bingham wrote:
> Hi Laurent,
>
> Thank you for updating the patch,
>
> On 17/02/2019 02:48, Laurent Pinchart wrote:
>> From: Kieran Bingham <kieran.bingham+renesas at ideasonboard.com>
>>
>> When the VSP1 is used in an active display pipeline, the output of the
>> WPF can supply the LIF entity directly and simultaneously write to
>> memory.
>>
>> Support this functionality in the VSP1 driver through a V4L2 video
>> device node connected to the WPF.
>>
>> The writeback video node support RGB formats only due to the WPF
>> outputting RGB to the LIF. The pixel format can otherwise be configured
>> freely.
>>
>> The resolution of the captured frames are set by the display mode. A
>> different resolution can be selected on the video node, in which case
>> cropping or composing will be performed.
>>
>> Signed-off-by: Kieran Bingham <kieran.bingham+renesas at ideasonboard.com>
>> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas at ideasonboard.com>
>
> For your changes,
>
> With the documentation updated for vsp1_dl_body_write_patch(),
>
> Reviewed-by: Kieran Bingham <kieran.bingham+renesas at ideasonboard.com>
>
>> ---
>> Changes since v3:
>>
>> - Infer has_writeback from the number of LIFs and the generation
>> - Remove vsp1_video::is_writeback
>> - Describe writeback video nodes as 'writeback'
>> - Add mechanism to patch active display lists
>> - Handle writeback format restrictions
>> - Don't wait for next page flip before completing writeback buffer
>
> This is a nice addition.
>
>> - Periods at the end of sentences.
>
> You also fix writeback for the bitrot that the previous sets had now
> that we have partition handling.
>
> Interestingly my local implementation for this bitrot was just to
> allocate a table for a single partition. I like your implementation
> better :)
>
>
>>
>> Changes since v2:
>> - Rebased to v4.12-rc1
>>
>> Changes since RFC
>> - Fix checkpatch.pl warnings
>> - Adapt to use a single source pad for the Writeback and LIF
>> - Use WPF properties to determine when to create links instead of VSP
>> - Remove incorrect vsp1_video_verify_format() changes
>> - Spelling and grammar fixes
>> ---
>> drivers/media/platform/vsp1/vsp1_dl.c | 83 +++++++++++++
>> drivers/media/platform/vsp1/vsp1_dl.h | 4 +
>> drivers/media/platform/vsp1/vsp1_drm.c | 14 ++-
>> drivers/media/platform/vsp1/vsp1_drv.c | 17 ++-
>> drivers/media/platform/vsp1/vsp1_pipe.c | 5 +
>> drivers/media/platform/vsp1/vsp1_pipe.h | 6 +
>> drivers/media/platform/vsp1/vsp1_rwpf.h | 2 +
>> drivers/media/platform/vsp1/vsp1_video.c | 150 +++++++++++++++++++++--
>> drivers/media/platform/vsp1/vsp1_video.h | 6 +
>> drivers/media/platform/vsp1/vsp1_wpf.c | 49 ++++++--
>> 10 files changed, 318 insertions(+), 18 deletions(-)
>>
>> diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c
>> index 886b3a69d329..591544382946 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];
>
> What's the significance of [2] ?
> Perhaps it will be clear what two entry's support patching later...
>
>
> Ok - yes - it's to patch both the Writeback enable and the display start
> enable.
>
>
>> };
>>
>> /**
>> @@ -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,37 @@ void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data)
>> dlb->num_entries++;
>> }
>>
>> +/**
>> + * vsp1_dl_body_write - Write a register to a display list body
>
> s/_write/_write_patch/
>
> "Store an update to an existing register for handling at display-start
> interrupt...."? (or as you see fit)
>
>
>> + * @dlb: The body
>> + * @reg: The register address
>> + * @data: The register value
>> + *
>
> + patch: The updated value to modify...
>
>> + * Write the given register and value to the display list body. The maximum
>> + * number of entries that can be written in a body is specified when the body is
>> + * allocated by vsp1_dl_body_alloc().
>
> I guess this is a copy/paste hangover.
>
> How about:
>
> "Display lists in continuous mode are re-used by the hardware for
> successive frames without needed to recommit a new display list. A patch
> allows us to apply small changes to the display list before it is reused
> to allow minor configuration changes without involving a full rewrite of
> the list or facing a race at commit."
>
>
> (Or however you see fit...)
>
>
>> + */
>> +void vsp1_dl_body_write_patch(struct vsp1_dl_body *dlb, u32 reg, u32 data,
>> + u32 patch)
>> +{
>> + 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 %lu)",
And I'm sure you can handle the %lu here as highlighted by the kbuild-robot.
>> + ARRAY_SIZE(dlb->patches)))
>> + return;
>> +
>> + dlb->patches[dlb->num_patches].entry = &dlb->entries[dlb->num_entries];
>> + dlb->patches[dlb->num_patches].data = patch;
>> + dlb->num_patches++;
>> +
>> + dlb->entries[dlb->num_entries].addr = reg;
>> + dlb->entries[dlb->num_entries].data = data;
>> + dlb->num_entries++;
>> +}
>> +
>> /* -----------------------------------------------------------------------------
>> * Display List Extended Command Management
>> */
>> @@ -652,6 +690,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 +969,36 @@ 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 patches registered for the active display list. This is used to
>> + * disable writeback for the next frame.
>> + */
>> +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);
>> +}
>> +
>> /**
>> * vsp1_dlm_irq_frame_end - Display list handler for the frame end interrupt
>> * @dlm: the display list manager
>> @@ -947,6 +1016,9 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, unsigned int dl_flags)
>> *
>> * The VSP1_DL_FRAME_END_INTERNAL flag indicates that the display list that just
>> * became active had been queued with the internal notification flag.
>> + *
>> + * The VSP1_DL_FRAME_END_WRITEBACK flag indicates that the previously active
>> + * display list had been queued with the writeback flag.
>> */
>> unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)
>> {
>> @@ -984,6 +1056,17 @@ unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)
>> if (status & VI6_STATUS_FLD_STD(dlm->index))
>> goto done;
>>
>> + /*
>> + * If the active display list has the writeback flag set, the frame
>> + * completion marks the end of the writeback capture. Return the
>> + * VSP1_DL_FRAME_END_WRITEBACK flag and reset the display list's
>> + * writeback flag.
>> + */
>> + if (dlm->active && (dlm->active->flags & VSP1_DL_FRAME_END_WRITEBACK)) {
>> + flags |= VSP1_DL_FRAME_END_WRITEBACK;
>> + dlm->active->flags &= ~VSP1_DL_FRAME_END_WRITEBACK;
>> + }
>> +
>> /*
>> * The device starts processing the queued display list right after the
>> * frame end interrupt. The display list thus becomes active.
>> diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h
>> index e0fdb145e6ed..cbaa0bf0cbc2 100644
>> --- a/drivers/media/platform/vsp1/vsp1_dl.h
>> +++ b/drivers/media/platform/vsp1/vsp1_dl.h
>> @@ -19,6 +19,7 @@ struct vsp1_dl_manager;
>>
>> #define VSP1_DL_FRAME_END_COMPLETED BIT(0)
>> #define VSP1_DL_FRAME_END_INTERNAL BIT(1)
>> +#define VSP1_DL_FRAME_END_WRITEBACK BIT(2)
>>
>> /**
>> * struct vsp1_dl_ext_cmd - Extended Display command
>> @@ -54,6 +55,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 +73,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_patch(struct vsp1_dl_body *dlb, u32 reg, u32 data,
>> + u32 patch);
>> 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);
>>
>> diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c
>> index 9d20ef5cd5f8..e9d0ce432a2c 100644
>> --- a/drivers/media/platform/vsp1/vsp1_drm.c
>> +++ b/drivers/media/platform/vsp1/vsp1_drm.c
>> @@ -23,6 +23,7 @@
>> #include "vsp1_pipe.h"
>> #include "vsp1_rwpf.h"
>> #include "vsp1_uif.h"
>> +#include "vsp1_video.h"
>>
>> #define BRX_NAME(e) (e)->type == VSP1_ENTITY_BRU ? "BRU" : "BRS"
>>
>> @@ -34,7 +35,7 @@ static void vsp1_du_pipeline_frame_end(struct vsp1_pipeline *pipe,
>> unsigned int completion)
>> {
>> struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe);
>> - bool complete = completion == VSP1_DL_FRAME_END_COMPLETED;
>> + bool complete = completion & VSP1_DL_FRAME_END_COMPLETED;
>>
>> if (drm_pipe->du_complete) {
>> struct vsp1_entity *uif = drm_pipe->uif;
>> @@ -48,6 +49,9 @@ static void vsp1_du_pipeline_frame_end(struct vsp1_pipeline *pipe,
>> drm_pipe->force_brx_release = false;
>> wake_up(&drm_pipe->wait_queue);
>> }
>> +
>> + if (completion & VSP1_DL_FRAME_END_WRITEBACK)
>> + vsp1_video_wb_frame_end(pipe->output->video);
>> }
>>
>> /* -----------------------------------------------------------------------------
>> @@ -541,6 +545,8 @@ static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe)
>>
>> if (drm_pipe->force_brx_release)
>> dl_flags |= VSP1_DL_FRAME_END_INTERNAL;
>> + if (pipe->output->writeback)
>> + dl_flags |= VSP1_DL_FRAME_END_WRITEBACK;
>>
>> dl = vsp1_dl_list_get(pipe->output->dlm);
>> dlb = vsp1_dl_list_get_body0(dl);
>> @@ -859,8 +865,14 @@ void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index,
>> drm_pipe->crc = cfg->crc;
>>
>> mutex_lock(&vsp1->drm->lock);
>> +
>> + /* If we have a writeback node attached, update the video buffers. */
>> + if (pipe->output->video)
>> + vsp1_video_wb_prepare(pipe->output->video);
>> +
>> vsp1_du_pipeline_setup_inputs(vsp1, pipe);
>> vsp1_du_pipeline_configure(pipe);
>> +
>> mutex_unlock(&vsp1->drm->lock);
>> }
>> EXPORT_SYMBOL_GPL(vsp1_du_atomic_flush);
>> diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c
>> index c650e45bb0ad..235febd18ffa 100644
>> --- a/drivers/media/platform/vsp1/vsp1_drv.c
>> +++ b/drivers/media/platform/vsp1/vsp1_drv.c
>> @@ -63,6 +63,21 @@ static irqreturn_t vsp1_irq_handler(int irq, void *data)
>> vsp1_pipeline_frame_end(wpf->entity.pipe);
>> ret = IRQ_HANDLED;
>> }
>> +
>> + /*
>> + * Process the display start interrupt after the frame end
>> + * interrupt to make sure the display list queue is correctly
>> + * updated when processing the display start.
>> + */
>> + if (wpf->has_writeback) {
>> + status = vsp1_read(vsp1, VI6_DISP_IRQ_STA(i));
>> + vsp1_write(vsp1, VI6_DISP_IRQ_STA(i), ~status & mask);
>> +
>> + if (status & VI6_DISP_IRQ_STA_DST) {
>> + vsp1_pipeline_display_start(wpf->entity.pipe);
>> + ret = IRQ_HANDLED;
>> + }
>> + }
>> }
>>
>> return ret;
>> @@ -435,7 +450,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
>> vsp1->wpf[i] = wpf;
>> list_add_tail(&wpf->entity.list_dev, &vsp1->entities);
>>
>> - if (vsp1->info->uapi) {
>> + if (vsp1->info->uapi || wpf->has_writeback) {
>> struct vsp1_video *video = vsp1_video_create(vsp1, wpf);
>>
>> if (IS_ERR(video)) {
>> diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/vsp1/vsp1_pipe.c
>> index 54ff539ffea0..016800c45bc1 100644
>> --- a/drivers/media/platform/vsp1/vsp1_pipe.c
>> +++ b/drivers/media/platform/vsp1/vsp1_pipe.c
>> @@ -309,6 +309,11 @@ bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe)
>> return pipe->buffers_ready == mask;
>> }
>>
>> +void vsp1_pipeline_display_start(struct vsp1_pipeline *pipe)
>> +{
>> + vsp1_dlm_irq_display_start(pipe->output->dlm);
>> +}
>> +
>> void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe)
>> {
>> unsigned int flags;
>> diff --git a/drivers/media/platform/vsp1/vsp1_pipe.h b/drivers/media/platform/vsp1/vsp1_pipe.h
>> index ae646c9ef337..82d51b891f1e 100644
>> --- a/drivers/media/platform/vsp1/vsp1_pipe.h
>> +++ b/drivers/media/platform/vsp1/vsp1_pipe.h
>> @@ -47,6 +47,11 @@ struct vsp1_format_info {
>> bool alpha;
>> };
>>
>> +static inline bool vsp1_format_is_rgb(const struct vsp1_format_info *info)
>> +{
>> + return info->mbus == MEDIA_BUS_FMT_ARGB8888_1X32;
>> +}
>> +
>> enum vsp1_pipeline_state {
>> VSP1_PIPELINE_STOPPED,
>> VSP1_PIPELINE_RUNNING,
>> @@ -158,6 +163,7 @@ bool vsp1_pipeline_stopped(struct vsp1_pipeline *pipe);
>> int vsp1_pipeline_stop(struct vsp1_pipeline *pipe);
>> bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe);
>>
>> +void vsp1_pipeline_display_start(struct vsp1_pipeline *pipe);
>> void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe);
>>
>> void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
>> diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h
>> index 70742ecf766f..910990b27617 100644
>> --- a/drivers/media/platform/vsp1/vsp1_rwpf.h
>> +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h
>> @@ -35,6 +35,7 @@ struct vsp1_rwpf {
>> struct v4l2_ctrl_handler ctrls;
>>
>> struct vsp1_video *video;
>> + bool has_writeback;
>>
>> unsigned int max_width;
>> unsigned int max_height;
>> @@ -61,6 +62,7 @@ struct vsp1_rwpf {
>> } flip;
>>
>> struct vsp1_rwpf_memory mem;
>> + bool writeback;
>>
>> struct vsp1_dl_manager *dlm;
>> };
>> diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c
>> index 8feaa8d89fe8..0ac3430292d0 100644
>> --- a/drivers/media/platform/vsp1/vsp1_video.c
>> +++ b/drivers/media/platform/vsp1/vsp1_video.c
>> @@ -34,7 +34,6 @@
>> #include "vsp1_uds.h"
>> #include "vsp1_video.h"
>>
>> -#define VSP1_VIDEO_DEF_FORMAT V4L2_PIX_FMT_YUYV
>> #define VSP1_VIDEO_DEF_WIDTH 1024
>> #define VSP1_VIDEO_DEF_HEIGHT 768
>>
>> @@ -45,6 +44,14 @@
>> * Helper functions
>> */
>>
>> +static inline unsigned int vsp1_video_default_format(struct vsp1_video *video)
>> +{
>> + if (video->rwpf->has_writeback)
>> + return V4L2_PIX_FMT_RGB24;
>> + else
>> + return V4L2_PIX_FMT_YUYV;
>> +}
>> +
>> static struct v4l2_subdev *
>> vsp1_video_remote_subdev(struct media_pad *local, u32 *pad)
>> {
>> @@ -113,11 +120,13 @@ static int __vsp1_video_try_format(struct vsp1_video *video,
>>
>> /*
>> * Retrieve format information and select the default format if the
>> - * requested format isn't supported.
>> + * requested format isn't supported. Writeback video nodes only support
>> + * RGB formats.
>> */
>> info = vsp1_get_format_info(video->vsp1, pix->pixelformat);
>> - if (info == NULL)
>> - info = vsp1_get_format_info(video->vsp1, VSP1_VIDEO_DEF_FORMAT);
>> + if (!info || (video->rwpf->has_writeback && !vsp1_format_is_rgb(info)))
>> + info = vsp1_get_format_info(video->vsp1,
>> + vsp1_video_default_format(video));
>>
>> pix->pixelformat = info->fourcc;
>> pix->colorspace = V4L2_COLORSPACE_SRGB;
>> @@ -946,6 +955,122 @@ static const struct vb2_ops vsp1_video_queue_qops = {
>> .stop_streaming = vsp1_video_stop_streaming,
>> };
>>
>> +/* -----------------------------------------------------------------------------
>> + * Writeback Support
>> + */
>> +
>> +static void vsp1_video_wb_buffer_queue(struct vb2_buffer *vb)
>> +{
>> + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
>> + struct vsp1_video *video = vb2_get_drv_priv(vb->vb2_queue);
>> + struct vsp1_vb2_buffer *buf = to_vsp1_vb2_buffer(vbuf);
>> + unsigned long flags;
>> +
>> + spin_lock_irqsave(&video->irqlock, flags);
>> + list_add_tail(&buf->queue, &video->irqqueue);
>> + spin_unlock_irqrestore(&video->irqlock, flags);
>> +}
>> +
>> +static int vsp1_video_wb_start_streaming(struct vb2_queue *vq,
>> + unsigned int count)
>> +{
>> + struct vsp1_video *video = vb2_get_drv_priv(vq);
>> +
>> + video->wb_running = true;
>> + return 0;
>> +}
>> +
>> +static bool vsp1_video_wb_stopped(struct vsp1_video *video)
>> +{
>> + unsigned long flags;
>> + bool stopped;
>> +
>> + spin_lock_irqsave(&video->irqlock, flags);
>> + stopped = list_empty(&video->wb_queue);
>> + spin_unlock_irqrestore(&video->irqlock, flags);
>> +
>> + return stopped;
>> +}
>> +
>> +static void vsp1_video_wb_stop_streaming(struct vb2_queue *vq)
>> +{
>> + struct vsp1_video *video = vb2_get_drv_priv(vq);
>> + struct vsp1_rwpf *rwpf = video->rwpf;
>> + struct vsp1_pipeline *pipe = rwpf->entity.pipe;
>> + unsigned long flags;
>> + int ret;
>> +
>> + /* Disable writeback and wait for the pending frames to complete. */
>> + spin_lock_irqsave(&video->irqlock, flags);
>> + video->wb_running = false;
>> + spin_unlock_irqrestore(&video->irqlock, flags);
>> +
>> + ret = wait_event_timeout(pipe->wq, vsp1_video_wb_stopped(video),
>> + msecs_to_jiffies(500));
>> + if (ret == 0) {
>> + dev_err(video->vsp1->dev, "writeback stop timeout\n");
>> + list_splice_init(&video->wb_queue, &video->irqqueue);
>> + }
>> +
>> + vsp1_video_release_buffers(video);
>> +}
>> +
>> +static const struct vb2_ops vsp1_video_wb_queue_qops = {
>> + .queue_setup = vsp1_video_queue_setup,
>> + .buf_prepare = vsp1_video_buffer_prepare,
>> + .buf_queue = vsp1_video_wb_buffer_queue,
>> + .wait_prepare = vb2_ops_wait_prepare,
>> + .wait_finish = vb2_ops_wait_finish,
>> + .start_streaming = vsp1_video_wb_start_streaming,
>> + .stop_streaming = vsp1_video_wb_stop_streaming,
>> +};
>> +
>> +void vsp1_video_wb_prepare(struct vsp1_video *video)
>> +{
>> + struct vsp1_vb2_buffer *buf;
>> + unsigned long flags;
>> +
>> + /*
>> + * If writeback is disabled, return immediately. Otherwise remove the
>> + * first buffer from the irqqueue, add it to the writeback queue and
>> + * configure the WPF for writeback.
>> + */
>> +
>> + spin_lock_irqsave(&video->irqlock, flags);
>> +
>> + if (!video->wb_running) {
>> + spin_unlock_irqrestore(&video->irqlock, flags);
>> + return;
>> + }
>> +
>> + buf = list_first_entry_or_null(&video->irqqueue, struct vsp1_vb2_buffer,
>> + queue);
>> + if (buf)
>> + list_move_tail(&buf->queue, &video->wb_queue);
>> +
>> + spin_unlock_irqrestore(&video->irqlock, flags);
>> +
>> + if (buf) {
>> + video->rwpf->mem = buf->mem;
>> + video->rwpf->writeback = true;
>> + }
>> +}
>> +
>> +void vsp1_video_wb_frame_end(struct vsp1_video *video)
>> +{
>> + struct vsp1_vb2_buffer *done;
>> + unsigned long flags;
>> +
>> + /* Complete the first buffer from the writeback queue. */
>> + spin_lock_irqsave(&video->irqlock, flags);
>> + done = list_first_entry(&video->wb_queue, struct vsp1_vb2_buffer,
>> + queue);
>> + list_del(&done->queue);
>> + spin_unlock_irqrestore(&video->irqlock, flags);
>> +
>> + vsp1_video_complete_buffer(video, done);
>> +}
>> +
>> /* -----------------------------------------------------------------------------
>> * V4L2 ioctls
>> */
>> @@ -1045,6 +1170,13 @@ vsp1_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
>> if (video->queue.owner && video->queue.owner != file->private_data)
>> return -EBUSY;
>>
>> + /*
>> + * Writeback video devices don't need to interface with the pipeline or
>> + * verify formats, just turn streaming on.
>> + */
>> + if (video->rwpf->has_writeback)
>> + return vb2_streamon(&video->queue, type);
>> +
>> /*
>> * Get a pipeline for the video node and start streaming on it. No link
>> * touching an entity in the pipeline can be activated or deactivated
>> @@ -1273,7 +1405,7 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1,
>> video->pad.flags = MEDIA_PAD_FL_SOURCE;
>> video->video.vfl_dir = VFL_DIR_TX;
>> } else {
>> - direction = "output";
>> + direction = rwpf->has_writeback ? "writeback" : "output";
>> video->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
>> video->pad.flags = MEDIA_PAD_FL_SINK;
>> video->video.vfl_dir = VFL_DIR_RX;
>> @@ -1282,6 +1414,7 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1,
>> mutex_init(&video->lock);
>> spin_lock_init(&video->irqlock);
>> INIT_LIST_HEAD(&video->irqqueue);
>> + INIT_LIST_HEAD(&video->wb_queue);
>>
>> /* Initialize the media entity... */
>> ret = media_entity_pads_init(&video->video.entity, 1, &video->pad);
>> @@ -1289,7 +1422,7 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1,
>> return ERR_PTR(ret);
>>
>> /* ... and the format ... */
>> - rwpf->format.pixelformat = VSP1_VIDEO_DEF_FORMAT;
>> + rwpf->format.pixelformat = vsp1_video_default_format(video);
>> rwpf->format.width = VSP1_VIDEO_DEF_WIDTH;
>> rwpf->format.height = VSP1_VIDEO_DEF_HEIGHT;
>> __vsp1_video_try_format(video, &rwpf->format, &rwpf->fmtinfo);
>> @@ -1310,7 +1443,10 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1,
>> video->queue.lock = &video->lock;
>> video->queue.drv_priv = video;
>> video->queue.buf_struct_size = sizeof(struct vsp1_vb2_buffer);
>> - video->queue.ops = &vsp1_video_queue_qops;
>> + if (rwpf->has_writeback)
>> + video->queue.ops = &vsp1_video_wb_queue_qops;
>> + else
>> + video->queue.ops = &vsp1_video_queue_qops;
>> video->queue.mem_ops = &vb2_dma_contig_memops;
>> video->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
>> video->queue.dev = video->vsp1->bus_master;
>> diff --git a/drivers/media/platform/vsp1/vsp1_video.h b/drivers/media/platform/vsp1/vsp1_video.h
>> index f3cf5e2fdf5a..13b357b07ee2 100644
>> --- a/drivers/media/platform/vsp1/vsp1_video.h
>> +++ b/drivers/media/platform/vsp1/vsp1_video.h
>> @@ -44,6 +44,9 @@ struct vsp1_video {
>> struct vb2_queue queue;
>> spinlock_t irqlock;
>> struct list_head irqqueue;
>> +
>> + bool wb_running;
>> + struct list_head wb_queue;
>> };
>>
>> static inline struct vsp1_video *to_vsp1_video(struct video_device *vdev)
>> @@ -54,6 +57,9 @@ static inline struct vsp1_video *to_vsp1_video(struct video_device *vdev)
>> void vsp1_video_suspend(struct vsp1_device *vsp1);
>> void vsp1_video_resume(struct vsp1_device *vsp1);
>>
>> +void vsp1_video_wb_prepare(struct vsp1_video *video);
>> +void vsp1_video_wb_frame_end(struct vsp1_video *video);
>> +
>> struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1,
>> struct vsp1_rwpf *rwpf);
>> void vsp1_video_cleanup(struct vsp1_video *video);
>> diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c
>> index 18c49e3a7875..81650a625185 100644
>> --- a/drivers/media/platform/vsp1/vsp1_wpf.c
>> +++ b/drivers/media/platform/vsp1/vsp1_wpf.c
>> @@ -240,6 +240,7 @@ static void wpf_configure_stream(struct vsp1_entity *entity,
>> struct vsp1_device *vsp1 = wpf->entity.vsp1;
>> const struct v4l2_mbus_framefmt *source_format;
>> const struct v4l2_mbus_framefmt *sink_format;
>> + unsigned int index = wpf->entity.index;
>> unsigned int i;
>> u32 outfmt = 0;
>> u32 srcrpf = 0;
>> @@ -250,8 +251,9 @@ static void wpf_configure_stream(struct vsp1_entity *entity,
>> source_format = vsp1_entity_get_pad_format(&wpf->entity,
>> wpf->entity.config,
>> RWPF_PAD_SOURCE);
>> +
>> /* Format */
>> - if (!pipe->lif) {
>> + if (!pipe->lif || wpf->writeback) {
>> const struct v4l2_pix_format_mplane *format = &wpf->format;
>> const struct vsp1_format_info *fmtinfo = wpf->fmtinfo;
>>
>> @@ -276,8 +278,7 @@ static void wpf_configure_stream(struct vsp1_entity *entity,
>>
>> vsp1_wpf_write(wpf, dlb, VI6_WPF_DSWAP, fmtinfo->swap);
>>
>> - if (vsp1_feature(vsp1, VSP1_HAS_WPF_HFLIP) &&
>> - wpf->entity.index == 0)
>> + if (vsp1_feature(vsp1, VSP1_HAS_WPF_HFLIP) && index == 0)
>> vsp1_wpf_write(wpf, dlb, VI6_WPF_ROT_CTRL,
>> VI6_WPF_ROT_CTRL_LN16 |
>> (256 << VI6_WPF_ROT_CTRL_LMEM_WD_SHIFT));
>> @@ -288,11 +289,9 @@ static void wpf_configure_stream(struct vsp1_entity *entity,
>>
>> wpf->outfmt = outfmt;
>>
>> - vsp1_dl_body_write(dlb, VI6_DPR_WPF_FPORCH(wpf->entity.index),
>> + vsp1_dl_body_write(dlb, VI6_DPR_WPF_FPORCH(index),
>> VI6_DPR_WPF_FPORCH_FP_WPFN);
>>
>> - vsp1_dl_body_write(dlb, VI6_WPF_WRBCK_CTRL(wpf->entity.index), 0);
>> -
>> /*
>> * Sources. If the pipeline has a single input and BRx is not used,
>> * configure it as the master layer. Otherwise configure all
>> @@ -318,9 +317,24 @@ static void wpf_configure_stream(struct vsp1_entity *entity,
>> vsp1_wpf_write(wpf, dlb, VI6_WPF_SRCRPF, srcrpf);
>>
>> /* Enable interrupts. */
>> - vsp1_dl_body_write(dlb, VI6_WPF_IRQ_STA(wpf->entity.index), 0);
>> - vsp1_dl_body_write(dlb, VI6_WPF_IRQ_ENB(wpf->entity.index),
>> + vsp1_dl_body_write(dlb, VI6_WPF_IRQ_STA(index), 0);
>> + vsp1_dl_body_write(dlb, VI6_WPF_IRQ_ENB(index),
>> VI6_WFP_IRQ_ENB_DFEE);
>> +
>> + /*
>> + * Configure writeback for display pipelines. The wpf writeback flag is
>> + * never set for memory-to-memory pipelines.
>> + */
>> + vsp1_dl_body_write(dlb, VI6_DISP_IRQ_STA(index), 0);
>> + if (wpf->writeback) {
>
> I feel like a comment here would be useful to make it clear that by
> using _patch the VI6_DISP_IRQ_ENB_DSTE, and VI6_WPF_WRBCK_CTRL_WBMD will
> be reset to 0 at the frame-end?
>
> But maybe that's too verbose ... and won't be an issue once the function
> documentation is updated for vsp1_dl_body_write_patch().
>
>
>> + vsp1_dl_body_write_patch(dlb, VI6_DISP_IRQ_ENB(index),
>> + VI6_DISP_IRQ_ENB_DSTE, 0);
>> + vsp1_dl_body_write_patch(dlb, VI6_WPF_WRBCK_CTRL(index),
>> + VI6_WPF_WRBCK_CTRL_WBMD, 0);
>> + } else {
>> + vsp1_dl_body_write(dlb, VI6_DISP_IRQ_ENB(index), 0);
>> + vsp1_dl_body_write(dlb, VI6_WPF_WRBCK_CTRL(index), 0);
>> + }
>> }
>>
>> static void wpf_configure_frame(struct vsp1_entity *entity,
>> @@ -390,7 +404,11 @@ static void wpf_configure_partition(struct vsp1_entity *entity,
>> (0 << VI6_WPF_SZCLIP_OFST_SHIFT) |
>> (height << VI6_WPF_SZCLIP_SIZE_SHIFT));
>>
>> - if (pipe->lif)
>> + /*
>> + * For display pipelines without writeback enabled there's no memory
>> + * address to configure, return now.
>> + */
>> + if (pipe->lif && !wpf->writeback)
>> return;
>>
>> /*
>> @@ -479,6 +497,12 @@ static void wpf_configure_partition(struct vsp1_entity *entity,
>> vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_ADDR_Y, mem.addr[0]);
>> vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_ADDR_C0, mem.addr[1]);
>> vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_ADDR_C1, mem.addr[2]);
>> +
>> + /*
>> + * Writeback operates in single-shot mode and lasts for a single frame,
>> + * reset the writeback flag to false for the next frame.
>> + */
>> + wpf->writeback = false;
>
>
> This differs from my implementation right? I think my version just ran
> whenever there was a buffer available. (Except that when there was no
> atomic_flush - there was a large amount of latency...)
>
> I guess this comes down to the fact that we will not queue up frames
> except unless there is a real frame-change caused by a fresh
> atomic_flush ... and so the buffer rate does not reflect the display
> refresh rate.
>
> Now that I've written the above, I think that's made the reasoning
> clearer for me so I've answered my own questions :)
>
>
>> }
>>
>> static unsigned int wpf_max_width(struct vsp1_entity *entity,
>> @@ -529,6 +553,13 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index)
>> wpf->max_height = WPF_GEN3_MAX_HEIGHT;
>> }
>>
>> + /*
>> + * On Gen3 WPFs with a LIF output can also write to memory for display
>
> Hrm ... I might have said 'with an LIF'. ... but it depends whether you say:
> 'with a liph'
>
> or you spell it out:
>
> 'with an ell-eye-eff
>
> I personally spell out acronyms in my head, so that makes is 'an'.
>
> It doesn't matter which really here though :-)
>
>
>> + * writeback.
>> + */
>> + if (vsp1->info->gen > 2 && index < vsp1->info->lif_count)
>> + wpf->has_writeback = true;
>> +
>> wpf->entity.ops = &wpf_entity_ops;
>> wpf->entity.type = VSP1_ENTITY_WPF;
>> wpf->entity.index = index;
>>
>
--
Regards
--
Kieran
More information about the dri-devel
mailing list