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

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

 



On 27 January 2012 19:13, Guennadi Liakhovetski <g.liakhovetski@xxxxxx> 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@xxxxxxxxxxxxxxxxx>
>> ---
>>  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 ^^


-- 
Javier Martin
Vista Silicon S.L.
CDTUC - FASE C - Oficina S-345
Avda de los Castros s/n
39005- Santander. Cantabria. Spain
+34 942 25 32 60
www.vista-silicon.com
--
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