Hi, Le mercredi 24 juillet 2024 à 02:19 +0200, Marek Vasut a écrit : > Introduce dedicated memory-to-memory IPUv3 VDI deinterlacer driver. > Currently the IPUv3 can operate VDI in DIRECT mode, from sensor to > memory. This only works for single stream, that is, one input from > one camera is deinterlaced on the fly with a helper buffer in DRAM > and the result is written into memory. > > The i.MX6Q/QP does support up to four analog cameras via two IPUv3 > instances, each containing one VDI deinterlacer block. In order to > deinterlace all four streams from all four analog cameras live, it > is necessary to operate VDI in INDIRECT mode, where the interlaced > streams are written to buffers in memory, and then deinterlaced in > memory using VDI in INDIRECT memory-to-memory mode. > > This driver also makes use of the IDMAC->VDI->IC->IDMAC data path > to provide pixel format conversion from input YUV formats to both > output YUV or RGB formats. The later is useful in case the data > are imported into the GPU, which on this platform cannot directly > sample YUV buffers. > > This is derived from previous work by Steve Longerbeam and from the > IPUv3 CSC Scaler mem2mem driver. > > Signed-off-by: Marek Vasut <marex@xxxxxxx> > --- > Cc: Daniel Vetter <daniel@xxxxxxxx> > Cc: David Airlie <airlied@xxxxxxxxx> > Cc: Fabio Estevam <festevam@xxxxxxxxx> > Cc: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx> > Cc: Helge Deller <deller@xxxxxx> > Cc: Mauro Carvalho Chehab <mchehab@xxxxxxxxxx> > Cc: Pengutronix Kernel Team <kernel@xxxxxxxxxxxxxx> > Cc: Philipp Zabel <p.zabel@xxxxxxxxxxxxxx> > Cc: Sascha Hauer <s.hauer@xxxxxxxxxxxxxx> > Cc: Shawn Guo <shawnguo@xxxxxxxxxx> > Cc: Steve Longerbeam <slongerbeam@xxxxxxxxx> > Cc: dri-devel@xxxxxxxxxxxxxxxxxxxxx > Cc: imx@xxxxxxxxxxxxxxx > Cc: linux-arm-kernel@xxxxxxxxxxxxxxxxxxx > Cc: linux-fbdev@xxxxxxxxxxxxxxx > Cc: linux-media@xxxxxxxxxxxxxxx > Cc: linux-staging@xxxxxxxxxxxxxxx > --- > V2: - Add complementary imx_media_mem2mem_vdic_uninit() > - Drop uninitiaized ret from ipu_mem2mem_vdic_device_run() > - Drop duplicate nbuffers assignment in ipu_mem2mem_vdic_queue_setup() > - Fix %u formatting string in ipu_mem2mem_vdic_queue_setup() > - Drop devm_*free from ipu_mem2mem_vdic_get_ipu_resources() fail path > and ipu_mem2mem_vdic_put_ipu_resources() > - Add missing video_device_release() > --- > drivers/staging/media/imx/Makefile | 2 +- > drivers/staging/media/imx/imx-media-dev.c | 55 + > .../media/imx/imx-media-mem2mem-vdic.c | 997 ++++++++++++++++++ > drivers/staging/media/imx/imx-media.h | 10 + > 4 files changed, 1063 insertions(+), 1 deletion(-) > create mode 100644 drivers/staging/media/imx/imx-media-mem2mem-vdic.c > > diff --git a/drivers/staging/media/imx/Makefile b/drivers/staging/media/imx/Makefile > index 330e0825f506b..0cad87123b590 100644 > --- a/drivers/staging/media/imx/Makefile > +++ b/drivers/staging/media/imx/Makefile > @@ -4,7 +4,7 @@ imx-media-common-objs := imx-media-capture.o imx-media-dev-common.o \ > > imx6-media-objs := imx-media-dev.o imx-media-internal-sd.o \ > imx-ic-common.o imx-ic-prp.o imx-ic-prpencvf.o imx-media-vdic.o \ > - imx-media-csc-scaler.o > + imx-media-mem2mem-vdic.o imx-media-csc-scaler.o > > imx6-media-csi-objs := imx-media-csi.o imx-media-fim.o > > diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c > index be54dca11465d..a841fdb4c2394 100644 > --- a/drivers/staging/media/imx/imx-media-dev.c > +++ b/drivers/staging/media/imx/imx-media-dev.c > @@ -57,7 +57,52 @@ 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 uninit_vdi0; > + } > + } > + > ret = imx_media_csc_scaler_device_register(imxmd->m2m_vdev); > + if (ret) > + goto uninit_vdi1; > + > + 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; > + > +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; > +uninit_vdi1: > + if (imxmd->ipu[1]) > + imx_media_mem2mem_vdic_uninit(imxmd->m2m_vdic[1]); > +uninit_vdi0: > + imx_media_mem2mem_vdic_uninit(imxmd->m2m_vdic[0]); > unlock: > mutex_unlock(&imxmd->mutex); > return ret; > @@ -108,6 +153,16 @@ static void imx_media_remove(struct platform_device *pdev) > > v4l2_info(&imxmd->v4l2_dev, "Removing imx-media\n"); > > + if (imxmd->m2m_vdic[1]) { /* MX6Q/QP only */ > + imx_media_mem2mem_vdic_unregister(imxmd->m2m_vdic[1]); > + imxmd->m2m_vdic[1] = NULL; > + } > + > + if (imxmd->m2m_vdic[0]) { > + imx_media_mem2mem_vdic_unregister(imxmd->m2m_vdic[0]); > + imxmd->m2m_vdic[0] = NULL; > + } > + > if (imxmd->m2m_vdev) { > imx_media_csc_scaler_device_unregister(imxmd->m2m_vdev); > imxmd->m2m_vdev = NULL; > diff --git a/drivers/staging/media/imx/imx-media-mem2mem-vdic.c b/drivers/staging/media/imx/imx-media-mem2mem-vdic.c > new file mode 100644 > index 0000000000000..71c6c023d2bf8 > --- /dev/null > +++ b/drivers/staging/media/imx/imx-media-mem2mem-vdic.c > @@ -0,0 +1,997 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > +/* > + * i.MX VDIC mem2mem de-interlace driver > + * > + * Copyright (c) 2024 Marek Vasut <marex@xxxxxxx> > + * > + * Based on previous VDIC mem2mem work by Steve Longerbeam that is: > + * Copyright (c) 2018 Mentor Graphics Inc. > + */ > + > +#include <linux/delay.h> > +#include <linux/fs.h> > +#include <linux/module.h> > +#include <linux/sched.h> > +#include <linux/slab.h> > +#include <linux/version.h> > + > +#include <media/media-device.h> > +#include <media/v4l2-ctrls.h> > +#include <media/v4l2-device.h> > +#include <media/v4l2-event.h> > +#include <media/v4l2-ioctl.h> > +#include <media/v4l2-mem2mem.h> > +#include <media/videobuf2-dma-contig.h> > + > +#include "imx-media.h" > + > +#define fh_to_ctx(__fh) container_of(__fh, struct ipu_mem2mem_vdic_ctx, fh) > + > +#define to_mem2mem_priv(v) container_of(v, struct ipu_mem2mem_vdic_priv, vdev) > + > +enum { > + V4L2_M2M_SRC = 0, > + V4L2_M2M_DST = 1, > +}; > + > +struct ipu_mem2mem_vdic_ctx; > + > +struct ipu_mem2mem_vdic_priv { > + struct imx_media_video_dev vdev; > + struct imx_media_dev *md; > + struct device *dev; > + struct ipu_soc *ipu_dev; > + int ipu_id; > + > + struct v4l2_m2m_dev *m2m_dev; > + struct mutex mutex; /* mem2mem device mutex */ > + > + /* VDI resources */ > + struct ipu_vdi *vdi; > + struct ipu_ic *ic; > + struct ipuv3_channel *vdi_in_ch_p; > + struct ipuv3_channel *vdi_in_ch; > + struct ipuv3_channel *vdi_in_ch_n; > + struct ipuv3_channel *vdi_out_ch; > + int eof_irq; > + int nfb4eof_irq; > + spinlock_t irqlock; /* protect eof_irq handler */ > + > + atomic_t stream_count; > + > + struct ipu_mem2mem_vdic_ctx *curr_ctx; > + > + struct v4l2_pix_format fmt[2]; > +}; > + > +struct ipu_mem2mem_vdic_ctx { > + struct ipu_mem2mem_vdic_priv *priv; > + struct v4l2_fh fh; > + unsigned int sequence; > + struct vb2_v4l2_buffer *prev_buf; > + struct vb2_v4l2_buffer *curr_buf; > +}; > + > +static struct v4l2_pix_format * > +ipu_mem2mem_vdic_get_format(struct ipu_mem2mem_vdic_priv *priv, > + enum v4l2_buf_type type) > +{ > + return &priv->fmt[V4L2_TYPE_IS_OUTPUT(type) ? V4L2_M2M_SRC : V4L2_M2M_DST]; > +}