Re: [RFC 3/3] v4l: vsp1: Provide a writeback video device

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxxxxxxxxx>
> ---
>  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@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux