[v2,3/4] media i.MX27 camera: improve discard buffer handling.

Message ID 1327579472-31597-3-git-send-email-javier.martin@vista-silicon.com (mailing list archive)
State Superseded, archived
Headers

Commit Message

Javier Martin Jan. 26, 2012, 12:04 p.m. UTC
  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.

Signed-off-by: Javier Martin <javier.martin@vista-silicon.com>
---
 Changes since v1:
 - Don't allocate discard buffer on every set_fmt.

---
 drivers/media/video/mx2_camera.c |  261 +++++++++++++++++++++-----------------
 1 files changed, 144 insertions(+), 117 deletions(-)
  

Comments

Guennadi Liakhovetski Jan. 27, 2012, 6:13 p.m. UTC | #1
On Thu, 26 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.
> 
> Signed-off-by: Javier Martin <javier.martin@vista-silicon.com>
> ---
>  Changes since v1:
>  - Don't allocate discard buffer on every set_fmt.
> 
> ---
>  drivers/media/video/mx2_camera.c |  261 +++++++++++++++++++++-----------------
>  1 files changed, 144 insertions(+), 117 deletions(-)
> 
> diff --git a/drivers/media/video/mx2_camera.c b/drivers/media/video/mx2_camera.c
> index 045c018..71054ab 100644
> --- a/drivers/media/video/mx2_camera.c
> +++ b/drivers/media/video/mx2_camera.c
> @@ -237,7 +237,8 @@ struct mx2_buffer {
>  	struct list_head		queue;
>  	enum mx2_buffer_state		state;
>  
> -	int bufnum;
> +	int				bufnum;
> +	bool				discard;
>  };
>  
>  struct mx2_camera_dev {
> @@ -256,6 +257,7 @@ struct mx2_camera_dev {
>  
>  	struct list_head	capture;
>  	struct list_head	active_bufs;
> +	struct list_head	discard;
>  
>  	spinlock_t		lock;
>  
> @@ -268,6 +270,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;
> @@ -329,6 +332,30 @@ 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)
> +{
> +	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);
> +		if (prp->out_fmt == V4L2_PIX_FMT_YUV420) {
> +			u32 imgsize = pcdev->icd->user_height *
> +					pcdev->icd->user_width;
> +
> +			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);

Please fix indentation.

> +		}
> +	}
> +}
> +
>  static void mx2_camera_deactivate(struct mx2_camera_dev *pcdev)
>  {
>  	unsigned long flags;
> @@ -377,7 +404,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);
> @@ -631,7 +658,7 @@ static void mx2_videobuf_release(struct vb2_buffer *vb)
>  
>  	spin_lock_irqsave(&pcdev->lock, flags);
>  	if (mx27_camera_emma(pcdev)) {
> -		list_del_init(&buf->queue);
> +		INIT_LIST_HEAD(&buf->queue);

The buffer had to be deleted from the queue to clean up the queue. The 
"_init" was actually not needed. Now, that you do 
INIT_LIST_HEAD(&pcdev->capture); and INIT_LIST_HEAD(&pcdev->active_bufs); 
in .stop_streaming(), you don't need to do anything about your individual 
buffers. You never test list_empty(&buf->queue), so, it doesn't matter 
what they contain.

>  	} else if (cpu_is_mx25() && buf->state == MX2_STATE_ACTIVE) {
>  		if (pcdev->fb1_active == buf) {
>  			pcdev->csicr1 &= ~CSICR1_FB1_DMA_INTEN;
> @@ -647,6 +674,34 @@ static void mx2_videobuf_release(struct vb2_buffer *vb)
>  	spin_unlock_irqrestore(&pcdev->lock, flags);
>  }
>  
> +static void mx27_camera_emma_buf_init(struct soc_camera_device *icd,
> +		int bytesperline)
> +{
> +	struct soc_camera_host *ici =
> +		to_soc_camera_host(icd->parent);
> +	struct mx2_camera_dev *pcdev = ici->priv;
> +	struct mx2_fmt_cfg *prp = pcdev->emma_prp;
> +
> +	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((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.ch1_pixel,
> +			pcdev->base_emma + PRP_CH1_PIXEL_FORMAT_CNTL);
> +	} else { /* channel 2 */
> +		writel((icd->user_width << 16) | icd->user_height,
> +			pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE);
> +	}
> +
> +	/* Enable interrupts */
> +	writel(prp->cfg.irq_flags, pcdev->base_emma + PRP_INTR_CNTL);
> +}
> +
>  static int mx2_start_streaming(struct vb2_queue *q, unsigned int count)
>  {
>  	struct soc_camera_device *icd = soc_camera_from_vb2q(q);
> @@ -654,7 +709,11 @@ 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 bytesperline;
>  	int ret = 0;
>  
>  	spin_lock_irqsave(&pcdev->lock, flags);
> @@ -664,6 +723,63 @@ 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 (pcdev->discard_buffer) {
> +			dma_free_coherent(ici->v4l2_dev.dev,
> +				pcdev->discard_size, pcdev->discard_buffer,
> +				pcdev->discard_buffer_dma);

dma_free_coherent() should better go into stop_streaming.

> +
> +			pcdev->buf_discard[0].discard = true;
> +			INIT_LIST_HEAD(&pcdev->buf_discard[0].queue);

No need to init before adding.

> +			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);

Now, do I understand it right, that on the first start_streaming 
pcdev->discard_buffer == NULL, so, you don't enter here and you don't add 
discard buffers onto the discard list, and still it works? That would 
confirm my suspicion, that all thess manipulations with the discard buffer 
list are unneeded.

> +		}
> +
> +		bytesperline = soc_mbus_bytes_per_line(icd->user_width,
> +				icd->current_fmt->host_fmt);
> +		if (bytesperline < 0)
> +			return bytesperline;
> +
> +		/*
> +		 * I didn't manage to properly enable/disable the prp
> +		 * on a per frame basis during running transfers,
> +		 * thus we allocate a buffer here and use it to
> +		 * discard frames when no buffer is available.
> +		 * Feel free to work on this ;)
> +		 */
> +		pcdev->discard_size = icd->user_height * bytesperline;
> +		pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev,
> +				pcdev->discard_size, &pcdev->discard_buffer_dma,
> +				GFP_KERNEL);
> +		if (!pcdev->discard_buffer)
> +			return -ENOMEM;
> +
> +		mx27_camera_emma_buf_init(icd, bytesperline);
> +
>  		if (prp->cfg.channel == 1) {
>  			writel(PRP_CNTL_CH1EN |
>  				PRP_CNTL_CSIEN |
> @@ -711,6 +827,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);
>  
> @@ -766,63 +885,6 @@ static int mx27_camera_emma_prp_reset(struct mx2_camera_dev *pcdev)
>  	return -ETIMEDOUT;
>  }
>  
> -static void mx27_camera_emma_buf_init(struct soc_camera_device *icd,
> -		int bytesperline)
> -{
> -	struct soc_camera_host *ici =
> -		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;
> -
> -	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 */
> -	writel(prp->cfg.irq_flags, pcdev->base_emma + PRP_INTR_CNTL);
> -}
> -
>  static int mx2_camera_set_bus_param(struct soc_camera_device *icd,
>  		__u32 pixfmt)
>  {
> @@ -906,27 +968,6 @@ static int mx2_camera_set_bus_param(struct soc_camera_device *icd,
>  		ret = mx27_camera_emma_prp_reset(pcdev);
>  		if (ret)
>  			return ret;
> -
> -		if (pcdev->discard_buffer)
> -			dma_free_coherent(ici->v4l2_dev.dev,
> -				pcdev->discard_size, pcdev->discard_buffer,
> -				pcdev->discard_buffer_dma);
> -
> -		/*
> -		 * I didn't manage to properly enable/disable the prp
> -		 * on a per frame basis during running transfers,
> -		 * thus we allocate a buffer here and use it to
> -		 * discard frames when no buffer is available.
> -		 * Feel free to work on this ;)
> -		 */
> -		pcdev->discard_size = icd->user_height * bytesperline;
> -		pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev,
> -				pcdev->discard_size, &pcdev->discard_buffer_dma,
> -				GFP_KERNEL);
> -		if (!pcdev->discard_buffer)
> -			return -ENOMEM;
> -
> -		mx27_camera_emma_buf_init(icd, bytesperline);
>  	} else if (cpu_is_mx25()) {
>  		writel((bytesperline * icd->user_height) >> 2,
>  				pcdev->base_csi + CSIRXCNT);
> @@ -1174,18 +1215,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)
>  {
> -	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));

Well, I understand, this is unexpected, but punishing the user for flaky 
hardware, that has sent an interrupt at an unexpected point of time... 
Unless you never have your interrupt enabled with the empty active list. 
Also, the check

	if ((((status & (3 << 5)) == (3 << 5)) ||
		((status & (3 << 3)) == (3 << 3)))
			&& !list_empty(&pcdev->active_bufs)) {

in mx27_camera_emma_irq() can now be simplified - list_empty() isn't 
needed there any more.

> +
> +	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);
> @@ -1207,6 +1254,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));
> @@ -1219,30 +1267,19 @@ static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev,
>  
>  	pcdev->frame_count++;
>  
> -	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);
> -			}
> -		}
> +	if (list_empty(&pcdev->capture) && !list_empty(&pcdev->discard)) {

Can the discard list be empty here actually?

> +		buf = list_entry(pcdev->discard.next,
> +			struct mx2_buffer, queue);

Someone might actually begin using the list_first_entry() macro;-) Yes, 
I'll follow:-)

> +		buf->bufnum = bufnum;
> +		list_move_tail(pcdev->discard.next, &pcdev->active_bufs);
> +		mx27_update_emma_buf(pcdev, pcdev->discard_buffer_dma, bufnum);
>  		return;
>  	}
>  
>  	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);
>  

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@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
  
Javier Martin Jan. 30, 2012, 12:07 p.m. UTC | #2
On 27 January 2012 19:13, Guennadi Liakhovetski <g.liakhovetski@gmx.de> wrote:
> On Thu, 26 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.
>>
>> Signed-off-by: Javier Martin <javier.martin@vista-silicon.com>
>> ---
>>  Changes since v1:
>>  - Don't allocate discard buffer on every set_fmt.
>>
>> ---
>>  drivers/media/video/mx2_camera.c |  261 +++++++++++++++++++++-----------------
>>  1 files changed, 144 insertions(+), 117 deletions(-)
>>
>> diff --git a/drivers/media/video/mx2_camera.c b/drivers/media/video/mx2_camera.c
>> index 045c018..71054ab 100644
>> --- a/drivers/media/video/mx2_camera.c
>> +++ b/drivers/media/video/mx2_camera.c
>> @@ -237,7 +237,8 @@ struct mx2_buffer {
>>       struct list_head                queue;
>>       enum mx2_buffer_state           state;
>>
>> -     int bufnum;
>> +     int                             bufnum;
>> +     bool                            discard;
>>  };
>>
>>  struct mx2_camera_dev {
>> @@ -256,6 +257,7 @@ struct mx2_camera_dev {
>>
>>       struct list_head        capture;
>>       struct list_head        active_bufs;
>> +     struct list_head        discard;
>>
>>       spinlock_t              lock;
>>
>> @@ -268,6 +270,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;
>> @@ -329,6 +332,30 @@ 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)
>> +{
>> +     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);
>> +             if (prp->out_fmt == V4L2_PIX_FMT_YUV420) {
>> +                     u32 imgsize = pcdev->icd->user_height *
>> +                                     pcdev->icd->user_width;
>> +
>> +                     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);
>
> Please fix indentation.

OK, will do in v3.

>> +             }
>> +     }
>> +}
>> +
>>  static void mx2_camera_deactivate(struct mx2_camera_dev *pcdev)
>>  {
>>       unsigned long flags;
>> @@ -377,7 +404,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);
>> @@ -631,7 +658,7 @@ static void mx2_videobuf_release(struct vb2_buffer *vb)
>>
>>       spin_lock_irqsave(&pcdev->lock, flags);
>>       if (mx27_camera_emma(pcdev)) {
>> -             list_del_init(&buf->queue);
>> +             INIT_LIST_HEAD(&buf->queue);
>
> The buffer had to be deleted from the queue to clean up the queue. The
> "_init" was actually not needed. Now, that you do
> INIT_LIST_HEAD(&pcdev->capture); and INIT_LIST_HEAD(&pcdev->active_bufs);
> in .stop_streaming(), you don't need to do anything about your individual
> buffers. You never test list_empty(&buf->queue), so, it doesn't matter
> what they contain.

Fine, v3 will solve this.

>>       } else if (cpu_is_mx25() && buf->state == MX2_STATE_ACTIVE) {
>>               if (pcdev->fb1_active == buf) {
>>                       pcdev->csicr1 &= ~CSICR1_FB1_DMA_INTEN;
>> @@ -647,6 +674,34 @@ static void mx2_videobuf_release(struct vb2_buffer *vb)
>>       spin_unlock_irqrestore(&pcdev->lock, flags);
>>  }
>>
>> +static void mx27_camera_emma_buf_init(struct soc_camera_device *icd,
>> +             int bytesperline)
>> +{
>> +     struct soc_camera_host *ici =
>> +             to_soc_camera_host(icd->parent);
>> +     struct mx2_camera_dev *pcdev = ici->priv;
>> +     struct mx2_fmt_cfg *prp = pcdev->emma_prp;
>> +
>> +     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((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.ch1_pixel,
>> +                     pcdev->base_emma + PRP_CH1_PIXEL_FORMAT_CNTL);
>> +     } else { /* channel 2 */
>> +             writel((icd->user_width << 16) | icd->user_height,
>> +                     pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE);
>> +     }
>> +
>> +     /* Enable interrupts */
>> +     writel(prp->cfg.irq_flags, pcdev->base_emma + PRP_INTR_CNTL);
>> +}
>> +
>>  static int mx2_start_streaming(struct vb2_queue *q, unsigned int count)
>>  {
>>       struct soc_camera_device *icd = soc_camera_from_vb2q(q);
>> @@ -654,7 +709,11 @@ 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 bytesperline;
>>       int ret = 0;
>>
>>       spin_lock_irqsave(&pcdev->lock, flags);
>> @@ -664,6 +723,63 @@ 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 (pcdev->discard_buffer) {
>> +                     dma_free_coherent(ici->v4l2_dev.dev,
>> +                             pcdev->discard_size, pcdev->discard_buffer,
>> +                             pcdev->discard_buffer_dma);
>
> dma_free_coherent() should better go into stop_streaming.
>
>> +
>> +                     pcdev->buf_discard[0].discard = true;
>> +                     INIT_LIST_HEAD(&pcdev->buf_discard[0].queue);
>
> No need to init before adding.
>
>> +                     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);
>
> Now, do I understand it right, that on the first start_streaming
> pcdev->discard_buffer == NULL, so, you don't enter here and you don't add
> discard buffers onto the discard list, and still it works? That would
> confirm my suspicion, that all thess manipulations with the discard buffer
> list are unneeded.

Not actually, it is true that this is clearly a mistake and I'm glad
you noticed. However, the fact that it worked is because I never had a
frame lost while testing. This discard_buffer code is still needed. I
am currently preparing a v3 version of the patch where I've made sure
that when a frame is lost discard buffers are now use properly.

>> +             }
>> +
>> +             bytesperline = soc_mbus_bytes_per_line(icd->user_width,
>> +                             icd->current_fmt->host_fmt);
>> +             if (bytesperline < 0)
>> +                     return bytesperline;
>> +
>> +             /*
>> +              * I didn't manage to properly enable/disable the prp
>> +              * on a per frame basis during running transfers,
>> +              * thus we allocate a buffer here and use it to
>> +              * discard frames when no buffer is available.
>> +              * Feel free to work on this ;)
>> +              */
>> +             pcdev->discard_size = icd->user_height * bytesperline;
>> +             pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev,
>> +                             pcdev->discard_size, &pcdev->discard_buffer_dma,
>> +                             GFP_KERNEL);
>> +             if (!pcdev->discard_buffer)
>> +                     return -ENOMEM;
>> +
>> +             mx27_camera_emma_buf_init(icd, bytesperline);
>> +
>>               if (prp->cfg.channel == 1) {
>>                       writel(PRP_CNTL_CH1EN |
>>                               PRP_CNTL_CSIEN |
>> @@ -711,6 +827,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);
>>
>> @@ -766,63 +885,6 @@ static int mx27_camera_emma_prp_reset(struct mx2_camera_dev *pcdev)
>>       return -ETIMEDOUT;
>>  }
>>
>> -static void mx27_camera_emma_buf_init(struct soc_camera_device *icd,
>> -             int bytesperline)
>> -{
>> -     struct soc_camera_host *ici =
>> -             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;
>> -
>> -     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 */
>> -     writel(prp->cfg.irq_flags, pcdev->base_emma + PRP_INTR_CNTL);
>> -}
>> -
>>  static int mx2_camera_set_bus_param(struct soc_camera_device *icd,
>>               __u32 pixfmt)
>>  {
>> @@ -906,27 +968,6 @@ static int mx2_camera_set_bus_param(struct soc_camera_device *icd,
>>               ret = mx27_camera_emma_prp_reset(pcdev);
>>               if (ret)
>>                       return ret;
>> -
>> -             if (pcdev->discard_buffer)
>> -                     dma_free_coherent(ici->v4l2_dev.dev,
>> -                             pcdev->discard_size, pcdev->discard_buffer,
>> -                             pcdev->discard_buffer_dma);
>> -
>> -             /*
>> -              * I didn't manage to properly enable/disable the prp
>> -              * on a per frame basis during running transfers,
>> -              * thus we allocate a buffer here and use it to
>> -              * discard frames when no buffer is available.
>> -              * Feel free to work on this ;)
>> -              */
>> -             pcdev->discard_size = icd->user_height * bytesperline;
>> -             pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev,
>> -                             pcdev->discard_size, &pcdev->discard_buffer_dma,
>> -                             GFP_KERNEL);
>> -             if (!pcdev->discard_buffer)
>> -                     return -ENOMEM;
>> -
>> -             mx27_camera_emma_buf_init(icd, bytesperline);
>>       } else if (cpu_is_mx25()) {
>>               writel((bytesperline * icd->user_height) >> 2,
>>                               pcdev->base_csi + CSIRXCNT);
>> @@ -1174,18 +1215,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)
>>  {
>> -     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));
>
> Well, I understand, this is unexpected, but punishing the user for flaky
> hardware, that has sent an interrupt at an unexpected point of time...
> Unless you never have your interrupt enabled with the empty active list.
> Also, the check
>
>        if ((((status & (3 << 5)) == (3 << 5)) ||
>                ((status & (3 << 3)) == (3 << 3)))
>                        && !list_empty(&pcdev->active_bufs)) {
>
> in mx27_camera_emma_irq() can now be simplified - list_empty() isn't
> needed there any more.

OK, will fix it.

>> +
>> +     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);
>> @@ -1207,6 +1254,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));
>> @@ -1219,30 +1267,19 @@ static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev,
>>
>>       pcdev->frame_count++;
>>
>> -     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);
>> -                     }
>> -             }
>> +     if (list_empty(&pcdev->capture) && !list_empty(&pcdev->discard)) {
>
> Can the discard list be empty here actually?
>
>> +             buf = list_entry(pcdev->discard.next,
>> +                     struct mx2_buffer, queue);
>
> Someone might actually begin using the list_first_entry() macro;-) Yes,
> I'll follow:-)

If you accept my offer of a separate cleanup series I will gladly
include this ^^
  

Patch

diff --git a/drivers/media/video/mx2_camera.c b/drivers/media/video/mx2_camera.c
index 045c018..71054ab 100644
--- a/drivers/media/video/mx2_camera.c
+++ b/drivers/media/video/mx2_camera.c
@@ -237,7 +237,8 @@  struct mx2_buffer {
 	struct list_head		queue;
 	enum mx2_buffer_state		state;
 
-	int bufnum;
+	int				bufnum;
+	bool				discard;
 };
 
 struct mx2_camera_dev {
@@ -256,6 +257,7 @@  struct mx2_camera_dev {
 
 	struct list_head	capture;
 	struct list_head	active_bufs;
+	struct list_head	discard;
 
 	spinlock_t		lock;
 
@@ -268,6 +270,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;
@@ -329,6 +332,30 @@  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)
+{
+	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);
+		if (prp->out_fmt == V4L2_PIX_FMT_YUV420) {
+			u32 imgsize = pcdev->icd->user_height *
+					pcdev->icd->user_width;
+
+			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;
@@ -377,7 +404,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);
@@ -631,7 +658,7 @@  static void mx2_videobuf_release(struct vb2_buffer *vb)
 
 	spin_lock_irqsave(&pcdev->lock, flags);
 	if (mx27_camera_emma(pcdev)) {
-		list_del_init(&buf->queue);
+		INIT_LIST_HEAD(&buf->queue);
 	} else if (cpu_is_mx25() && buf->state == MX2_STATE_ACTIVE) {
 		if (pcdev->fb1_active == buf) {
 			pcdev->csicr1 &= ~CSICR1_FB1_DMA_INTEN;
@@ -647,6 +674,34 @@  static void mx2_videobuf_release(struct vb2_buffer *vb)
 	spin_unlock_irqrestore(&pcdev->lock, flags);
 }
 
+static void mx27_camera_emma_buf_init(struct soc_camera_device *icd,
+		int bytesperline)
+{
+	struct soc_camera_host *ici =
+		to_soc_camera_host(icd->parent);
+	struct mx2_camera_dev *pcdev = ici->priv;
+	struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+
+	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((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.ch1_pixel,
+			pcdev->base_emma + PRP_CH1_PIXEL_FORMAT_CNTL);
+	} else { /* channel 2 */
+		writel((icd->user_width << 16) | icd->user_height,
+			pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE);
+	}
+
+	/* Enable interrupts */
+	writel(prp->cfg.irq_flags, pcdev->base_emma + PRP_INTR_CNTL);
+}
+
 static int mx2_start_streaming(struct vb2_queue *q, unsigned int count)
 {
 	struct soc_camera_device *icd = soc_camera_from_vb2q(q);
@@ -654,7 +709,11 @@  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 bytesperline;
 	int ret = 0;
 
 	spin_lock_irqsave(&pcdev->lock, flags);
@@ -664,6 +723,63 @@  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 (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);
+		}
+
+		bytesperline = soc_mbus_bytes_per_line(icd->user_width,
+				icd->current_fmt->host_fmt);
+		if (bytesperline < 0)
+			return bytesperline;
+
+		/*
+		 * I didn't manage to properly enable/disable the prp
+		 * on a per frame basis during running transfers,
+		 * thus we allocate a buffer here and use it to
+		 * discard frames when no buffer is available.
+		 * Feel free to work on this ;)
+		 */
+		pcdev->discard_size = icd->user_height * bytesperline;
+		pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev,
+				pcdev->discard_size, &pcdev->discard_buffer_dma,
+				GFP_KERNEL);
+		if (!pcdev->discard_buffer)
+			return -ENOMEM;
+
+		mx27_camera_emma_buf_init(icd, bytesperline);
+
 		if (prp->cfg.channel == 1) {
 			writel(PRP_CNTL_CH1EN |
 				PRP_CNTL_CSIEN |
@@ -711,6 +827,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);
 
@@ -766,63 +885,6 @@  static int mx27_camera_emma_prp_reset(struct mx2_camera_dev *pcdev)
 	return -ETIMEDOUT;
 }
 
-static void mx27_camera_emma_buf_init(struct soc_camera_device *icd,
-		int bytesperline)
-{
-	struct soc_camera_host *ici =
-		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;
-
-	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 */
-	writel(prp->cfg.irq_flags, pcdev->base_emma + PRP_INTR_CNTL);
-}
-
 static int mx2_camera_set_bus_param(struct soc_camera_device *icd,
 		__u32 pixfmt)
 {
@@ -906,27 +968,6 @@  static int mx2_camera_set_bus_param(struct soc_camera_device *icd,
 		ret = mx27_camera_emma_prp_reset(pcdev);
 		if (ret)
 			return ret;
-
-		if (pcdev->discard_buffer)
-			dma_free_coherent(ici->v4l2_dev.dev,
-				pcdev->discard_size, pcdev->discard_buffer,
-				pcdev->discard_buffer_dma);
-
-		/*
-		 * I didn't manage to properly enable/disable the prp
-		 * on a per frame basis during running transfers,
-		 * thus we allocate a buffer here and use it to
-		 * discard frames when no buffer is available.
-		 * Feel free to work on this ;)
-		 */
-		pcdev->discard_size = icd->user_height * bytesperline;
-		pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev,
-				pcdev->discard_size, &pcdev->discard_buffer_dma,
-				GFP_KERNEL);
-		if (!pcdev->discard_buffer)
-			return -ENOMEM;
-
-		mx27_camera_emma_buf_init(icd, bytesperline);
 	} else if (cpu_is_mx25()) {
 		writel((bytesperline * icd->user_height) >> 2,
 				pcdev->base_csi + CSIRXCNT);
@@ -1174,18 +1215,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)
 {
-	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);
@@ -1207,6 +1254,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));
@@ -1219,30 +1267,19 @@  static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev,
 
 	pcdev->frame_count++;
 
-	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);
-			}
-		}
+	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;
 	}
 
 	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);
 
@@ -1250,18 +1287,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)
@@ -1413,6 +1439,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);
 
 	/*