On Sat, Jul 13, 2024 at 05:48:02PM +0200, Marek Vasut wrote: > diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c > index be54dca11465d..b75eec4513eab 100644 > --- a/drivers/staging/media/imx/imx-media-dev.c > +++ b/drivers/staging/media/imx/imx-media-dev.c > @@ -57,7 +57,47 @@ static int imx6_media_probe_complete(struct v4l2_async_notifier *notifier) > goto unlock; > } > > + imxmd->m2m_vdic[0] = imx_media_mem2mem_vdic_init(imxmd, 0); > + if (IS_ERR(imxmd->m2m_vdic[0])) { > + ret = PTR_ERR(imxmd->m2m_vdic[0]); > + imxmd->m2m_vdic[0] = NULL; > + goto unlock; > + } > + > + /* MX6S/DL has one IPUv3, init second VDI only on MX6Q/QP */ > + if (imxmd->ipu[1]) { > + imxmd->m2m_vdic[1] = imx_media_mem2mem_vdic_init(imxmd, 1); > + if (IS_ERR(imxmd->m2m_vdic[1])) { > + ret = PTR_ERR(imxmd->m2m_vdic[1]); > + imxmd->m2m_vdic[1] = NULL; > + goto unlock; There should be some clean up on this error path. Ideally imx_media_mem2mem_vdic_init() would have a matching uninit() function we could call. > + } > + } > + > ret = imx_media_csc_scaler_device_register(imxmd->m2m_vdev); > + if (ret) > + goto unlock; > + > + ret = imx_media_mem2mem_vdic_register(imxmd->m2m_vdic[0]); > + if (ret) > + goto unreg_csc; > + > + /* MX6S/DL has one IPUv3, init second VDI only on MX6Q/QP */ > + if (imxmd->ipu[1]) { > + ret = imx_media_mem2mem_vdic_register(imxmd->m2m_vdic[1]); > + if (ret) > + goto unreg_vdic; > + } > + > + mutex_unlock(&imxmd->mutex); > + return ret; return 0; > + > +unreg_vdic: > + imx_media_mem2mem_vdic_unregister(imxmd->m2m_vdic[0]); > + imxmd->m2m_vdic[0] = NULL; > +unreg_csc: > + imx_media_csc_scaler_device_unregister(imxmd->m2m_vdev); > + imxmd->m2m_vdev = NULL; > unlock: > mutex_unlock(&imxmd->mutex); > return ret; [ snip ] > +static void ipu_mem2mem_vdic_device_run(void *_ctx) > +{ > + struct ipu_mem2mem_vdic_ctx *ctx = _ctx; > + struct ipu_mem2mem_vdic_priv *priv = ctx->priv; > + struct vb2_v4l2_buffer *curr_buf, *dst_buf; > + dma_addr_t prev_phys, curr_phys, out_phys; > + struct v4l2_pix_format *infmt; > + u32 phys_offset = 0; > + unsigned long flags; > + int ret; > + > + infmt = ipu_mem2mem_vdic_get_format(priv, V4L2_BUF_TYPE_VIDEO_OUTPUT); > + if (V4L2_FIELD_IS_SEQUENTIAL(infmt->field)) > + phys_offset = infmt->sizeimage / 2; > + else if (V4L2_FIELD_IS_INTERLACED(infmt->field)) > + phys_offset = infmt->bytesperline; > + else > + dev_err(priv->dev, "Invalid field %d\n", infmt->field); > + > + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); > + out_phys = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); > + > + curr_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); > + if (!curr_buf) { > + dev_err(priv->dev, "Not enough buffers %d\n", ret); ret is uninitialized. Just delete it. > + return; > + } > + > + spin_lock_irqsave(&priv->irqlock, flags); > + > + if (ctx->curr_buf) { > + ctx->prev_buf = ctx->curr_buf; > + ctx->curr_buf = curr_buf; > + } else { > + ctx->prev_buf = curr_buf; > + ctx->curr_buf = curr_buf; > + dev_warn(priv->dev, "Single-buffer mode, fix your userspace\n"); > + } > + > + prev_phys = vb2_dma_contig_plane_dma_addr(&ctx->prev_buf->vb2_buf, 0); > + curr_phys = vb2_dma_contig_plane_dma_addr(&ctx->curr_buf->vb2_buf, 0); > + > + priv->curr_ctx = ctx; > + spin_unlock_irqrestore(&priv->irqlock, flags); > + > + ipu_cpmem_set_buffer(priv->vdi_out_ch, 0, out_phys); > + ipu_cpmem_set_buffer(priv->vdi_in_ch_p, 0, prev_phys + phys_offset); > + ipu_cpmem_set_buffer(priv->vdi_in_ch, 0, curr_phys); > + ipu_cpmem_set_buffer(priv->vdi_in_ch_n, 0, curr_phys + phys_offset); > + > + /* No double buffering, always pick buffer 0 */ > + ipu_idmac_select_buffer(priv->vdi_out_ch, 0); > + ipu_idmac_select_buffer(priv->vdi_in_ch_p, 0); > + ipu_idmac_select_buffer(priv->vdi_in_ch, 0); > + ipu_idmac_select_buffer(priv->vdi_in_ch_n, 0); > + > + /* Enable the channels */ > + ipu_idmac_enable_channel(priv->vdi_out_ch); > + ipu_idmac_enable_channel(priv->vdi_in_ch_p); > + ipu_idmac_enable_channel(priv->vdi_in_ch); > + ipu_idmac_enable_channel(priv->vdi_in_ch_n); > +} [ snip ] > +static int ipu_mem2mem_vdic_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, > + unsigned int *nplanes, unsigned int sizes[], > + struct device *alloc_devs[]) > +{ > + struct ipu_mem2mem_vdic_ctx *ctx = vb2_get_drv_priv(vq); > + struct ipu_mem2mem_vdic_priv *priv = ctx->priv; > + struct v4l2_pix_format *fmt = ipu_mem2mem_vdic_get_format(priv, vq->type); > + unsigned int count = *nbuffers; > + > + *nbuffers = count; count and *nbuffers are the same already. Delete this statement. > + > + if (*nplanes) > + return sizes[0] < fmt->sizeimage ? -EINVAL : 0; > + > + *nplanes = 1; > + sizes[0] = fmt->sizeimage; > + > + dev_dbg(ctx->priv->dev, "get %d buffer(s) of size %d each.\n", ^^ %u for unsigned int. > + count, fmt->sizeimage); > + > + return 0; > +} [ snip ] > +static int ipu_mem2mem_vdic_get_ipu_resources(struct ipu_mem2mem_vdic_priv *priv, > + struct video_device *vfd) > +{ > + char *nfbname, *eofname; > + int ret; > + > + nfbname = devm_kasprintf(priv->dev, GFP_KERNEL, "%s_nfb4eof:%u", > + vfd->name, priv->ipu_id); > + if (!nfbname) > + return -ENOMEM; > + > + eofname = devm_kasprintf(priv->dev, GFP_KERNEL, "%s_eof:%u", > + vfd->name, priv->ipu_id); > + if (!eofname) > + goto err_eof; > + > + priv->vdi = ipu_vdi_get(priv->ipu_dev); > + if (IS_ERR(priv->vdi)) { > + ret = PTR_ERR(priv->vdi); > + goto err_vdi; > + } > + > + priv->ic = ipu_ic_get(priv->ipu_dev, IC_TASK_VIEWFINDER); > + if (IS_ERR(priv->ic)) { > + ret = PTR_ERR(priv->ic); > + goto err_ic; > + } > + > + priv->vdi_in_ch_p = ipu_idmac_get(priv->ipu_dev, > + IPUV3_CHANNEL_MEM_VDI_PREV); > + if (IS_ERR(priv->vdi_in_ch_p)) { > + ret = PTR_ERR(priv->vdi_in_ch_p); > + goto err_prev; > + } > + > + priv->vdi_in_ch = ipu_idmac_get(priv->ipu_dev, > + IPUV3_CHANNEL_MEM_VDI_CUR); > + if (IS_ERR(priv->vdi_in_ch)) { > + ret = PTR_ERR(priv->vdi_in_ch); > + goto err_curr; > + } > + > + priv->vdi_in_ch_n = ipu_idmac_get(priv->ipu_dev, > + IPUV3_CHANNEL_MEM_VDI_NEXT); > + if (IS_ERR(priv->vdi_in_ch_n)) { > + ret = PTR_ERR(priv->vdi_in_ch_n); > + goto err_next; > + } > + > + priv->vdi_out_ch = ipu_idmac_get(priv->ipu_dev, > + IPUV3_CHANNEL_IC_PRP_VF_MEM); > + if (IS_ERR(priv->vdi_out_ch)) { > + ret = PTR_ERR(priv->vdi_out_ch); > + goto err_out; > + } > + > + priv->nfb4eof_irq = ipu_idmac_channel_irq(priv->ipu_dev, > + priv->vdi_out_ch, > + IPU_IRQ_NFB4EOF); > + ret = devm_request_irq(priv->dev, priv->nfb4eof_irq, > + ipu_mem2mem_vdic_nfb4eof_interrupt, 0, > + nfbname, priv); > + if (ret) > + goto err_irq_nfb4eof; > + > + priv->eof_irq = ipu_idmac_channel_irq(priv->ipu_dev, > + priv->vdi_out_ch, > + IPU_IRQ_EOF); > + ret = devm_request_irq(priv->dev, priv->eof_irq, > + ipu_mem2mem_vdic_eof_interrupt, 0, > + eofname, priv); > + if (ret) > + goto err_irq_eof; > + > + /* > + * Enable PRG, without PRG clock enabled (CCGR6:prg_clk_enable[0] > + * and CCGR6:prg_clk_enable[1]), the VDI does not produce any > + * interrupts at all. > + */ > + if (ipu_prg_present(priv->ipu_dev)) > + ipu_prg_enable(priv->ipu_dev); > + > + return 0; > + > +err_irq_eof: > + devm_free_irq(priv->dev, priv->nfb4eof_irq, priv); ^^^^ > +err_irq_nfb4eof: > + ipu_idmac_put(priv->vdi_out_ch); > +err_out: > + ipu_idmac_put(priv->vdi_in_ch_n); > +err_next: > + ipu_idmac_put(priv->vdi_in_ch); > +err_curr: > + ipu_idmac_put(priv->vdi_in_ch_p); > +err_prev: > + ipu_ic_put(priv->ic); > +err_ic: > + ipu_vdi_put(priv->vdi); > +err_vdi: > + devm_kfree(priv->dev, eofname); ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ > +err_eof: > + devm_kfree(priv->dev, nfbname); ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Any time we call devm_kfree() it's a red flag. Sometimes it makes sense but I haven't looked at it closely enough to see how it makes sense here. Is it an ordering issue where we had to do devm_free_irq() and then we just freed oefname and nfbname for consistency and because why not? > + return ret; > +} > + > +static void ipu_mem2mem_vdic_put_ipu_resources(struct ipu_mem2mem_vdic_priv *priv) > +{ > + devm_free_irq(priv->dev, priv->eof_irq, priv); > + devm_free_irq(priv->dev, priv->nfb4eof_irq, priv); > + ipu_idmac_put(priv->vdi_out_ch); > + ipu_idmac_put(priv->vdi_in_ch_n); > + ipu_idmac_put(priv->vdi_in_ch); > + ipu_idmac_put(priv->vdi_in_ch_p); > + ipu_ic_put(priv->ic); > + ipu_vdi_put(priv->vdi); > +} > + > +int imx_media_mem2mem_vdic_register(struct imx_media_video_dev *vdev) > +{ > + struct ipu_mem2mem_vdic_priv *priv = to_mem2mem_priv(vdev); > + struct video_device *vfd = vdev->vfd; > + int ret; > + > + vfd->v4l2_dev = &priv->md->v4l2_dev; > + > + ret = ipu_mem2mem_vdic_get_ipu_resources(priv, vfd); > + if (ret) { > + v4l2_err(vfd->v4l2_dev, "Failed to get VDIC resources (%d)\n", ret); > + return ret; > + } > + > + ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); > + if (ret) { > + v4l2_err(vfd->v4l2_dev, "Failed to register video device\n"); Probably should call ipu_mem2mem_vdic_put_ipu_resources() on this error path? > + return ret; > + } > + > + v4l2_info(vfd->v4l2_dev, "Registered %s as /dev/%s\n", vfd->name, > + video_device_node_name(vfd)); > + > + return 0; > +} > + > +void imx_media_mem2mem_vdic_unregister(struct imx_media_video_dev *vdev) > +{ > + struct ipu_mem2mem_vdic_priv *priv = to_mem2mem_priv(vdev); > + struct video_device *vfd = priv->vdev.vfd; > + > + video_unregister_device(vfd); > + > + ipu_mem2mem_vdic_put_ipu_resources(priv); > +} > + > +struct imx_media_video_dev * > +imx_media_mem2mem_vdic_init(struct imx_media_dev *md, int ipu_id) > +{ > + struct ipu_mem2mem_vdic_priv *priv; > + struct video_device *vfd; > + int ret; > + > + priv = kzalloc(sizeof(*priv), GFP_KERNEL); > + if (!priv) > + return ERR_PTR(-ENOMEM); > + > + priv->md = md; > + priv->ipu_id = ipu_id; > + priv->ipu_dev = md->ipu[ipu_id]; > + priv->dev = md->md.dev; > + > + mutex_init(&priv->mutex); > + > + vfd = video_device_alloc(); > + if (!vfd) { > + ret = -ENOMEM; > + goto err_vfd; > + } > + > + *vfd = mem2mem_template; > + vfd->lock = &priv->mutex; > + priv->vdev.vfd = vfd; > + > + INIT_LIST_HEAD(&priv->vdev.list); > + spin_lock_init(&priv->irqlock); > + atomic_set(&priv->stream_count, 0); > + > + video_set_drvdata(vfd, priv); > + > + priv->m2m_dev = v4l2_m2m_init(&m2m_ops); > + if (IS_ERR(priv->m2m_dev)) { > + ret = PTR_ERR(priv->m2m_dev); > + v4l2_err(&md->v4l2_dev, "Failed to init mem2mem device: %d\n", > + ret); > + goto err_m2m; > + } > + > + /* Reset formats */ > + priv->fmt[V4L2_M2M_SRC] = ipu_mem2mem_vdic_default; > + priv->fmt[V4L2_M2M_SRC].pixelformat = V4L2_PIX_FMT_YUV420; > + priv->fmt[V4L2_M2M_SRC].field = V4L2_FIELD_SEQ_TB; > + priv->fmt[V4L2_M2M_SRC].bytesperline = DEFAULT_WIDTH; > + priv->fmt[V4L2_M2M_SRC].sizeimage = DEFAULT_WIDTH * DEFAULT_HEIGHT * 3 / 2; > + > + priv->fmt[V4L2_M2M_DST] = ipu_mem2mem_vdic_default; > + priv->fmt[V4L2_M2M_DST].pixelformat = V4L2_PIX_FMT_RGB565; > + priv->fmt[V4L2_M2M_DST].field = V4L2_FIELD_NONE; > + priv->fmt[V4L2_M2M_DST].bytesperline = DEFAULT_WIDTH * 2; > + priv->fmt[V4L2_M2M_DST].sizeimage = DEFAULT_WIDTH * DEFAULT_HEIGHT * 2; > + > + return &priv->vdev; > + > +err_m2m: > + video_set_drvdata(vfd, NULL); video_device_release(vfd) regards, dan carpenter > +err_vfd: > + kfree(priv); > + return ERR_PTR(ret); > +}