Message ID | 1477576885-21978-4-git-send-email-kieran.bingham+renesas@ideasonboard.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Looks like I forgot to run checkpatch here, and it picked up a few things. Please disregard them in your review, and they will be fixed for next version. On 27/10/16 15:01, Kieran Bingham wrote: > 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, by extending the WPF > source pads, and establishing a V4L2 video device node connected to the > new source. > > The source will be able to perform pixel format conversion, but not > rescaling, and as such the output from the memory node will always be > of the same dimensions as the display output. > > Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com> > --- > drivers/media/platform/vsp1/vsp1.h | 1 + > drivers/media/platform/vsp1/vsp1_drm.c | 20 ++++ > drivers/media/platform/vsp1/vsp1_drv.c | 5 +- > drivers/media/platform/vsp1/vsp1_rwpf.c | 15 ++- > drivers/media/platform/vsp1/vsp1_rwpf.h | 2 + > drivers/media/platform/vsp1/vsp1_video.c | 161 ++++++++++++++++++++++++++++++- > drivers/media/platform/vsp1/vsp1_video.h | 5 + > drivers/media/platform/vsp1/vsp1_wpf.c | 23 ++++- > 8 files changed, 219 insertions(+), 13 deletions(-) > > diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h > index 85387a64179a..a2d462264312 100644 > --- a/drivers/media/platform/vsp1/vsp1.h > +++ b/drivers/media/platform/vsp1/vsp1.h > @@ -54,6 +54,7 @@ struct vsp1_uds; > #define VSP1_HAS_WPF_HFLIP (1 << 6) > #define VSP1_HAS_HGO (1 << 7) > #define VSP1_HAS_HGT (1 << 8) > +#define VSP1_HAS_WPF_WRITEBACK (1 << 9) > > struct vsp1_device_info { > u32 version; > diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c > index 0daf5f2c06e2..446188a06a92 100644 > --- a/drivers/media/platform/vsp1/vsp1_drm.c > +++ b/drivers/media/platform/vsp1/vsp1_drm.c > @@ -27,6 +27,7 @@ > #include "vsp1_lif.h" > #include "vsp1_pipe.h" > #include "vsp1_rwpf.h" > +#include "vsp1_video.h" > > > /* ----------------------------------------------------------------------------- > @@ -479,6 +480,13 @@ void vsp1_du_atomic_flush(struct device *dev) > __func__, rpf->entity.index); > } > > + /* > + * If we have a writeback node attached, we use this opportunity to > + * update the video buffers. > + */ > + if (pipe->output->video && pipe->output->video->frame_end) > + pipe->output->video->frame_end(pipe); > + > /* Configure all entities in the pipeline. */ > list_for_each_entry(entity, &pipe->entities, list_pipe) { > /* Disconnect unused RPFs from the pipeline. */ > @@ -590,6 +598,17 @@ int vsp1_drm_create_links(struct vsp1_device *vsp1) > if (ret < 0) > return ret; > > + if (!(vsp1->info->features & VSP1_HAS_WPF_WRITEBACK)) > + return 0; > + > + /* Connect the video device to the WPF for Writeback support */ > + ret = media_create_pad_link(&vsp1->wpf[0]->entity.subdev.entity, > + RWPF_PAD_SOURCE_WB, > + &vsp1->wpf[0]->video->video.entity, > + 0, flags); > + if (ret < 0) > + return ret; > + > return 0; > } > > @@ -620,6 +639,7 @@ int vsp1_drm_init(struct vsp1_device *vsp1) > pipe->bru = &vsp1->bru->entity; > pipe->lif = &vsp1->lif->entity; > pipe->output = vsp1->wpf[0]; > + pipe->output->pipe = pipe; > > return 0; > } > diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c > index 3b084976094b..42fa822b38d3 100644 > --- a/drivers/media/platform/vsp1/vsp1_drv.c > +++ b/drivers/media/platform/vsp1/vsp1_drv.c > @@ -408,7 +408,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)) { > @@ -705,7 +705,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = { > .version = VI6_IP_VERSION_MODEL_VSPD_GEN3, > .model = "VSP2-D", > .gen = 3, > - .features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_WPF_VFLIP, > + .features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_WPF_VFLIP > + | VSP1_HAS_WPF_WRITEBACK, > .rpf_count = 5, > .wpf_count = 2, > .num_bru_inputs = 5, > diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c > index 7deb6b9acf0b..7ad3ed097d50 100644 > --- a/drivers/media/platform/vsp1/vsp1_rwpf.c > +++ b/drivers/media/platform/vsp1/vsp1_rwpf.c > @@ -83,9 +83,10 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, > > format = vsp1_entity_get_pad_format(&rwpf->entity, config, fmt->pad); > > - if (fmt->pad == RWPF_PAD_SOURCE) { > + if (fmt->pad == RWPF_PAD_SOURCE || > + fmt->pad == RWPF_PAD_SOURCE_WB) { > /* The RWPF performs format conversion but can't scale, only the > - * format code can be changed on the source pad. > + * format code can be changed on the source pads. > */ > format->code = fmt->format.code; > fmt->format = *format; > @@ -113,11 +114,19 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, > crop->height = fmt->format.height; > } > > - /* Propagate the format to the source pad. */ > + /* Propagate the format to the source pads. */ s/Propagate/Propogate/ will be fixed in next version. > format = vsp1_entity_get_pad_format(&rwpf->entity, config, > RWPF_PAD_SOURCE); > *format = fmt->format; > > + if (rwpf->has_writeback) { > + /* Propogate the format to the Writeback pad */ Likewise: s/Propagate/Propogate/ will be fixed in next version. > + format = vsp1_entity_get_pad_format(&rwpf->entity, config, > + RWPF_PAD_SOURCE_WB); > + > + *format = fmt->format; > + } > + > if (rwpf->flip.rotate) { > format->width = fmt->format.height; > format->height = fmt->format.width; > diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h > index b4ffc38f48af..739088a009e8 100644 > --- a/drivers/media/platform/vsp1/vsp1_rwpf.h > +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h > @@ -24,6 +24,7 @@ > > #define RWPF_PAD_SINK 0 > #define RWPF_PAD_SOURCE 1 > +#define RWPF_PAD_SOURCE_WB 2 > > struct v4l2_ctrl; > struct vsp1_dl_manager; > @@ -53,6 +54,7 @@ struct vsp1_rwpf { > > u32 mult_alpha; > u32 outfmt; > + bool has_writeback; > > struct { > spinlock_t lock; > diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c > index f10401065cd3..68cef93117f9 100644 > --- a/drivers/media/platform/vsp1/vsp1_video.c > +++ b/drivers/media/platform/vsp1/vsp1_video.c > @@ -81,11 +81,16 @@ static int vsp1_video_verify_format(struct vsp1_video *video) > if (ret < 0) > return ret == -ENOIOCTLCMD ? -EINVAL : ret; > > - if (video->rwpf->fmtinfo->mbus != fmt.format.code || > - video->rwpf->format.height != fmt.format.height || > + /* WPF entities can not perform any scaling action */ > + if (video->rwpf->format.height != fmt.format.height || > video->rwpf->format.width != fmt.format.width) > return -EINVAL; > > + /* Unless we are in writeback mode, we must match the format as well */ > + if (!video->is_writeback && > + video->rwpf->fmtinfo->mbus != fmt.format.code) > + return -EINVAL; > + > return 0; > } > > @@ -860,6 +865,7 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq) > list_for_each_entry(buffer, &video->irqqueue, queue) > vb2_buffer_done(&buffer->buf.vb2_buf, VB2_BUF_STATE_ERROR); > INIT_LIST_HEAD(&video->irqqueue); > + INIT_LIST_HEAD(&video->wbqueue); > spin_unlock_irqrestore(&video->irqlock, flags); > } > > @@ -873,6 +879,149 @@ static const struct vb2_ops vsp1_video_queue_qops = { > .stop_streaming = vsp1_video_stop_streaming, > }; > > + > +/* ----------------------------------------------------------------------------- > + * videobuf2 queue operations for writeback nodes > + */ > + > +static void vsp1_video_wb_process_buffer(struct vsp1_video *video) > +{ > + struct vsp1_vb2_buffer *buf; > + unsigned long flags; > + > + /* > + * Writeback uses a running stream, unlike the M2M interface which > + * controls a pipeline process manually though the use of > + * vsp1_pipeline_run(). > + * > + * Instead writeback will commence at the next frame interval, and can > + * be marked complete at the interval following that. To handle this we > + * store the configured buffer as pending until the next callback. > + * > + * | | | | | > + * A |<-->| > + * B |<-->| > + * C |<-->| : Only at interrupt C can A be marked done > + */ > + > + spin_lock_irqsave(&video->irqlock, flags); > + > + /* Move the pending image to the active hw queue */ > + if (video->pending) { > + list_add_tail(&video->pending->queue, &video->irqqueue); > + video->pending = NULL; > + } > + > + buf = list_first_entry_or_null(&video->wbqueue, struct vsp1_vb2_buffer, > + queue); > + > + if (buf) { > + video->rwpf->mem = buf->mem; > + > + /* Store this buffer as pending. It will commence at the next > + * frame start interrupt */ > + video->pending = buf; > + list_del(&buf->queue); > + } else { > + /* Disable writeback with no buffer */ > + video->rwpf->mem = (struct vsp1_rwpf_memory) { 0 }; > + } > + > + spin_unlock_irqrestore(&video->irqlock, flags); > +} > + > +static void vsp1_video_wb_frame_end(struct vsp1_pipeline *pipe) > +{ > + struct vsp1_video *video = pipe->output->video; > + unsigned long flags; > + > + spin_lock_irqsave(&pipe->irqlock, flags); > + > + /* Complete any buffer on the IRQ queue */ > + vsp1_video_complete_buffer(video); > + > + /* Queue up any buffer from our wb queue, and place on the IRQ queue */ > + vsp1_video_wb_process_buffer(video); > + > + spin_unlock_irqrestore(&pipe->irqlock, flags); > +} > + > +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->wbqueue); > + spin_unlock_irqrestore(&video->irqlock, flags); > + > + return; return statement not needed - will be removed. > +} > + > +static int vsp1_video_wb_start_streaming(struct vb2_queue *vq, > + unsigned int count) > +{ > + struct vsp1_video *video = vb2_get_drv_priv(vq); > + unsigned long flags; > + > + /* Enable the completion interrupts */ > + spin_lock_irqsave(&video->irqlock, flags); > + video->frame_end = vsp1_video_wb_frame_end; > + spin_unlock_irqrestore(&video->irqlock, flags); > + > + return 0; > +} > + > +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->pipe; > + struct vsp1_vb2_buffer *buffer; > + unsigned long flags; > + > + /* > + * Disable the completion interrupts, and clear the WPF memory to > + * prevent writing out frames > + */ > + spin_lock_irqsave(&video->irqlock, flags); > + video->frame_end = NULL; > + rwpf->mem = (struct vsp1_rwpf_memory) { 0 }; > + > + /* Return all queued buffers to userspace */ > + list_for_each_entry(buffer, &video->wbqueue, queue) > + vb2_buffer_done(&buffer->buf.vb2_buf, VB2_BUF_STATE_ERROR); > + list_for_each_entry(buffer, &video->irqqueue, queue) > + vb2_buffer_done(&buffer->buf.vb2_buf, VB2_BUF_STATE_ERROR); > + if (video->pending) { > + vb2_buffer_done(&video->pending->buf.vb2_buf, > + VB2_BUF_STATE_ERROR); > + video->pending = NULL; > + } > + > + INIT_LIST_HEAD(&video->wbqueue); > + INIT_LIST_HEAD(&video->irqqueue); > + spin_unlock_irqrestore(&video->irqlock, flags); > + > + /* Return the reference obtained by vsp1_video_streamon() */ > + vsp1_video_pipeline_put(pipe); > + > + return; return statement not needed - will be removed. > +} > + > +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, > +}; > + > + > /* ----------------------------------------------------------------------------- > * V4L2 ioctls > */ > @@ -1109,6 +1258,8 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1, > video->vsp1 = vsp1; > video->rwpf = rwpf; > > + video->is_writeback = rwpf->has_writeback; > + > if (rwpf->entity.type == VSP1_ENTITY_RPF) { > direction = "input"; > video->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; > @@ -1124,6 +1275,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->wbqueue); > > /* Initialize the media entity... */ > ret = media_entity_pads_init(&video->video.entity, 1, &video->pad); > @@ -1147,12 +1299,15 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1, > > video_set_drvdata(&video->video, video); > > + if (video->is_writeback) > + video->queue.ops = &vsp1_video_wb_queue_qops; > + else > + video->queue.ops = &vsp1_video_queue_qops; > video->queue.type = video->type; > video->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; > 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; > video->queue.mem_ops = &vb2_dma_contig_memops; > video->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; > video->queue.dev = video->vsp1->dev; > diff --git a/drivers/media/platform/vsp1/vsp1_video.h b/drivers/media/platform/vsp1/vsp1_video.h > index 50ea7f02205f..b63e14bbaef0 100644 > --- a/drivers/media/platform/vsp1/vsp1_video.h > +++ b/drivers/media/platform/vsp1/vsp1_video.h > @@ -48,6 +48,11 @@ struct vsp1_video { > struct vb2_queue queue; > spinlock_t irqlock; > struct list_head irqqueue; > + > + bool is_writeback; > + struct list_head wbqueue; > + struct vsp1_vb2_buffer *pending; > + void (*frame_end)(struct vsp1_pipeline *pipe); > }; > > static inline struct vsp1_video *to_vsp1_video(struct video_device *vdev) > diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c > index b5f44d6839c6..fea0fd3e662e 100644 > --- a/drivers/media/platform/vsp1/vsp1_wpf.c > +++ b/drivers/media/platform/vsp1/vsp1_wpf.c > @@ -248,6 +248,8 @@ static void wpf_configure(struct vsp1_entity *entity, > u32 outfmt = 0; > u32 srcrpf = 0; > > + bool writeback = pipe->lif && wpf->mem.addr[0]; > + > if (params == VSP1_ENTITY_PARAMS_RUNTIME) { > const unsigned int mask = BIT(WPF_CTRL_VFLIP) > | BIT(WPF_CTRL_HFLIP); > @@ -299,7 +301,14 @@ static void wpf_configure(struct vsp1_entity *entity, > (0 << VI6_WPF_SZCLIP_OFST_SHIFT) | > (height << VI6_WPF_SZCLIP_SIZE_SHIFT)); > > - if (pipe->lif) > + vsp1_dl_list_write(dl, VI6_WPF_WRBCK_CTRL, writeback ? > + VI6_WPF_WRBCK_CTRL_WBMD : 0); > + > + /* > + * Display pipelines with no writeback memory do not configure > + * the write out address > + */ > + if (pipe->lif && !writeback) > return; > > /* > @@ -384,7 +393,7 @@ static void wpf_configure(struct vsp1_entity *entity, > } > > /* Format */ > - if (!pipe->lif) { > + if (!pipe->lif || writeback) { > const struct v4l2_pix_format_mplane *format = &wpf->format; > const struct vsp1_format_info *fmtinfo = wpf->fmtinfo; > > @@ -424,8 +433,6 @@ static void wpf_configure(struct vsp1_entity *entity, > vsp1_dl_list_write(dl, VI6_DPR_WPF_FPORCH(wpf->entity.index), > VI6_DPR_WPF_FPORCH_FP_WPFN); > > - vsp1_dl_list_write(dl, VI6_WPF_WRBCK_CTRL, 0); > - > /* Sources. If the pipeline has a single input and BRU is not used, > * configure it as the master layer. Otherwise configure all > * inputs as sub-layers and select the virtual RPF as the master > @@ -475,6 +482,7 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) > { > struct vsp1_rwpf *wpf; > char name[6]; > + int sink_pads; > int ret; > > wpf = devm_kzalloc(vsp1->dev, sizeof(*wpf), GFP_KERNEL); > @@ -493,8 +501,13 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) > wpf->entity.type = VSP1_ENTITY_WPF; > wpf->entity.index = index; > > + /* WPF's with Write back support can output to the LIF and Memory */ > + wpf->has_writeback = (vsp1->info->features & VSP1_HAS_WPF_WRITEBACK) > + && index == 0; > + sink_pads = wpf->has_writeback ? 2 : 1; > + > sprintf(name, "wpf.%u", index); > - ret = vsp1_entity_init(vsp1, &wpf->entity, name, 1, 1, &wpf_ops, > + ret = vsp1_entity_init(vsp1, &wpf->entity, name, 1, sink_pads, &wpf_ops, > MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER); > if (ret < 0) > return ERR_PTR(ret); > -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h index 85387a64179a..a2d462264312 100644 --- a/drivers/media/platform/vsp1/vsp1.h +++ b/drivers/media/platform/vsp1/vsp1.h @@ -54,6 +54,7 @@ struct vsp1_uds; #define VSP1_HAS_WPF_HFLIP (1 << 6) #define VSP1_HAS_HGO (1 << 7) #define VSP1_HAS_HGT (1 << 8) +#define VSP1_HAS_WPF_WRITEBACK (1 << 9) struct vsp1_device_info { u32 version; diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c index 0daf5f2c06e2..446188a06a92 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.c +++ b/drivers/media/platform/vsp1/vsp1_drm.c @@ -27,6 +27,7 @@ #include "vsp1_lif.h" #include "vsp1_pipe.h" #include "vsp1_rwpf.h" +#include "vsp1_video.h" /* ----------------------------------------------------------------------------- @@ -479,6 +480,13 @@ void vsp1_du_atomic_flush(struct device *dev) __func__, rpf->entity.index); } + /* + * If we have a writeback node attached, we use this opportunity to + * update the video buffers. + */ + if (pipe->output->video && pipe->output->video->frame_end) + pipe->output->video->frame_end(pipe); + /* Configure all entities in the pipeline. */ list_for_each_entry(entity, &pipe->entities, list_pipe) { /* Disconnect unused RPFs from the pipeline. */ @@ -590,6 +598,17 @@ int vsp1_drm_create_links(struct vsp1_device *vsp1) if (ret < 0) return ret; + if (!(vsp1->info->features & VSP1_HAS_WPF_WRITEBACK)) + return 0; + + /* Connect the video device to the WPF for Writeback support */ + ret = media_create_pad_link(&vsp1->wpf[0]->entity.subdev.entity, + RWPF_PAD_SOURCE_WB, + &vsp1->wpf[0]->video->video.entity, + 0, flags); + if (ret < 0) + return ret; + return 0; } @@ -620,6 +639,7 @@ int vsp1_drm_init(struct vsp1_device *vsp1) pipe->bru = &vsp1->bru->entity; pipe->lif = &vsp1->lif->entity; pipe->output = vsp1->wpf[0]; + pipe->output->pipe = pipe; return 0; } diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index 3b084976094b..42fa822b38d3 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -408,7 +408,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)) { @@ -705,7 +705,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .version = VI6_IP_VERSION_MODEL_VSPD_GEN3, .model = "VSP2-D", .gen = 3, - .features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_WPF_VFLIP, + .features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_WPF_VFLIP + | VSP1_HAS_WPF_WRITEBACK, .rpf_count = 5, .wpf_count = 2, .num_bru_inputs = 5, diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c index 7deb6b9acf0b..7ad3ed097d50 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/vsp1/vsp1_rwpf.c @@ -83,9 +83,10 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, format = vsp1_entity_get_pad_format(&rwpf->entity, config, fmt->pad); - if (fmt->pad == RWPF_PAD_SOURCE) { + if (fmt->pad == RWPF_PAD_SOURCE || + fmt->pad == RWPF_PAD_SOURCE_WB) { /* The RWPF performs format conversion but can't scale, only the - * format code can be changed on the source pad. + * format code can be changed on the source pads. */ format->code = fmt->format.code; fmt->format = *format; @@ -113,11 +114,19 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, crop->height = fmt->format.height; } - /* Propagate the format to the source pad. */ + /* Propagate the format to the source pads. */ format = vsp1_entity_get_pad_format(&rwpf->entity, config, RWPF_PAD_SOURCE); *format = fmt->format; + if (rwpf->has_writeback) { + /* Propogate the format to the Writeback pad */ + format = vsp1_entity_get_pad_format(&rwpf->entity, config, + RWPF_PAD_SOURCE_WB); + + *format = fmt->format; + } + if (rwpf->flip.rotate) { format->width = fmt->format.height; format->height = fmt->format.width; diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h index b4ffc38f48af..739088a009e8 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h @@ -24,6 +24,7 @@ #define RWPF_PAD_SINK 0 #define RWPF_PAD_SOURCE 1 +#define RWPF_PAD_SOURCE_WB 2 struct v4l2_ctrl; struct vsp1_dl_manager; @@ -53,6 +54,7 @@ struct vsp1_rwpf { u32 mult_alpha; u32 outfmt; + bool has_writeback; struct { spinlock_t lock; diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index f10401065cd3..68cef93117f9 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -81,11 +81,16 @@ static int vsp1_video_verify_format(struct vsp1_video *video) if (ret < 0) return ret == -ENOIOCTLCMD ? -EINVAL : ret; - if (video->rwpf->fmtinfo->mbus != fmt.format.code || - video->rwpf->format.height != fmt.format.height || + /* WPF entities can not perform any scaling action */ + if (video->rwpf->format.height != fmt.format.height || video->rwpf->format.width != fmt.format.width) return -EINVAL; + /* Unless we are in writeback mode, we must match the format as well */ + if (!video->is_writeback && + video->rwpf->fmtinfo->mbus != fmt.format.code) + return -EINVAL; + return 0; } @@ -860,6 +865,7 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq) list_for_each_entry(buffer, &video->irqqueue, queue) vb2_buffer_done(&buffer->buf.vb2_buf, VB2_BUF_STATE_ERROR); INIT_LIST_HEAD(&video->irqqueue); + INIT_LIST_HEAD(&video->wbqueue); spin_unlock_irqrestore(&video->irqlock, flags); } @@ -873,6 +879,149 @@ static const struct vb2_ops vsp1_video_queue_qops = { .stop_streaming = vsp1_video_stop_streaming, }; + +/* ----------------------------------------------------------------------------- + * videobuf2 queue operations for writeback nodes + */ + +static void vsp1_video_wb_process_buffer(struct vsp1_video *video) +{ + struct vsp1_vb2_buffer *buf; + unsigned long flags; + + /* + * Writeback uses a running stream, unlike the M2M interface which + * controls a pipeline process manually though the use of + * vsp1_pipeline_run(). + * + * Instead writeback will commence at the next frame interval, and can + * be marked complete at the interval following that. To handle this we + * store the configured buffer as pending until the next callback. + * + * | | | | | + * A |<-->| + * B |<-->| + * C |<-->| : Only at interrupt C can A be marked done + */ + + spin_lock_irqsave(&video->irqlock, flags); + + /* Move the pending image to the active hw queue */ + if (video->pending) { + list_add_tail(&video->pending->queue, &video->irqqueue); + video->pending = NULL; + } + + buf = list_first_entry_or_null(&video->wbqueue, struct vsp1_vb2_buffer, + queue); + + if (buf) { + video->rwpf->mem = buf->mem; + + /* Store this buffer as pending. It will commence at the next + * frame start interrupt */ + video->pending = buf; + list_del(&buf->queue); + } else { + /* Disable writeback with no buffer */ + video->rwpf->mem = (struct vsp1_rwpf_memory) { 0 }; + } + + spin_unlock_irqrestore(&video->irqlock, flags); +} + +static void vsp1_video_wb_frame_end(struct vsp1_pipeline *pipe) +{ + struct vsp1_video *video = pipe->output->video; + unsigned long flags; + + spin_lock_irqsave(&pipe->irqlock, flags); + + /* Complete any buffer on the IRQ queue */ + vsp1_video_complete_buffer(video); + + /* Queue up any buffer from our wb queue, and place on the IRQ queue */ + vsp1_video_wb_process_buffer(video); + + spin_unlock_irqrestore(&pipe->irqlock, flags); +} + +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->wbqueue); + spin_unlock_irqrestore(&video->irqlock, flags); + + return; +} + +static int vsp1_video_wb_start_streaming(struct vb2_queue *vq, + unsigned int count) +{ + struct vsp1_video *video = vb2_get_drv_priv(vq); + unsigned long flags; + + /* Enable the completion interrupts */ + spin_lock_irqsave(&video->irqlock, flags); + video->frame_end = vsp1_video_wb_frame_end; + spin_unlock_irqrestore(&video->irqlock, flags); + + return 0; +} + +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->pipe; + struct vsp1_vb2_buffer *buffer; + unsigned long flags; + + /* + * Disable the completion interrupts, and clear the WPF memory to + * prevent writing out frames + */ + spin_lock_irqsave(&video->irqlock, flags); + video->frame_end = NULL; + rwpf->mem = (struct vsp1_rwpf_memory) { 0 }; + + /* Return all queued buffers to userspace */ + list_for_each_entry(buffer, &video->wbqueue, queue) + vb2_buffer_done(&buffer->buf.vb2_buf, VB2_BUF_STATE_ERROR); + list_for_each_entry(buffer, &video->irqqueue, queue) + vb2_buffer_done(&buffer->buf.vb2_buf, VB2_BUF_STATE_ERROR); + if (video->pending) { + vb2_buffer_done(&video->pending->buf.vb2_buf, + VB2_BUF_STATE_ERROR); + video->pending = NULL; + } + + INIT_LIST_HEAD(&video->wbqueue); + INIT_LIST_HEAD(&video->irqqueue); + spin_unlock_irqrestore(&video->irqlock, flags); + + /* Return the reference obtained by vsp1_video_streamon() */ + vsp1_video_pipeline_put(pipe); + + return; +} + +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, +}; + + /* ----------------------------------------------------------------------------- * V4L2 ioctls */ @@ -1109,6 +1258,8 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1, video->vsp1 = vsp1; video->rwpf = rwpf; + video->is_writeback = rwpf->has_writeback; + if (rwpf->entity.type == VSP1_ENTITY_RPF) { direction = "input"; video->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; @@ -1124,6 +1275,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->wbqueue); /* Initialize the media entity... */ ret = media_entity_pads_init(&video->video.entity, 1, &video->pad); @@ -1147,12 +1299,15 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1, video_set_drvdata(&video->video, video); + if (video->is_writeback) + video->queue.ops = &vsp1_video_wb_queue_qops; + else + video->queue.ops = &vsp1_video_queue_qops; video->queue.type = video->type; video->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; 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; video->queue.mem_ops = &vb2_dma_contig_memops; video->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; video->queue.dev = video->vsp1->dev; diff --git a/drivers/media/platform/vsp1/vsp1_video.h b/drivers/media/platform/vsp1/vsp1_video.h index 50ea7f02205f..b63e14bbaef0 100644 --- a/drivers/media/platform/vsp1/vsp1_video.h +++ b/drivers/media/platform/vsp1/vsp1_video.h @@ -48,6 +48,11 @@ struct vsp1_video { struct vb2_queue queue; spinlock_t irqlock; struct list_head irqqueue; + + bool is_writeback; + struct list_head wbqueue; + struct vsp1_vb2_buffer *pending; + void (*frame_end)(struct vsp1_pipeline *pipe); }; static inline struct vsp1_video *to_vsp1_video(struct video_device *vdev) diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index b5f44d6839c6..fea0fd3e662e 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -248,6 +248,8 @@ static void wpf_configure(struct vsp1_entity *entity, u32 outfmt = 0; u32 srcrpf = 0; + bool writeback = pipe->lif && wpf->mem.addr[0]; + if (params == VSP1_ENTITY_PARAMS_RUNTIME) { const unsigned int mask = BIT(WPF_CTRL_VFLIP) | BIT(WPF_CTRL_HFLIP); @@ -299,7 +301,14 @@ static void wpf_configure(struct vsp1_entity *entity, (0 << VI6_WPF_SZCLIP_OFST_SHIFT) | (height << VI6_WPF_SZCLIP_SIZE_SHIFT)); - if (pipe->lif) + vsp1_dl_list_write(dl, VI6_WPF_WRBCK_CTRL, writeback ? + VI6_WPF_WRBCK_CTRL_WBMD : 0); + + /* + * Display pipelines with no writeback memory do not configure + * the write out address + */ + if (pipe->lif && !writeback) return; /* @@ -384,7 +393,7 @@ static void wpf_configure(struct vsp1_entity *entity, } /* Format */ - if (!pipe->lif) { + if (!pipe->lif || writeback) { const struct v4l2_pix_format_mplane *format = &wpf->format; const struct vsp1_format_info *fmtinfo = wpf->fmtinfo; @@ -424,8 +433,6 @@ static void wpf_configure(struct vsp1_entity *entity, vsp1_dl_list_write(dl, VI6_DPR_WPF_FPORCH(wpf->entity.index), VI6_DPR_WPF_FPORCH_FP_WPFN); - vsp1_dl_list_write(dl, VI6_WPF_WRBCK_CTRL, 0); - /* Sources. If the pipeline has a single input and BRU is not used, * configure it as the master layer. Otherwise configure all * inputs as sub-layers and select the virtual RPF as the master @@ -475,6 +482,7 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) { struct vsp1_rwpf *wpf; char name[6]; + int sink_pads; int ret; wpf = devm_kzalloc(vsp1->dev, sizeof(*wpf), GFP_KERNEL); @@ -493,8 +501,13 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) wpf->entity.type = VSP1_ENTITY_WPF; wpf->entity.index = index; + /* WPF's with Write back support can output to the LIF and Memory */ + wpf->has_writeback = (vsp1->info->features & VSP1_HAS_WPF_WRITEBACK) + && index == 0; + sink_pads = wpf->has_writeback ? 2 : 1; + sprintf(name, "wpf.%u", index); - ret = vsp1_entity_init(vsp1, &wpf->entity, name, 1, 1, &wpf_ops, + ret = vsp1_entity_init(vsp1, &wpf->entity, name, 1, sink_pads, &wpf_ops, MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER); if (ret < 0) return ERR_PTR(ret);
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, by extending the WPF source pads, and establishing a V4L2 video device node connected to the new source. The source will be able to perform pixel format conversion, but not rescaling, and as such the output from the memory node will always be of the same dimensions as the display output. Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com> --- drivers/media/platform/vsp1/vsp1.h | 1 + drivers/media/platform/vsp1/vsp1_drm.c | 20 ++++ drivers/media/platform/vsp1/vsp1_drv.c | 5 +- drivers/media/platform/vsp1/vsp1_rwpf.c | 15 ++- drivers/media/platform/vsp1/vsp1_rwpf.h | 2 + drivers/media/platform/vsp1/vsp1_video.c | 161 ++++++++++++++++++++++++++++++- drivers/media/platform/vsp1/vsp1_video.h | 5 + drivers/media/platform/vsp1/vsp1_wpf.c | 23 ++++- 8 files changed, 219 insertions(+), 13 deletions(-)