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