Re: [PATCH 3/4] media i.MX27 camera: improve discard buffer handling.

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

 



On Fri, 20 Jan 2012, Javier Martin wrote:

> The way discard buffer was previously handled lead
> to possible races that made a buffer that was not
> yet ready to be overwritten by new video data. This
> is easily detected at 25fps just adding "#define DEBUG"
> to enable the "memset" check and seeing how the image
> is corrupted.
> 
> A new "discard" queue and two discard buffers have
> been added to make them flow trough the pipeline
> of queues and thus provide suitable event ordering.

Hmm, do I understand it right, that the problem is, that while the first 
frame went to the discard buffer, the second one went already to a user 
buffer, while it wasn't ready yet? And you solve this by adding one more 
discard buffer? Wouldn't it be possible to either not start capture until 
.start_streaming() is issued, which should also be the case after your 
patch 2/4, or, at least, just reuse one discard buffer multiple times 
until user buffers are available?

If I understand right, you don't really introduce two discard buffers, 
there's still only one data buffer, but you add two discard data objects 
and a list to keep them on. TBH, this seems severely over-engineered to 
me. What's wrong with just keeping one DMA data buffer and using it as 
long, as needed, and checking in your ISR, whether a proper buffer is 
present, by looking for list_empty(active)?

I added a couple of comments below, but my biggest question really is - 
why are these two buffer objects needed? Please, consider getting rid of 
them. So, this is not a full review, if the objects get removed, most of 
this patch will change anyway.

> 
> Signed-off-by: Javier Martin <javier.martin@xxxxxxxxxxxxxxxxx>
> ---
>  drivers/media/video/mx2_camera.c |  215 +++++++++++++++++++++-----------------
>  1 files changed, 117 insertions(+), 98 deletions(-)
> 
> diff --git a/drivers/media/video/mx2_camera.c b/drivers/media/video/mx2_camera.c
> index 4816da6..e0c5dd4 100644
> --- a/drivers/media/video/mx2_camera.c
> +++ b/drivers/media/video/mx2_camera.c
> @@ -224,6 +224,28 @@ struct mx2_fmt_cfg {
>  	struct mx2_prp_cfg		cfg;
>  };
>  
> +enum mx2_buffer_state {
> +	MX2_STATE_NEEDS_INIT = 0,
> +	MX2_STATE_PREPARED   = 1,
> +	MX2_STATE_QUEUED     = 2,
> +	MX2_STATE_ACTIVE     = 3,
> +	MX2_STATE_DONE       = 4,
> +	MX2_STATE_ERROR      = 5,
> +	MX2_STATE_IDLE       = 6,
> +};
> +
> +/* buffer for one video frame */
> +struct mx2_buffer {
> +	/* common v4l buffer stuff -- must be first */
> +	struct vb2_buffer		vb;
> +	struct list_head		queue;
> +	enum mx2_buffer_state		state;
> +	enum v4l2_mbus_pixelcode	code;
> +
> +	int				bufnum;
> +	bool				discard;
> +};
> +

When submitting a patch series, it is usually good to avoid 
double-patching. E.g., in this case, your first patch adds these enum and 
struct and this patch moves them a couple of lines up. Please, place them 
at the correct location already with the first patch.

>  struct mx2_camera_dev {
>  	struct device		*dev;
>  	struct soc_camera_host	soc_host;
> @@ -240,6 +262,7 @@ struct mx2_camera_dev {
>  
>  	struct list_head	capture;
>  	struct list_head	active_bufs;
> +	struct list_head	discard;
>  
>  	spinlock_t		lock;
>  
> @@ -252,6 +275,7 @@ struct mx2_camera_dev {
>  
>  	u32			csicr1;
>  
> +	struct mx2_buffer	buf_discard[2];
>  	void			*discard_buffer;
>  	dma_addr_t		discard_buffer_dma;
>  	size_t			discard_size;
> @@ -260,27 +284,6 @@ struct mx2_camera_dev {
>  	struct vb2_alloc_ctx	*alloc_ctx;
>  };
>  
> -enum mx2_buffer_state {
> -	MX2_STATE_NEEDS_INIT = 0,
> -	MX2_STATE_PREPARED   = 1,
> -	MX2_STATE_QUEUED     = 2,
> -	MX2_STATE_ACTIVE     = 3,
> -	MX2_STATE_DONE       = 4,
> -	MX2_STATE_ERROR      = 5,
> -	MX2_STATE_IDLE       = 6,
> -};
> -
> -/* buffer for one video frame */
> -struct mx2_buffer {
> -	/* common v4l buffer stuff -- must be first */
> -	struct vb2_buffer		vb;
> -	struct list_head		queue;
> -	enum mx2_buffer_state		state;
> -	enum v4l2_mbus_pixelcode	code;
> -
> -	int bufnum;
> -};
> -
>  static struct mx2_fmt_cfg mx27_emma_prp_table[] = {
>  	/*
>  	 * This is a generic configuration which is valid for most
> @@ -334,6 +337,29 @@ static struct mx2_fmt_cfg *mx27_emma_prp_get_format(
>  	return &mx27_emma_prp_table[0];
>  };
>  
> +static void mx27_update_emma_buf(struct mx2_camera_dev *pcdev,
> +				 unsigned long phys, int bufnum)
> +{
> +	u32 imgsize = pcdev->icd->user_height * pcdev->icd->user_width;

Are only 1-byte-per-pixel formats supported? Ok, it is only used for 
YUV420, please, move this variable down in that "if."

> +	struct mx2_fmt_cfg *prp = pcdev->emma_prp;
> +
> +	if (prp->cfg.channel == 1) {
> +		writel(phys, pcdev->base_emma +
> +				PRP_DEST_RGB1_PTR + 4 * bufnum);
> +	} else {
> +		writel(phys, pcdev->base_emma +
> +					PRP_DEST_Y_PTR -
> +					0x14 * bufnum);

Join the above two lines, please.

> +		if (prp->out_fmt == V4L2_PIX_FMT_YUV420) {
> +			writel(phys + imgsize,
> +			pcdev->base_emma + PRP_DEST_CB_PTR -
> +			0x14 * bufnum);
> +			writel(phys + ((5 * imgsize) / 4), pcdev->base_emma +
> +			PRP_DEST_CR_PTR - 0x14 * bufnum);
> +		}
> +	}
> +}
> +
>  static void mx2_camera_deactivate(struct mx2_camera_dev *pcdev)
>  {
>  	unsigned long flags;
> @@ -382,7 +408,7 @@ static int mx2_camera_add_device(struct soc_camera_device *icd)
>  	writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
>  
>  	pcdev->icd = icd;
> -	pcdev->frame_count = -1;
> +	pcdev->frame_count = 0;
>  
>  	dev_info(icd->parent, "Camera driver attached to camera %d\n",
>  		 icd->devnum);
> @@ -648,10 +674,9 @@ static void mx2_videobuf_release(struct vb2_buffer *vb)
>  	 * types.
>  	 */
>  	spin_lock_irqsave(&pcdev->lock, flags);
> -	if (buf->state == MX2_STATE_QUEUED || buf->state == MX2_STATE_ACTIVE) {
> -		list_del_init(&buf->queue);
> -		buf->state = MX2_STATE_NEEDS_INIT;
> -	} else if (cpu_is_mx25() && buf->state == MX2_STATE_ACTIVE) {
> +	INIT_LIST_HEAD(&buf->queue);

Wouldn't this leave the list, on which this buffer is, corrupted?

> +	buf->state = MX2_STATE_NEEDS_INIT;
> +	if (cpu_is_mx25() && buf->state == MX2_STATE_ACTIVE) {
>  		if (pcdev->fb1_active == buf) {
>  			pcdev->csicr1 &= ~CSICR1_FB1_DMA_INTEN;
>  			writel(0, pcdev->base_csi + CSIDMASA_FB1);
> @@ -674,7 +699,10 @@ static int mx2_start_streaming(struct vb2_queue *q, unsigned int count)
>  		to_soc_camera_host(icd->parent);
>  	struct mx2_camera_dev *pcdev = ici->priv;
>  	struct mx2_fmt_cfg *prp = pcdev->emma_prp;
> +	struct vb2_buffer *vb;
> +	struct mx2_buffer *buf;
>  	unsigned long flags;
> +	unsigned long phys;
>  	int ret = 0;
>  
>  	spin_lock_irqsave(&pcdev->lock, flags);
> @@ -684,6 +712,26 @@ static int mx2_start_streaming(struct vb2_queue *q, unsigned int count)
>  			goto err;
>  		}
>  
> +		buf = list_entry(pcdev->capture.next,
> +				 struct mx2_buffer, queue);
> +		buf->bufnum = 0;
> +		vb = &buf->vb;
> +		buf->state = MX2_STATE_ACTIVE;
> +
> +		phys = vb2_dma_contig_plane_dma_addr(vb, 0);
> +		mx27_update_emma_buf(pcdev, phys, buf->bufnum);
> +		list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
> +
> +		buf = list_entry(pcdev->capture.next,
> +				 struct mx2_buffer, queue);
> +		buf->bufnum = 1;
> +		vb = &buf->vb;
> +		buf->state = MX2_STATE_ACTIVE;
> +
> +		phys = vb2_dma_contig_plane_dma_addr(vb, 0);
> +		mx27_update_emma_buf(pcdev, phys, buf->bufnum);
> +		list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
> +
>  		if (prp->cfg.channel == 1) {
>  			writel(PRP_CNTL_CH1EN |
>  				PRP_CNTL_CSIEN |
> @@ -731,6 +779,9 @@ static int mx2_stop_streaming(struct vb2_queue *q)
>  			writel(cntl & ~PRP_CNTL_CH2EN,
>  			       pcdev->base_emma + PRP_CNTL);
>  		}
> +		INIT_LIST_HEAD(&pcdev->capture);
> +		INIT_LIST_HEAD(&pcdev->active_bufs);
> +		INIT_LIST_HEAD(&pcdev->discard);
>  	}
>  	spin_unlock_irqrestore(&pcdev->lock, flags);
>  
> @@ -793,50 +844,21 @@ static void mx27_camera_emma_buf_init(struct soc_camera_device *icd,
>  		to_soc_camera_host(icd->parent);
>  	struct mx2_camera_dev *pcdev = ici->priv;
>  	struct mx2_fmt_cfg *prp = pcdev->emma_prp;
> -	u32 imgsize = pcdev->icd->user_height * pcdev->icd->user_width;
>  
> +	writel((icd->user_width << 16) | icd->user_height,
> +	       pcdev->base_emma + PRP_SRC_FRAME_SIZE);
> +	writel(prp->cfg.src_pixel,
> +	       pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL);
>  	if (prp->cfg.channel == 1) {
> -		writel(pcdev->discard_buffer_dma,
> -				pcdev->base_emma + PRP_DEST_RGB1_PTR);
> -		writel(pcdev->discard_buffer_dma,
> -				pcdev->base_emma + PRP_DEST_RGB2_PTR);
> -
> -		writel((icd->user_width << 16) | icd->user_height,
> -			pcdev->base_emma + PRP_SRC_FRAME_SIZE);
>  		writel((icd->user_width << 16) | icd->user_height,
>  			pcdev->base_emma + PRP_CH1_OUT_IMAGE_SIZE);
>  		writel(bytesperline,
>  			pcdev->base_emma + PRP_DEST_CH1_LINE_STRIDE);
> -		writel(prp->cfg.src_pixel,
> -			pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL);
>  		writel(prp->cfg.ch1_pixel,
>  			pcdev->base_emma + PRP_CH1_PIXEL_FORMAT_CNTL);
>  	} else { /* channel 2 */
> -		writel(pcdev->discard_buffer_dma,
> -			pcdev->base_emma + PRP_DEST_Y_PTR);
> -		writel(pcdev->discard_buffer_dma,
> -			pcdev->base_emma + PRP_SOURCE_Y_PTR);
> -
> -		if (prp->cfg.out_fmt == PRP_CNTL_CH2_OUT_YUV420) {
> -			writel(pcdev->discard_buffer_dma + imgsize,
> -				pcdev->base_emma + PRP_DEST_CB_PTR);
> -			writel(pcdev->discard_buffer_dma + ((5 * imgsize) / 4),
> -				pcdev->base_emma + PRP_DEST_CR_PTR);
> -			writel(pcdev->discard_buffer_dma + imgsize,
> -				pcdev->base_emma + PRP_SOURCE_CB_PTR);
> -			writel(pcdev->discard_buffer_dma + ((5 * imgsize) / 4),
> -				pcdev->base_emma + PRP_SOURCE_CR_PTR);
> -		}
> -
> -		writel((icd->user_width << 16) | icd->user_height,
> -			pcdev->base_emma + PRP_SRC_FRAME_SIZE);
> -
>  		writel((icd->user_width << 16) | icd->user_height,
>  			pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE);
> -
> -		writel(prp->cfg.src_pixel,
> -			pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL);
> -
>  	}
>  
>  	/* Enable interrupts */
> @@ -927,11 +949,22 @@ static int mx2_camera_set_bus_param(struct soc_camera_device *icd,
>  		if (ret)
>  			return ret;
>  
> -		if (pcdev->discard_buffer)
> +		if (pcdev->discard_buffer) {
>  			dma_free_coherent(ici->v4l2_dev.dev,
>  				pcdev->discard_size, pcdev->discard_buffer,
>  				pcdev->discard_buffer_dma);
>  
> +			pcdev->buf_discard[0].discard = true;
> +			INIT_LIST_HEAD(&pcdev->buf_discard[0].queue);
> +			list_add_tail(&pcdev->buf_discard[0].queue,
> +				      &pcdev->discard);
> +
> +			pcdev->buf_discard[1].discard = true;
> +			INIT_LIST_HEAD(&pcdev->buf_discard[1].queue);
> +			list_add_tail(&pcdev->buf_discard[1].queue,
> +				      &pcdev->discard);
> +		}
> +

So, you want to do this every time someone does S_FMT?...

>  		/*
>  		 * I didn't manage to properly enable/disable the prp
>  		 * on a per frame basis during running transfers,
> @@ -1193,18 +1226,24 @@ static struct soc_camera_host_ops mx2_soc_camera_host_ops = {
>  static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev,
>  		int bufnum, int state)
>  {
> -	u32 imgsize = pcdev->icd->user_height * pcdev->icd->user_width;
> -	struct mx2_fmt_cfg *prp = pcdev->emma_prp;
>  	struct mx2_buffer *buf;
>  	struct vb2_buffer *vb;
>  	unsigned long phys;
>  
> -	if (!list_empty(&pcdev->active_bufs)) {
> -		buf = list_entry(pcdev->active_bufs.next,
> -			struct mx2_buffer, queue);
> +	BUG_ON(list_empty(&pcdev->active_bufs));
> +
> +	buf = list_entry(pcdev->active_bufs.next,
> +			 struct mx2_buffer, queue);
>  
> -		BUG_ON(buf->bufnum != bufnum);
> +	BUG_ON(buf->bufnum != bufnum);
>  
> +	if (buf->discard) {
> +		/*
> +		 * Discard buffer must not be returned to user space.
> +		 * Just return it to the discard queue.
> +		 */
> +		list_move_tail(pcdev->active_bufs.next, &pcdev->discard);
> +	} else {
>  		vb = &buf->vb;
>  #ifdef DEBUG
>  		phys = vb2_dma_contig_plane_dma_addr(vb, 0);
> @@ -1226,6 +1265,7 @@ static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev,
>  			}
>  		}
>  #endif
> +
>  		dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__, vb,
>  				vb2_plane_vaddr(vb, 0),
>  				vb2_get_plane_payload(vb, 0));
> @@ -1237,32 +1277,21 @@ static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev,
>  		vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
>  	}
>  
> -	if (list_empty(&pcdev->capture)) {
> -		if (prp->cfg.channel == 1) {
> -			writel(pcdev->discard_buffer_dma, pcdev->base_emma +
> -					PRP_DEST_RGB1_PTR + 4 * bufnum);
> -		} else {
> -			writel(pcdev->discard_buffer_dma, pcdev->base_emma +
> -						PRP_DEST_Y_PTR -
> -						0x14 * bufnum);
> -			if (prp->out_fmt == V4L2_PIX_FMT_YUV420) {
> -				writel(pcdev->discard_buffer_dma + imgsize,
> -				       pcdev->base_emma + PRP_DEST_CB_PTR -
> -				       0x14 * bufnum);
> -				writel(pcdev->discard_buffer_dma +
> -				       ((5 * imgsize) / 4), pcdev->base_emma +
> -				       PRP_DEST_CR_PTR - 0x14 * bufnum);
> -			}
> -		}
> +	pcdev->frame_count++;
> +
> +	if (list_empty(&pcdev->capture) && !list_empty(&pcdev->discard)) {
> +		buf = list_entry(pcdev->discard.next,
> +			struct mx2_buffer, queue);
> +		buf->bufnum = bufnum;
> +		list_move_tail(pcdev->discard.next, &pcdev->active_bufs);
> +		mx27_update_emma_buf(pcdev, pcdev->discard_buffer_dma, bufnum);
>  		return;
>  	}
>  
> -	pcdev->frame_count++;
> -
>  	buf = list_entry(pcdev->capture.next,
>  			struct mx2_buffer, queue);
>  
> -	buf->bufnum = !bufnum;
> +	buf->bufnum = bufnum;
>  
>  	list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
>  
> @@ -1270,18 +1299,7 @@ static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev,
>  	buf->state = MX2_STATE_ACTIVE;
>  
>  	phys = vb2_dma_contig_plane_dma_addr(vb, 0);
> -	if (prp->cfg.channel == 1) {
> -		writel(phys, pcdev->base_emma + PRP_DEST_RGB1_PTR + 4 * bufnum);
> -	} else {
> -		writel(phys, pcdev->base_emma +
> -				PRP_DEST_Y_PTR - 0x14 * bufnum);
> -		if (prp->cfg.out_fmt == PRP_CNTL_CH2_OUT_YUV420) {
> -			writel(phys + imgsize, pcdev->base_emma +
> -					PRP_DEST_CB_PTR - 0x14 * bufnum);
> -			writel(phys + ((5 * imgsize) / 4), pcdev->base_emma +
> -					PRP_DEST_CR_PTR - 0x14 * bufnum);
> -		}
> -	}
> +	mx27_update_emma_buf(pcdev, phys, bufnum);
>  }
>  
>  static irqreturn_t mx27_camera_emma_irq(int irq_emma, void *data)
> @@ -1433,6 +1451,7 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev)
>  
>  	INIT_LIST_HEAD(&pcdev->capture);
>  	INIT_LIST_HEAD(&pcdev->active_bufs);
> +	INIT_LIST_HEAD(&pcdev->discard);
>  	spin_lock_init(&pcdev->lock);
>  
>  	/*
> -- 
> 1.7.0.4
> 

Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/
--
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