Linux Media Controller development
 help / color / mirror / Atom feed
From: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
To: "Niklas Söderlund" <niklas.soderlund+renesas@ragnatech.se>
Cc: Jacopo Mondi <jacopo.mondi@ideasonboard.com>,
	 Jai Luthra <jai.luthra+renesas@ideasonboard.com>,
	Mauro Carvalho Chehab <mchehab@kernel.org>,
	 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>,
	Laurent Pinchart <laurent.pinchart@ideasonboard.com>,
	 linux-media@vger.kernel.org, linux-renesas-soc@vger.kernel.org,
	linux-kernel@vger.kernel.org
Subject: Re: [v8 03/14] media: rcar-isp: Add support for ISPCORE
Date: Wed, 3 Jun 2026 11:11:47 +0200	[thread overview]
Message-ID: <ah_qnWSHVx6oN25g@zed> (raw)
In-Reply-To: <20260516201636.GW332351@ragnatech.se>

Hi Niklas

On Sat, May 16, 2026 at 10:16:36PM +0200, Niklas Söderlund wrote:
> Hi Jacopo,
>
> Thanks for your review!
>
> On 2026-05-06 17:02:32 +0200, Jacopo Mondi wrote:
> > Hi Niklas
> >
> > On Mon, May 04, 2026 at 03:05:45AM +0200, Niklas Söderlund wrote:
> > > The Renesas R-Car ISP block consists of two different IP blocks, one
> > > CSI-2 Channel Selector (CSISP) and one traditional ISP for image
> > > operation (ISPCORE). The R-Car ISP driver currently supports the CSISP
> > > functionality as part of the video capture pipeline, this change adds
> > > support for the ISPCORE functionality.
> > >
> > > The ISPCORE functionality is further split in two parts, a Renesas
> > > specific part and a Dream Chip Real-time Pixel Processor IP part
> > > (RPPX1). The Renesas part deals with I/O to/from the block while the
> > > RPPX1 part deals with the actual ISP functions.
> > >
> > > The RPPX1 functionality is implemented in a support framework (DCT
> > > RPPX1) as this block can be used by different vendors or setups.  This
> > > change deals with the Renesas part of exposing the V4L2 elements needed
> > > for a user-space interface to the RPPX1 and deals with the DMA to/from
> > > the RPP block. It also facilitates the user-space V4L2 API to allow
> > > configuring the RPPX1 using the DCT RPPX1 support framework.
> > >
> > > The functionality exposed are one input video device where RAW bayer
> > > frames can be queued for processing, one output video device where the
> > > debayerd image can be read as either ABGR32 or NV16M format. Further
> > > more a video device to queue the image processing parameters to
> > > configure the RPPX1 IPS as well as a video device to read statistics
> > > about the processed image is available.
> > >
> > > There is no change in the operation of the CSISP functionality.
> > >
> > > Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
> > > Signed-off-by: Jai Luthra <jai.luthra+renesas@ideasonboard.com>
> > > ---
> > > * Changes since v7
> > > - Fix kdoc tag.
> > > - Update to init ISP device pointer.
> > >
> > > * Changes since v5
> > > - Use NULL instead of 0 when init rcar_isp_core in probe.
> > >
> > > * Changes since v4
> > > - Add depend on VIDEO_RENESAS_VSP1
> > >
> > > * Changes since v3
> > > - Make sure VSPX is stopped before queueing next job, starting with
> > >   v6.18-rc1 the two can get out of sync.
> > > - Fix a possible race with VSPX when stopping streaming.
> > >
> > > * Changes since v2
> > > - Rework the start procedure so the ISP is reset without from a context
> > >   that can sleep.
> > > ---
> > >  .../media/platform/renesas/rcar-isp/Kconfig   |    2 +
> > >  .../media/platform/renesas/rcar-isp/Makefile  |    2 +-
> > >  .../media/platform/renesas/rcar-isp/core-io.c | 1017 +++++++++++++++++
> > >  .../media/platform/renesas/rcar-isp/core.c    |  826 +++++++++++++
> > >  .../media/platform/renesas/rcar-isp/csisp.c   |   48 +-
> > >  .../platform/renesas/rcar-isp/risp-core.h     |  170 +++
> > >  6 files changed, 2057 insertions(+), 8 deletions(-)
> > >  create mode 100644 drivers/media/platform/renesas/rcar-isp/core-io.c
> > >  create mode 100644 drivers/media/platform/renesas/rcar-isp/core.c
> > >  create mode 100644 drivers/media/platform/renesas/rcar-isp/risp-core.h
> > >
> > > diff --git a/drivers/media/platform/renesas/rcar-isp/Kconfig b/drivers/media/platform/renesas/rcar-isp/Kconfig
> > > index 242f6a23851f..bacc15c250fe 100644
> > > --- a/drivers/media/platform/renesas/rcar-isp/Kconfig
> > > +++ b/drivers/media/platform/renesas/rcar-isp/Kconfig
> > > @@ -5,10 +5,12 @@ config VIDEO_RCAR_ISP
> > >  	depends on V4L_PLATFORM_DRIVERS
> > >  	depends on VIDEO_DEV && OF
> > >  	depends on ARCH_RENESAS || COMPILE_TEST
> > > +	depends on VIDEO_RENESAS_VSP1
> > >  	select MEDIA_CONTROLLER
> > >  	select VIDEO_V4L2_SUBDEV_API
> > >  	select RESET_CONTROLLER
> > >  	select V4L2_FWNODE
> > > +	select VIDEO_DCT_RPPX1
> > >  	help
> > >  	  Support for Renesas R-Car Image Signal Processor (ISP).
> > >  	  Enable this to support the Renesas R-Car Image Signal
> > > diff --git a/drivers/media/platform/renesas/rcar-isp/Makefile b/drivers/media/platform/renesas/rcar-isp/Makefile
> > > index b542118c831e..c0c80303682c 100644
> > > --- a/drivers/media/platform/renesas/rcar-isp/Makefile
> > > +++ b/drivers/media/platform/renesas/rcar-isp/Makefile
> > > @@ -1,4 +1,4 @@
> > >  # SPDX-License-Identifier: GPL-2.0
> > > -rcar-isp-objs = csisp.o
> > > +rcar-isp-objs = csisp.o core.o core-io.o
> > >
> > >  obj-$(CONFIG_VIDEO_RCAR_ISP) += rcar-isp.o
> > > diff --git a/drivers/media/platform/renesas/rcar-isp/core-io.c b/drivers/media/platform/renesas/rcar-isp/core-io.c
> > > new file mode 100644
> > > index 000000000000..7fce4da41abc
> > > --- /dev/null
> > > +++ b/drivers/media/platform/renesas/rcar-isp/core-io.c
> > > @@ -0,0 +1,1017 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +
> > > +#include <linux/pm_runtime.h>
> > > +
> > > +#include <media/v4l2-ctrls.h>
> > > +#include <media/v4l2-event.h>
> > > +#include <media/v4l2-ioctl.h>
> > > +#include <media/v4l2-isp.h>
> > > +#include <media/v4l2-mc.h>
> > > +
> > > +#include <linux/media/dreamchip/rppx1-config.h>
> > > +
> > > +#include "risp-core.h"
> > > +
> > > +#define risp_io_err(d, fmt, arg...)         dev_err((d)->core->dev, fmt, ##arg)
> > > +
> > > +static struct risp_buffer *risp_io_vb2buf(struct vb2_v4l2_buffer *vb)
> > > +{
> > > +	return container_of(vb, struct risp_buffer, vb);
> > > +}
> > > +
> > > +static int risp_io_open(struct file *file)
> > > +{
> > > +	struct rcar_isp_core_io *io = video_drvdata(file);
> > > +	struct rcar_isp_core *core = io->core;
> > > +	int ret;
> > > +
> > > +	ret = pm_runtime_resume_and_get(core->dev);
> > > +	if (ret < 0)
> > > +		return ret;
> > > +
> > > +	ret = reset_control_deassert(core->csrstc);
> > > +	if (ret)
> > > +		goto err_csrstc;
> > > +
> > > +	ret = clk_prepare_enable(core->clk);
> > > +	if (ret)
> > > +		goto err_clk;
> >
> > How does this work ?
> >
> > This handler is registered for all video devices. Everytime a video
> > device is opened, we go through this routine, right ?
>
> Yes.
>
> >
> > First question first: do we need pm_runtime here ? The 'csisp' driver
> > which provides 'core->dev' registers a platform driver, but has not
> > pm_runtime handlers. What is pm_runtime_resume_and_get() supposed to
> > call here ?
>
> It is needed to turn on the power domain.
>
> >
> > Then, do we need to power-up at open() time or at start streaming
> > time ?
> >
> > Wouldn't it be better to define a policy like "when all video devices
> > have started streaming then let's power up ?". I see you have a
> > similar check implemented in risp_core_start_streaming() already.
> >
> > Unless I missed something obvious you're here going through the reset
> > deassertion and clock prepare for every open() on all video device ?
>
> That is a good question. There is a dependency between the R-Car ISP CS
> and CORE modules. And the CORE needs to access registers gated by the CS
> reset and power domain.
>
> When I started to work on this I structured it with this at stream
> start/stop time as you ask here, but I had issues working the CORE and
> CSISP reliably together. While this was early in the driver development
> stage I have kept it as is.
>
> I did some tests moving this to stream start/stop now and found no ill
> effects. Thanks for spotting it and let's hope no gremlin crept in!
>
> >
> > > +
> > > +	ret = mutex_lock_interruptible(&io->lock);
> >
> > Why interruptible, and you need to lock at all ? File operations (on
> > the same file) should be serialized already, aren't they ?
>
> TBH, I'm not sure. In the past I have been asked in review for other
> drivers to have this type of locking. It is interruptible so that
> user-space is able to break it in case it gets tiered of waiting on
> open().
>
> >
> > > +	if (ret)
> > > +		goto err_pm;
> > > +
> > > +	file->private_data = io;
> > > +
> > > +	ret = v4l2_fh_open(file);
> > > +	if (ret)
> > > +		goto err_unlock;
> > > +
> > > +	ret = v4l2_pipeline_pm_get(&io->vdev.entity);
> > > +	if (ret < 0)
> > > +		goto err_open;
> > > +
> > > +	mutex_unlock(&io->lock);
> > > +
> > > +	return 0;
> > > +err_open:
> > > +	v4l2_fh_release(file);
> > > +err_unlock:
> > > +	mutex_unlock(&io->lock);
> > > +err_pm:
> > > +	pm_runtime_put(core->dev);
> > > +err_clk:
> > > +	clk_disable_unprepare(core->clk);
> > > +err_csrstc:
> > > +	reset_control_assert(core->csrstc);
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +static int risp_io_release(struct file *file)
> > > +{
> > > +	struct rcar_isp_core_io *io = video_drvdata(file);
> > > +	struct rcar_isp_core *core = io->core;
> > > +	int ret;
> > > +
> > > +	mutex_lock(&io->lock);
> > > +
> > > +	ret = _vb2_fop_release(file, NULL);
> > > +
> > > +	v4l2_pipeline_pm_put(&io->vdev.entity);
> > > +
> > > +	mutex_unlock(&io->lock);
> > > +
> > > +	clk_disable_unprepare(core->clk);
> > > +
> > > +	pm_runtime_put(core->dev);
> > > +
> > > +	reset_control_assert(core->csrstc);
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +static const struct v4l2_file_operations risp_io_fops = {
> > > +	.owner		= THIS_MODULE,
> > > +	.unlocked_ioctl	= video_ioctl2,
> > > +	.open		= risp_io_open,
> > > +	.release	= risp_io_release,
> > > +	.poll		= vb2_fop_poll,
> > > +	.mmap		= vb2_fop_mmap,
> > > +	.read		= vb2_fop_read,
> > > +};
> > > +
> > > +/* -----------------------------------------------------------------------------
> > > + * Common queue
> > > + */
> > > +
> > > +static int risp_io_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
> > > +			       unsigned int *nplanes, unsigned int sizes[],
> > > +			       struct device *alloc_devs[])
> > > +
> > > +{
> > > +	struct rcar_isp_core_io *io = vb2_get_drv_priv(vq);
> > > +
> > > +	if (V4L2_TYPE_IS_MULTIPLANAR(vq->type)) {
> > > +		const struct v4l2_pix_format_mplane *pix = &io->format.fmt.pix_mp;
> > > +
> > > +		if (*nplanes) {
> > > +			if (*nplanes > pix->num_planes)
> > > +				return -EINVAL;
> > > +
> > > +			for (unsigned int i = 0; i < pix->num_planes; i++)
> > > +				if (sizes[i] < pix->plane_fmt[i].sizeimage)
> > > +					return -EINVAL;
> > > +
> > > +			return 0;
> > > +		}
> > > +
> > > +		*nplanes = pix->num_planes;
> > > +		for (unsigned int i = 0; i < pix->num_planes; i++)
> > > +			sizes[i] = pix->plane_fmt[i].sizeimage;
> > > +	} else {
> > > +		if (*nplanes) {
> > > +			if (sizes[0] < io->format.fmt.meta.buffersize)
> > > +				return -EINVAL;
> > > +
> > > +			return 0;
> > > +		}
> > > +
> > > +		*nplanes = 1;
> > > +		sizes[0] = io->format.fmt.meta.buffersize;
> > > +	}
> > > +
> > > +	/* Initialize buffer queue */
> > > +	INIT_LIST_HEAD(&io->buffers);
> > > +
> > > +	return 0;
> > > +};
> > > +
> > > +static int risp_io_buffer_prepare_set(struct rcar_isp_core_io *io,
> > > +				      struct vb2_buffer *vb, unsigned int plane,
> > > +				      unsigned long size)
> > > +{
> > > +	if (vb2_plane_size(vb, plane) < size) {
> > > +		risp_io_err(io, "Buffer too small (%lu < %lu)\n",
> > > +			    vb2_plane_size(vb, plane), size);
> > > +		return -EINVAL;
> > > +	}
> > > +
> > > +	vb2_set_plane_payload(vb, plane, size);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int risp_io_buffer_prepare(struct vb2_buffer *vb)
> > > +{
> > > +	struct rcar_isp_core_io *io = vb2_get_drv_priv(vb->vb2_queue);
> > > +	int ret = 0;
> > > +
> > > +	if (V4L2_TYPE_IS_MULTIPLANAR(vb->vb2_queue->type)) {
> > > +		const struct v4l2_pix_format_mplane *pix = &io->format.fmt.pix_mp;
> > > +
> > > +		for (unsigned int i = 0; i < pix->num_planes; i++) {
> > > +			ret = risp_io_buffer_prepare_set(io, vb, i,
> > > +							 pix->plane_fmt[i].sizeimage);
> > > +			if (ret)
> > > +				break;
> > > +		}
> >
> >                 return 0;
>
> return ret;
>
> >         }
> >
> > > +	} else {
> > > +		ret = risp_io_buffer_prepare_set(io, vb, 0,
> > > +						 io->format.fmt.meta.buffersize);
> > > +	}
> > > +
> > > +	return ret;
> >
> >         return risp_io_buffer_prepare_set(io, vb, 0,
> > 					  io->format.fmt.meta.buffersize);
>
> Thanks, this is much nicer.
>
> >   }
> >
> > > +}
> > > +
> > > +static void risp_io_buffer_queue(struct vb2_buffer *vb)
> > > +{
> > > +	struct rcar_isp_core_io *io = vb2_get_drv_priv(vb->vb2_queue);
> > > +	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> > > +	struct risp_buffer *buf = risp_io_vb2buf(vbuf);
> > > +
> > > +	guard(mutex)(&io->core->io_lock);
> > > +
> > > +	list_add_tail(&buf->list, &io->buffers);
> > > +
> > > +	if (risp_core_job_prepare(io->core))
> > > +		risp_io_err(io, "Failed to prepare job\n");
> > > +}
> > > +
> > > +static void risp_io_return_buffers(struct rcar_isp_core_io *io,
> > > +				   enum vb2_buffer_state state)
> > > +{
> > > +	struct risp_buffer *buf, *node;
> > > +
> > > +	lockdep_assert_held(&io->core->io_lock);
> > > +
> > > +	list_for_each_entry_safe(buf, node, &io->buffers, list) {
> > > +		vb2_buffer_done(&buf->vb.vb2_buf, state);
> > > +		list_del(&buf->list);
> > > +	}
> > > +}
> > > +
> > > +static int risp_io_start_streaming(struct vb2_queue *vq, unsigned int count)
> > > +{
> > > +	struct rcar_isp_core_io *io = vb2_get_drv_priv(vq);
> > > +	int ret;
> > > +
> > > +	scoped_guard(mutex, &io->core->io_lock) {
> > > +		if (io->core->io[RISP_CORE_INPUT1].format.fmt.pix_mp.width !=
> > > +		    io->core->io[RISP_CORE_OUTPUT1].format.fmt.pix_mp.width ||
> > > +		    io->core->io[RISP_CORE_INPUT1].format.fmt.pix_mp.height !=
> > > +		    io->core->io[RISP_CORE_OUTPUT1].format.fmt.pix_mp.height) {
> >
> > I understand you can't use link_validate(), but shouldn't this be done
> > by the core.c file instead of performing this for every video device,
> > maybe in the risp_core_start_streaming() function after we have
> > validated all video device have started streaming ?
>
> We could do that, but it would be messier IMHO. This driver trying to
> juggle four vdev into a single operational unit was very messy in early
> prototypes. Pushing as much functionality as possible down to each vdev
> was one way to reduce the complexity. It have worked quiet well IMHO.
>
> >
> >
> > > +			risp_io_return_buffers(io, VB2_BUF_STATE_QUEUED);
> > > +			return -EPIPE;
> > > +		}
> > > +
> > > +		io->streaming = true;
> > > +	}
> > > +
> > > +	ret = risp_core_start_streaming(io->core);
> > > +	if (ret) {
> > > +		guard(mutex)(&io->core->io_lock);
> > > +
> > > +		risp_io_return_buffers(io, VB2_BUF_STATE_QUEUED);
> > > +		return ret;
> > > +	}
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static void risp_io_stop_streaming(struct vb2_queue *vq)
> > > +{
> > > +	struct rcar_isp_core_io *io = vb2_get_drv_priv(vq);
> > > +
> > > +	scoped_guard(mutex, &io->core->io_lock) {
> > > +		io->streaming = false;
> > > +		risp_core_stop_streaming(io->core);
> > > +		risp_io_return_buffers(io, VB2_BUF_STATE_ERROR);
> > > +	}
> > > +
> > > +	/*
> > > +	 * Wait for buffers part of the jobs not yet processed. Note that this
> > > +	 * might complete buffers out of order.
> > > +	 */
> > > +	vb2_wait_for_all_buffers(&io->queue);
> > > +}
> > > +
> > > +/* -----------------------------------------------------------------------------
> > > + * Common V4L2 IOCTLs
> > > + */
> > > +
> > > +static int risp_io_querycap(struct file *file, void *priv,
> > > +			    struct v4l2_capability *cap)
> > > +{
> > > +	struct video_device *vdev = video_devdata(file);
> > > +
> > > +	strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
> > > +	strscpy(cap->card, vdev->name, sizeof(cap->card));
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +/* -----------------------------------------------------------------------------
> > > + * Input Exposure
> >
> > Exposure ?
>
> Used in the datasheet for input data. I tried to match it as close as
> possible to make it easy to read.
>
> >
> > > + */
> > > +
> > > +static int risp_io_input_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
> > > +				     unsigned int *nplanes, unsigned int sizes[],
> > > +				     struct device *alloc_devs[])
> > > +
> > > +{
> > > +	struct rcar_isp_core_io *io = vb2_get_drv_priv(vq);
> > > +	struct rcar_isp_core *core = io->core;
> > > +	struct device *bus_master;
> > > +	int ret;
> > > +
> > > +	ret = risp_io_queue_setup(vq, nbuffers, nplanes, sizes, alloc_devs);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	bus_master = vsp1_isp_get_bus_master(core->vspx.dev);
> > > +	if (IS_ERR_OR_NULL(bus_master)) {
> > > +		risp_io_err(io, "Missing reference to bus-master device\n");
> > > +		return -EINVAL;
> > > +	}
> > > +
> > > +	/*
> > > +	 * Allocate buffers using the bus_master device associated with the
> > > +	 * VSPX associated to this ISP instance.
> > > +	 */
> > > +	alloc_devs[0] = bus_master;
> > > +
> > > +	return 0;
> > > +};
> > > +
> > > +static const struct vb2_ops risp_io_input_qops = {
> > > +	.queue_setup		= risp_io_input_queue_setup,
> > > +	.buf_prepare		= risp_io_buffer_prepare,
> > > +	.buf_queue		= risp_io_buffer_queue,
> > > +	.start_streaming	= risp_io_start_streaming,
> > > +	.stop_streaming		= risp_io_stop_streaming,
> > > +};
> > > +
> > > +static const struct v4l2_pix_format_mplane risp_io_input_default_format = {
> > > +	.width = 1920,
> > > +	.height = 1080,
> > > +	.field = V4L2_FIELD_NONE,
> > > +	.pixelformat = V4L2_PIX_FMT_SGRBG8,
> > > +	.colorspace = V4L2_COLORSPACE_RAW,
> >
> >         .xfer_func = V4L2_XFER_FUNC_NONE,
> >         .ycbcr_enc = V4L2_YCBCR_ENC_601,
> >         .quantization = V4L2_QUANTIZATION_FULL_RANGE,
>
> Thanks.
>
> >
> > > +	.num_planes = 1,
> > > +	.plane_fmt = {
> > > +		[0] = {
> > > +			.sizeimage = 1920 * 1080,
> > > +			.bytesperline = 1920,
> > > +		},
> > > +	},
> > > +};
> > > +
> > > +static const struct risp_io_input_format {
> > > +	unsigned int fourcc;
> > > +	unsigned int bpp;
> > > +} risp_io_input_formats[] = {
> > > +	{ .fourcc = V4L2_PIX_FMT_SBGGR8,	.bpp = 1 },
> > > +	{ .fourcc = V4L2_PIX_FMT_SGBRG8,	.bpp = 1 },
> > > +	{ .fourcc = V4L2_PIX_FMT_SGRBG8,	.bpp = 1 },
> > > +	{ .fourcc = V4L2_PIX_FMT_SRGGB8,	.bpp = 1 },
> > > +	{ .fourcc = V4L2_PIX_FMT_SBGGR10,	.bpp = 2 },
> > > +	{ .fourcc = V4L2_PIX_FMT_SGBRG10,	.bpp = 2 },
> > > +	{ .fourcc = V4L2_PIX_FMT_SGRBG10,	.bpp = 2 },
> > > +	{ .fourcc = V4L2_PIX_FMT_SRGGB10,	.bpp = 2 },
> > > +	{ .fourcc = V4L2_PIX_FMT_SBGGR12,	.bpp = 2 },
> > > +	{ .fourcc = V4L2_PIX_FMT_SGBRG12,	.bpp = 2 },
> > > +	{ .fourcc = V4L2_PIX_FMT_SGRBG12,	.bpp = 2 },
> > > +	{ .fourcc = V4L2_PIX_FMT_SRGGB12,	.bpp = 2 },
> > > +};
> > > +
> > > +static void risp_io_input_try_format(struct rcar_isp_core_io *io,
> > > +				     struct v4l2_pix_format_mplane *pix)
> > > +{
> > > +	unsigned int bpp = 0;
> > > +
> > > +	v4l_bound_align_image(&pix->width, 128, 5120, 2,
> > > +			      &pix->height, 128, 4096, 2, 0);
> > > +
> > > +	for (unsigned int i = 0; i < ARRAY_SIZE(risp_io_input_formats); i++) {
> > > +		if (risp_io_input_formats[i].fourcc == pix->pixelformat) {
> > > +			bpp = risp_io_input_formats[i].bpp;
> > > +			break;
> > > +		}
> > > +	}
> > > +
> > > +	if (!bpp) {
> > > +		pix->pixelformat = risp_io_input_formats[0].fourcc;
> > > +		bpp = risp_io_input_formats[0].bpp;
> > > +	}
> > > +
> > > +	pix->field = V4L2_FIELD_NONE;
> > > +	pix->colorspace = V4L2_COLORSPACE_RAW;
> > > +
> > > +	pix->num_planes = 1;
> > > +	pix->plane_fmt[0].bytesperline = pix->width * bpp;
> > > +	pix->plane_fmt[0].sizeimage = pix->plane_fmt[0].bytesperline * pix->height;
> > > +}
> > > +
> > > +static int risp_io_input_enum_fmt(struct file *file, void *priv,
> > > +				  struct v4l2_fmtdesc *f)
> > > +{
> > > +	if (f->index >= ARRAY_SIZE(risp_io_input_formats))
> > > +		return -EINVAL;
> > > +
> > > +	f->pixelformat = risp_io_input_formats[f->index].fourcc;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int risp_io_input_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
> > > +{
> > > +	struct rcar_isp_core_io *io = video_drvdata(file);
> > > +
> > > +	if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> > > +		return -EINVAL;
> > > +
> > > +	f->fmt.pix_mp = io->format.fmt.pix_mp;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int risp_io_input_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
> > > +{
> > > +	struct rcar_isp_core_io *io = video_drvdata(file);
> > > +
> > > +	if (vb2_is_busy(&io->queue))
> > > +		return -EBUSY;
> > > +
> > > +	if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> > > +		return -EINVAL;
> > > +
> > > +	risp_io_input_try_format(io, &f->fmt.pix_mp);
> > > +
> > > +	io->format.fmt.pix_mp = f->fmt.pix_mp;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int risp_io_input_try_fmt(struct file *file, void *fh,
> > > +				 struct v4l2_format *f)
> > > +{
> > > +	struct rcar_isp_core_io *io = video_drvdata(file);
> > > +
> > > +	risp_io_input_try_format(io, &f->fmt.pix_mp);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int risp_io_input_enum_framesizes(struct file *file, void *fh,
> > > +					 struct v4l2_frmsizeenum *fsize)
> > > +{
> > > +	bool found = false;
> > > +
> > > +	if (fsize->index != 0)
> > > +		return -EINVAL;
> > > +
> > > +	for (unsigned int i = 0; i < ARRAY_SIZE(risp_io_input_formats); i++) {
> > > +		if (risp_io_input_formats[i].fourcc == fsize->pixel_format) {
> > > +			found = true;
> > > +			break;
> > > +		}
> > > +	}
> > > +
> > > +	if (!found)
> > > +		return -EINVAL;
> > > +
> > > +	fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
> > > +
> > > +	fsize->stepwise.min_width = 128;
> > > +	fsize->stepwise.max_width = 5120;
> > > +	fsize->stepwise.step_width = 2;
> > > +
> > > +	fsize->stepwise.min_height = 128;
> > > +	fsize->stepwise.max_height = 4096;
> > > +	fsize->stepwise.step_height = 2;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static const struct v4l2_ioctl_ops risp_io_input_ioctl_ops = {
> > > +	.vidioc_querycap		= risp_io_querycap,
> > > +
> > > +	.vidioc_enum_fmt_vid_out	= risp_io_input_enum_fmt,
> > > +	.vidioc_g_fmt_vid_out_mplane	= risp_io_input_g_fmt,
> > > +	.vidioc_s_fmt_vid_out_mplane	= risp_io_input_s_fmt,
> > > +	.vidioc_try_fmt_vid_out_mplane	= risp_io_input_try_fmt,
> > > +	.vidioc_enum_framesizes		= risp_io_input_enum_framesizes,
> > > +
> > > +	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
> > > +	.vidioc_querybuf		= vb2_ioctl_querybuf,
> > > +	.vidioc_qbuf			= vb2_ioctl_qbuf,
> > > +	.vidioc_expbuf			= vb2_ioctl_expbuf,
> > > +	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
> > > +	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
> > > +	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
> > > +	.vidioc_streamon		= vb2_ioctl_streamon,
> > > +	.vidioc_streamoff		= vb2_ioctl_streamoff,
> > > +};
> > > +
> > > +/* -----------------------------------------------------------------------------
> > > + * Parameters
> > > + *
> > > + */
> > > +
> > > +/* Max 2048 address + value pairs in one VSPX buffer, increase if needed. */
> > > +#define RISP_IO_PARAMS_BUF_SIZE	16384
> > > +
> > > +static int risp_io_params_buf_init(struct vb2_buffer *vb)
> > > +{
> > > +	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> > > +	struct risp_buffer *buf = risp_io_vb2buf(vbuf);
> > > +	struct rcar_isp_core_io *io = vb2_get_drv_priv(vb->vb2_queue);
> > > +	struct rcar_isp_core *core = io->core;
> > > +	size_t size;
> > > +	int ret;
> > > +
> > > +	memset(&buf->vsp_buffer, 0, sizeof(buf->vsp_buffer));
> > > +
> > > +	size = RISP_IO_PARAMS_BUF_SIZE;
> > > +	ret = vsp1_isp_alloc_buffer(core->vspx.dev, size, &buf->vsp_buffer);
> > > +	if (ret)
> > > +		return -EINVAL;
> > > +
> > > +	memset(buf->vsp_buffer.cpu_addr, 0, RISP_IO_PARAMS_BUF_SIZE);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static void risp_io_params_buf_cleanup(struct vb2_buffer *vb)
> > > +{
> > > +	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> > > +	struct risp_buffer *buf = risp_io_vb2buf(vbuf);
> > > +	struct rcar_isp_core_io *io = vb2_get_drv_priv(vb->vb2_queue);
> > > +	struct rcar_isp_core *core = io->core;
> > > +
> > > +	vsp1_isp_free_buffer(core->vspx.dev, &buf->vsp_buffer);
> > > +}
> > > +
> > > +struct risp_conf_dma_write_desc {
> > > +	u32 *buf;
> > > +	u32 base;
> > > +	unsigned int count;
> > > +};
> > > +
> > > +static int risp_conf_dma_prepare(void *priv, u32 offset, u32 value)
> > > +{
> > > +	struct risp_conf_dma_write_desc *desc = priv;
> > > +
> > > +	/* Bounds check, 8 bytes = address (4)+ value (4). */
> > > +	if ((desc->count + 1) * 8 > RISP_IO_PARAMS_BUF_SIZE)
> > > +		return -ENOMEM;
> > > +
> > > +	(*desc->buf++) = desc->base | offset;
> > > +	(*desc->buf++) = value;
> > > +
> > > +	desc->count++;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int risp_io_params_buffer_prepare(struct vb2_buffer *vb)
> > > +{
> > > +	struct rcar_isp_core_io *io = vb2_get_drv_priv(vb->vb2_queue);
> > > +	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> > > +	struct risp_buffer *buf = risp_io_vb2buf(vbuf);
> > > +	struct risp_conf_dma_write_desc desc;
> > > +	u32 *cpu_addr;
> > > +	int ret;
> > > +
> > > +	/* Prepare params. */
> > > +	cpu_addr = (u32 *)buf->vsp_buffer.cpu_addr;
> > > +
> > > +	desc.buf = cpu_addr + 2;
> > > +	desc.base = io->core->rppaddr;
> > > +	desc.count = 0;
> > > +
> > > +	/* Fill params body. */
> > > +	ret = rppx1_params(io->core->rpp, vb, io->format.fmt.meta.buffersize,
> > > +			   risp_conf_dma_prepare, &desc);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	/* Fill params header. */
> > > +	cpu_addr[0] = desc.count;
> > > +	cpu_addr[1] = 0x0;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static const struct vb2_ops risp_io_params_qops = {
> > > +	.queue_setup		= risp_io_queue_setup,
> > > +	.buf_init		= risp_io_params_buf_init,
> > > +	.buf_cleanup		= risp_io_params_buf_cleanup,
> > > +	.buf_prepare		= risp_io_params_buffer_prepare,
> > > +	.buf_queue		= risp_io_buffer_queue,
> > > +	.start_streaming	= risp_io_start_streaming,
> > > +	.stop_streaming		= risp_io_stop_streaming,
> > > +};
> > > +
> > > +static const struct v4l2_meta_format risp_io_params_default_format = {
> > > +	.dataformat = V4L2_META_FMT_RPP_X1_PARAMS,
> > > +	.buffersize = v4l2_isp_buffer_size(RPPX1_PARAMS_MAX_SIZE),
> > > +};
> > > +
> > > +static int risp_io_params_enum_fmt(struct file *file, void *priv,
> > > +				   struct v4l2_fmtdesc *f)
> > > +{
> > > +	struct rcar_isp_core_io *io = video_drvdata(file);
> > > +
> > > +	if (f->type != V4L2_BUF_TYPE_META_OUTPUT || f->index)
> > > +		return -EINVAL;
> > > +
> > > +	f->pixelformat = io->format.fmt.meta.dataformat;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int risp_io_params_g_fmt(struct file *file, void *priv,
> > > +				struct v4l2_format *f)
> > > +{
> > > +	struct rcar_isp_core_io *io = video_drvdata(file);
> > > +	struct v4l2_meta_format *meta = &f->fmt.meta;
> > > +
> > > +	if (f->type != V4L2_BUF_TYPE_META_OUTPUT)
> > > +		return -EINVAL;
> > > +
> > > +	*meta = io->format.fmt.meta;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int risp_io_params_s_fmt(struct file *file, void *priv,
> > > +				struct v4l2_format *f)
> > > +{
> > > +	struct rcar_isp_core_io *io = video_drvdata(file);
> > > +
> > > +	if (vb2_is_busy(&io->queue))
> > > +		return -EBUSY;
> > > +
> > > +	return risp_io_params_g_fmt(file, priv, f);
> > > +}
> > > +
> > > +static const struct v4l2_ioctl_ops risp_io_params_ioctl_ops = {
> > > +	.vidioc_querycap		= risp_io_querycap,
> > > +
> > > +	.vidioc_enum_fmt_meta_out	= risp_io_params_enum_fmt,
> > > +	.vidioc_g_fmt_meta_out		= risp_io_params_g_fmt,
> > > +	.vidioc_s_fmt_meta_out		= risp_io_params_s_fmt,
> > > +	.vidioc_try_fmt_meta_out	= risp_io_params_g_fmt,
> > > +
> > > +	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
> > > +	.vidioc_querybuf		= vb2_ioctl_querybuf,
> > > +	.vidioc_qbuf			= vb2_ioctl_qbuf,
> > > +	.vidioc_expbuf			= vb2_ioctl_expbuf,
> > > +	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
> > > +	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
> > > +	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
> > > +	.vidioc_streamon		= vb2_ioctl_streamon,
> > > +	.vidioc_streamoff		= vb2_ioctl_streamoff,
> > > +};
> > > +
> > > +/* -----------------------------------------------------------------------------
> > > + * Statistics
> > > + */
> > > +
> > > +static const struct vb2_ops risp_io_stats_qops = {
> > > +	.queue_setup		= risp_io_queue_setup,
> > > +	.buf_prepare		= risp_io_buffer_prepare,
> > > +	.buf_queue		= risp_io_buffer_queue,
> > > +	.start_streaming	= risp_io_start_streaming,
> > > +	.stop_streaming		= risp_io_stop_streaming,
> > > +};
> > > +
> > > +static const struct v4l2_meta_format risp_io_stats_default_format = {
> > > +	.dataformat = V4L2_META_FMT_RPP_X1_STATS,
> > > +	.buffersize = v4l2_isp_buffer_size(RPPX1_STATS_MAX_SIZE),
> > > +};
> > > +
> > > +static int risp_io_stats_enum_fmt(struct file *file, void *priv,
> > > +				  struct v4l2_fmtdesc *f)
> > > +{
> > > +	struct rcar_isp_core_io *io = video_drvdata(file);
> > > +
> > > +	if (f->type != V4L2_BUF_TYPE_META_CAPTURE || f->index)
> > > +		return -EINVAL;
> > > +
> > > +	f->pixelformat = io->format.fmt.meta.dataformat;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int risp_io_stats_g_fmt(struct file *file, void *priv,
> > > +			       struct v4l2_format *f)
> > > +{
> > > +	struct rcar_isp_core_io *io = video_drvdata(file);
> > > +	struct v4l2_meta_format *meta = &f->fmt.meta;
> > > +
> > > +	if (f->type != V4L2_BUF_TYPE_META_CAPTURE)
> > > +		return -EINVAL;
> > > +
> > > +	*meta = io->format.fmt.meta;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int risp_io_stats_s_fmt(struct file *file, void *priv,
> > > +			       struct v4l2_format *f)
> > > +{
> > > +	struct rcar_isp_core_io *io = video_drvdata(file);
> > > +
> > > +	if (vb2_is_busy(&io->queue))
> > > +		return -EBUSY;
> > > +
> > > +	return risp_io_stats_g_fmt(file, priv, f);
> > > +}
> > > +
> > > +static const struct v4l2_ioctl_ops risp_io_stats_ioctl_ops = {
> > > +	.vidioc_querycap		= risp_io_querycap,
> > > +
> > > +	.vidioc_enum_fmt_meta_cap	= risp_io_stats_enum_fmt,
> > > +	.vidioc_g_fmt_meta_cap		= risp_io_stats_g_fmt,
> > > +	.vidioc_s_fmt_meta_cap		= risp_io_stats_s_fmt,
> > > +	.vidioc_try_fmt_meta_cap	= risp_io_stats_g_fmt,
> > > +
> > > +	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
> > > +	.vidioc_querybuf		= vb2_ioctl_querybuf,
> > > +	.vidioc_qbuf			= vb2_ioctl_qbuf,
> > > +	.vidioc_expbuf			= vb2_ioctl_expbuf,
> > > +	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
> > > +	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
> > > +	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
> > > +	.vidioc_streamon		= vb2_ioctl_streamon,
> > > +	.vidioc_streamoff		= vb2_ioctl_streamoff,
> > > +};
> > > +
> > > +/* -----------------------------------------------------------------------------
> > > + * Video capture
> > > + */
> > > +
> > > +static const struct vb2_ops risp_io_capture_qops = {
> > > +	.queue_setup		= risp_io_queue_setup,
> > > +	.buf_prepare		= risp_io_buffer_prepare,
> > > +	.buf_queue		= risp_io_buffer_queue,
> > > +	.start_streaming	= risp_io_start_streaming,
> > > +	.stop_streaming		= risp_io_stop_streaming,
> > > +};
> > > +
> > > +static const struct v4l2_pix_format_mplane risp_io_capture_default_format = {
> > > +	.width = 1920,
> > > +	.height = 1080,
> > > +	.pixelformat = V4L2_PIX_FMT_XBGR32,
> > > +	.field = V4L2_FIELD_NONE,
> > > +	.colorspace = V4L2_COLORSPACE_DEFAULT,
> >
> > Isn't this V4L2_COLORSPACE_SRGB ?
>
> Yes it is.
>
> >
> > > +	.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT,
> >
> > There shouldn't be an ycbcr encoding, I'm surprised there's no
> > V4L2_YCBCR_ENC_NONE..
>
> :-)
>
>
> >
> > > +	.quantization = V4L2_QUANTIZATION_DEFAULT,
> >         .quantization = V4L2_QUANTIZATION_FULL_RANGE,
> >
> >         .xfer_func = V4L2_XFER_FUNC_SRGB,
>
> Thanks.
>
> >
> > > +	.num_planes = 1,
> > > +	.plane_fmt = {
> > > +		[0] = {
> > > +			.bytesperline = ALIGN(1920 * 4, 256),
> >
> > isn't 1920*4 is already aligned to 256 ?
>
> It is, but this serves as a compile time reminder the lines needs to be
> 256 aligned.
>
> >
> > > +			.sizeimage = ALIGN(1920 * 4, 256) * 1080,
>
> Same here, I could just precomputed the number but this adds context
> IMHO.
>
> > > +		},
> > > +	},
> > > +};
> > > +
> > > +static void risp_io_capture_try_format(struct rcar_isp_core_io *io,
> > > +				       struct v4l2_pix_format_mplane *pix)
> > > +{
> > > +	v4l_bound_align_image(&pix->width, 128, 5120, 2,
> > > +			      &pix->height, 128, 4096, 2, 0);
> > > +
> > > +	pix->field = V4L2_FIELD_NONE;
> > > +	pix->colorspace = V4L2_COLORSPACE_DEFAULT;
> > > +	pix->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
> >
> > should probably be adjusted to the match the output format ?
>
> Indeed, will do.
>
> >
> > > +	pix->quantization = V4L2_QUANTIZATION_DEFAULT;
> > > +
> > > +	switch (pix->pixelformat) {
> > > +	case V4L2_PIX_FMT_NV16M:
> > > +		pix->num_planes = 2;
> > > +		pix->plane_fmt[0].bytesperline = ALIGN(pix->width, 256);
> > > +		pix->plane_fmt[0].sizeimage = pix->plane_fmt[0].bytesperline * pix->height;
> > > +		pix->plane_fmt[1].bytesperline = ALIGN(pix->width, 256);
> > > +		pix->plane_fmt[1].sizeimage = pix->plane_fmt[1].bytesperline * pix->height;
> > > +		break;
> > > +	default:
> > > +		pix->pixelformat = V4L2_PIX_FMT_XBGR32;
> > > +		pix->num_planes = 1;
> > > +		pix->plane_fmt[0].bytesperline = ALIGN(pix->width * 4, 256);
> > > +		pix->plane_fmt[0].sizeimage = pix->plane_fmt[0].bytesperline * pix->height;
> > > +		break;
> > > +	}
> > > +}
> > > +
> > > +static int risp_io_capture_enum_fmt(struct file *file, void *priv,
> > > +				    struct v4l2_fmtdesc *f)
> > > +{
> > > +	if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> > > +		return -EINVAL;
> > > +
> > > +	switch (f->index) {
> > > +	case 0:
> > > +		f->pixelformat = V4L2_PIX_FMT_NV16M;
> > > +		break;
> > > +	case 1:
> > > +		f->pixelformat = V4L2_PIX_FMT_XBGR32;
> > > +		break;
> > > +	default:
> > > +		return -EINVAL;
> > > +	}
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int risp_io_capture_g_fmt(struct file *file, void *priv,
> > > +				 struct v4l2_format *f)
> > > +{
> > > +	struct rcar_isp_core_io *io = video_drvdata(file);
> > > +
> > > +	if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> > > +		return -EINVAL;
> > > +
> > > +	f->fmt.pix_mp = io->format.fmt.pix_mp;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int risp_io_capture_s_fmt(struct file *file, void *priv,
> > > +				 struct v4l2_format *f)
> > > +{
> > > +	struct rcar_isp_core_io *io = video_drvdata(file);
> > > +
> > > +	if (vb2_is_busy(&io->queue))
> > > +		return -EBUSY;
> > > +
> > > +	if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> > > +		return -EINVAL;
> > > +
> > > +	risp_io_capture_try_format(io, &f->fmt.pix_mp);
> > > +
> > > +	io->format.fmt.pix_mp = f->fmt.pix_mp;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int risp_io_capture_try_fmt(struct file *file, void *fh,
> > > +				   struct v4l2_format *f)
> > > +{
> > > +	struct rcar_isp_core_io *io = video_drvdata(file);
> > > +
> > > +	risp_io_capture_try_format(io, &f->fmt.pix_mp);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int risp_io_capture_enum_framesizes(struct file *file, void *fh,
> > > +					   struct v4l2_frmsizeenum *fsize)
> > > +{
> > > +	if (fsize->index != 0)
> > > +		return -EINVAL;
> > > +
> > > +	switch (fsize->pixel_format) {
> > > +	case V4L2_PIX_FMT_NV16M:
> > > +	case V4L2_PIX_FMT_XBGR32:
> > > +		break;
> > > +	default:
> > > +		return -EINVAL;
> > > +	}
> > > +
> > > +	fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
> > > +
> > > +	fsize->stepwise.min_width = 128;
> > > +	fsize->stepwise.max_width = 5120;
> > > +	fsize->stepwise.step_width = 2;
> > > +
> > > +	fsize->stepwise.min_height = 128;
> > > +	fsize->stepwise.max_height = 4096;
> > > +	fsize->stepwise.step_height = 2;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static const struct v4l2_ioctl_ops risp_io_capture_ioctl_ops = {
> > > +	.vidioc_querycap		= risp_io_querycap,
> > > +
> > > +	.vidioc_enum_fmt_vid_cap	= risp_io_capture_enum_fmt,
> > > +	.vidioc_g_fmt_vid_cap_mplane	= risp_io_capture_g_fmt,
> > > +	.vidioc_s_fmt_vid_cap_mplane	= risp_io_capture_s_fmt,
> > > +	.vidioc_try_fmt_vid_cap_mplane	= risp_io_capture_try_fmt,
> > > +	.vidioc_enum_framesizes		= risp_io_capture_enum_framesizes,
> > > +
> > > +	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
> > > +	.vidioc_querybuf		= vb2_ioctl_querybuf,
> > > +	.vidioc_qbuf			= vb2_ioctl_qbuf,
> > > +	.vidioc_expbuf			= vb2_ioctl_expbuf,
> > > +	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
> > > +	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
> > > +	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
> > > +	.vidioc_streamon		= vb2_ioctl_streamon,
> > > +	.vidioc_streamoff		= vb2_ioctl_streamoff,
> > > +};
> > > +
> > > +/* -----------------------------------------------------------------------------
> > > + * Create and remove IO video devices
> > > + */
> > > +
> > > +int risp_core_io_create(struct device *dev, struct rcar_isp_core *core,
> > > +			struct rcar_isp_core_io *io, unsigned int pad)
> > > +{
> > > +	struct video_device *vdev = &io->vdev;
> > > +	struct vb2_queue *q = &io->queue;
> > > +	int ret;
> > > +
> > > +	switch (pad) {
> > > +	case RISP_CORE_INPUT1:
> > > +		snprintf(vdev->name, sizeof(vdev->name), "%s %s input1",
> > > +			 KBUILD_MODNAME, dev_name(dev));
> > > +		vdev->vfl_dir = VFL_DIR_TX;
> > > +		vdev->device_caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE;
> > > +		vdev->ioctl_ops = &risp_io_input_ioctl_ops;
> > > +
> > > +		q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
> > > +		q->ops = &risp_io_input_qops;
> > > +
> > > +		io->pad.flags = MEDIA_PAD_FL_SOURCE;
> > > +		io->format.fmt.pix_mp = risp_io_input_default_format;
> > > +		break;
> > > +
> > > +	case RISP_CORE_PARAMS:
> > > +		snprintf(vdev->name, sizeof(vdev->name), "%s %s params",
> > > +			 KBUILD_MODNAME, dev_name(dev));
> > > +		vdev->vfl_dir = VFL_DIR_TX;
> > > +		vdev->device_caps = V4L2_CAP_META_OUTPUT;
> > > +		vdev->ioctl_ops = &risp_io_params_ioctl_ops;
> > > +
> > > +		q->type = V4L2_BUF_TYPE_META_OUTPUT;
> > > +		q->ops = &risp_io_params_qops;
> > > +
> > > +		io->pad.flags = MEDIA_PAD_FL_SOURCE;
> > > +		io->format.fmt.meta = risp_io_params_default_format;
> > > +		break;
> > > +
> > > +	case RISP_CORE_STATS:
> > > +		snprintf(vdev->name, sizeof(vdev->name), "%s %s stats",
> > > +			 KBUILD_MODNAME, dev_name(dev));
> > > +		vdev->vfl_dir = VFL_DIR_RX;
> > > +		vdev->device_caps = V4L2_CAP_META_CAPTURE;
> > > +		vdev->ioctl_ops = &risp_io_stats_ioctl_ops;
> > > +
> > > +		q->type = V4L2_BUF_TYPE_META_CAPTURE;
> > > +		q->ops = &risp_io_stats_qops;
> > > +
> > > +		io->pad.flags = MEDIA_PAD_FL_SINK;
> > > +		io->format.fmt.meta = risp_io_stats_default_format;
> > > +		break;
> > > +
> > > +	case RISP_CORE_OUTPUT1:
> > > +		snprintf(vdev->name, sizeof(vdev->name), "%s %s output1",
> > > +			 KBUILD_MODNAME, dev_name(dev));
> > > +		vdev->vfl_dir = VFL_DIR_RX;
> > > +		vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE;
> > > +		vdev->ioctl_ops = &risp_io_capture_ioctl_ops;
> > > +
> > > +		q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
> > > +		q->ops = &risp_io_capture_qops;
> > > +
> > > +		io->pad.flags = MEDIA_PAD_FL_SINK;
> > > +		io->format.fmt.pix_mp = risp_io_capture_default_format;
> > > +		break;
> > > +	}
> > > +
> > > +	io->core = core;
> > > +
> > > +	mutex_init(&io->lock);
> > > +	INIT_LIST_HEAD(&io->buffers);
> > > +
> > > +	/* Create media graph pad. */
> > > +	ret = media_entity_pads_init(&io->vdev.entity, 1, &io->pad);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	/* Create queue */
> > > +	q->io_modes = VB2_MMAP | VB2_DMABUF;
> > > +	q->lock = &io->lock;
> > > +	q->drv_priv = io;
> > > +	q->mem_ops = &vb2_dma_contig_memops;
> > > +	q->buf_struct_size = sizeof(struct risp_buffer);
> > > +	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
> > > +	q->dev = dev;
> > > +
> > > +	ret = vb2_queue_init(q);
> > > +	if (ret < 0) {
> > > +		risp_io_err(io, "Failed to initialize VB2 queue\n");
> > > +		return ret;
> > > +	}
> > > +
> > > +	/* Create video device */
> > > +	vdev->v4l2_dev = &core->v4l2_dev;
> > > +	vdev->queue = &io->queue;
> > > +
> > > +	vdev->release = video_device_release_empty;
> > > +	vdev->lock = &io->lock;
> > > +	vdev->fops = &risp_io_fops;
> > > +
> > > +	vdev->device_caps |= V4L2_CAP_STREAMING | V4L2_CAP_IO_MC;
> > > +
> > > +	ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
> >
> > Have you tried compiling the driver as module and perform a
> > load/unload/load ?
> >
> > I'm afraid it might cause issues as this function is called in the
> > CS's internal_ops.registered() path, while the video device is
> > unregistered in risp_core_io_destory() which is in driver's remove()
> > call path.
> >
> > Should you add an handler for the CS's .unregister call ?
> >
> > I'm asking as I'm going through a similar issue with Mali, I have one
> > patch that address this and I wonder if this driver is affected by the
> > same problem.
>
> The while VIN pipeline, and V4L2 with it's lifetime management issues
> suffer from various issues in this area. That is why this driver
> registers itself with,
>
>     .suppress_bind_attrs = true,
>
> So there is no issue here ;-)
>

This suppresses module's unbinding via sysfs, and I take it as if go
through a module unload the .remove() handler will be called and the
video device will be unregistered there ?


> On a more serious note, I have a series I need to respin which will
> allow me to fix the lifetime management issues of the vdevs in R-Car
> VIN, which is the root of this issue in this graph. Once that is done I
> will start to slowly add test-cases and removing and reworking all R-Car
> video drivers involved in this graph to allow bind atttributes. As I
> can't test this now I will levae this as is, as fixing this (if it even
> is an issue) will surely trigger other issues in CSISP and other drivers
> in this media graph.
>

In my similar case with Mali, I also had to use video_device_alloc()
and registering two times a video device (even going through a
video_unregister_device()) requires to 0 the 'struct video_device'
before registering it again. I used the _alloc() variant to avoid
a memset, but I guess both are valid.

Just leaving it here for reference, I'm not asking to address this
now.

> >
> > > +	if (ret) {
> > > +		risp_io_err(io, "Failed to register video device\n");
> > > +		return ret;
> > > +	}
> > > +
> > > +	video_set_drvdata(&io->vdev, io);
> > > +
> > > +	v4l2_info(&core->v4l2_dev, "Device registered as %s\n",
> >
> > Why v4l2_info() and not simply dev_dbg() ?
>
> For good or bad all vdev devices in the R-Car capture pipeline prints
> this message at the info level.
>

So why not dev_info() ? it's my understanding v4l2_ debug helpers are
deprecated.

If you're using them in the rest of the R-Car pipeline then fine, I
guess they'll have to be replaced in one go then ?

> >
> > > +		  video_device_node_name(vdev));
> > > +
> > > +	switch (pad) {
> > > +	case RISP_CORE_INPUT1:
> > > +	case RISP_CORE_PARAMS:
> > > +		ret = media_create_pad_link(&io->vdev.entity, 0,
> > > +					    &core->subdev.entity, pad,
> > > +					    MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
> > > +		break;
> > > +	case RISP_CORE_STATS:
> > > +	case RISP_CORE_OUTPUT1:
> > > +		ret = media_create_pad_link(&core->subdev.entity, pad,
> > > +					    &io->vdev.entity, 0,
> > > +					    MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
> > > +		break;
> > > +	}
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +void risp_core_io_destory(struct rcar_isp_core_io *io)
> > > +{
> > > +	if (!video_is_registered(&io->vdev))
> > > +		return;
> > > +
> > > +	video_unregister_device(&io->vdev);
> > > +}
> > > diff --git a/drivers/media/platform/renesas/rcar-isp/core.c b/drivers/media/platform/renesas/rcar-isp/core.c
> > > new file mode 100644
> > > index 000000000000..20dd0fb3dc71
> > > --- /dev/null
> > > +++ b/drivers/media/platform/renesas/rcar-isp/core.c
> > > @@ -0,0 +1,826 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +
> > > +#include <linux/delay.h>
> > > +#include <linux/of_platform.h>
> > > +
> > > +#include <media/v4l2-ioctl.h>
> > > +#include <media/videobuf2-dma-contig.h>
> > > +#include <media/vsp1.h>
> > > +
> > > +#include "risp-core.h"
> > > +
> > > +#define ISP_CS_STREAMER_MODE_REG				0x7000
> > > +#define ISP_CS_STREAMER_MODE_STREAMER_EN			0xf
> > > +
> > > +#define ISP_CS_STREAMER_VBLANK_REG				0x7004
> > > +#define ISP_CS_STREAMER_HBLANK_REG				0x7008
> > > +
> > > +#define ISP_CS_STREAMER_CONFIG_DMA_CONTROL_REG			0x7100
> > > +#define ISP_CS_STREAMER_CONFIG_DMA_REG_ADDRESS_UPPER_8BIT_MASK	GENMASK(31, 24)
> > > +#define ISP_CS_STREAMER_CONFIG_DMA_ENABLE0			BIT(0)
> > > +
> > > +#define ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_REG			0x2100
> > > +#define ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_ENABLE1		BIT(31)
> > > +#define ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_CONFIG_DATA_START_REG_ADDRESS_MASK	GENMASK(15, 0)
> > > +
> > > +#define ISP_CS_STREAMER_CONFIG_DMA_CONTROL2_REG			0x2104
> > > +
> > > +#define ISP_CORE_ISPCORE_INT_STATUS			    0x80000
> > > +#define ISP_CORE_ISPCORE_INT_ENABLE			    0x80004
> > > +#define ISPCORE_DMA_IMAGE_FRAME_MODE(i, f)		    (0x84000 + 0x1000 * (i) + 0x100 * (f))
> > > +#define ISPCORE_DMA_IMAGE_FRAME_PIXEL_POSITION(i, f)	    (0x84004 + 0x1000 * (i) + 0x100 * (f))
> > > +#define ISPCORE_DMA_IMAGE_FRAME_PIXEL_BITWIDTH_MINUS1(i, f) (0x84008 + 0x1000 * (i) + 0x100 * (f))
> > > +#define ISPCORE_DMA_IMAGE_FRAME_PIXEL_BPP(i, f)		    (0x8400c + 0x1000 * (i) + 0x100 * (f))
> > > +#define ISPCORE_DMA_IMAGE_FRAME_BASE_ADDRESS_COMP0(i, f)    (0x84010 + 0x1000 * (i) + 0x100 * (f))
> > > +#define ISPCORE_DMA_IMAGE_FRAME_BASE_ADDRESS_COMP1(i, f)    (0x84014 + 0x1000 * (i) + 0x100 * (f))
> > > +#define ISPCORE_DMA_IMAGE_FRAME_BASE_ADDRESS_COMP2(i, f)    (0x84018 + 0x1000 * (i) + 0x100 * (f))
> > > +#define ISPCORE_DMA_IMAGE_FRAME_BASE_ADDRESS_COMP3(i, f)    (0x8401c + 0x1000 * (i) + 0x100 * (f))
> > > +#define ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP0(i, f)	    (0x84020 + 0x1000 * (i) + 0x100 * (f))
> > > +#define ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP1(i, f)	    (0x84024 + 0x1000 * (i) + 0x100 * (f))
> > > +#define ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP2(i, f)	    (0x84028 + 0x1000 * (i) + 0x100 * (f))
> > > +#define ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP3(i, f)	    (0x8402c + 0x1000 * (i) + 0x100 * (f))
> > > +#define ISPCORE_DMA_IMAGE_FRAME_AXI_ID(i, f)		    (0x84030 + 0x1000 * (i) + 0x100 * (f))
> > > +
> > > +#define ISPCORE_DMA_IMAGE_FLUSH_OUT_REG(i)			(0x84400 + 0x1000 * (i))
> > > +#define ISPCORE_DMA_IMAGE_FLUSH_OUT_PADDING_PIXEL_EOF_MASK	GENMASK(31, 16)
> > > +#define ISPCORE_DMA_IMAGE_FLUSH_OUT_PADDING_PIXEL_EOF_SHIFT	16
> > > +
> > > +#define ISPCORE_DMA_IMAGE_AXI_CONFIG_REG(i)			(0x84800 + 0x1000 * (i))
> > > +
> > > +static void risp_cs_write(struct rcar_isp_core *core, u32 offset, u32 value)
> > > +{
> > > +	iowrite32(value, core->csbase + offset);
> > > +}
> > > +
> > > +static u32 risp_cs_read(struct rcar_isp_core *core, u32 offset)
> > > +{
> > > +	return ioread32(core->csbase + offset);
> > > +}
> > > +
> > > +static void risp_core_write(struct rcar_isp_core *core, u32 offset, u32 value)
> > > +{
> > > +	iowrite32(value, core->base + offset);
> > > +}
> > > +
> > > +static u32 risp_core_read(struct rcar_isp_core *core, u32 offset)
> > > +{
> > > +	return ioread32(core->base + offset);
> > > +}
> > > +
> > > +static void risp_core_job_run_params(struct rcar_isp_core *core,
> > > +				     struct vsp1_isp_job_desc *vspx_job,
> > > +				     struct risp_buffer *buf)
> > > +{
> > > +	u32 *params_buf = (u32 *)buf->vsp_buffer.cpu_addr;
> > > +	bool have_config = !!params_buf[0];
> > > +	u32 ctrl0, ctrl1, ctrl2;
> > > +
> > > +	/*
> > > +	 * If we have a configuration but not asked the VSPX to programme it,
> >
> > s/programme/program ?
>
> Thanks.
>
> >
> > > +	 * use MMIO to write the configuration. This might be needed to work
> > > +	 * around limitations of the VSPX ConfigDMA.
> >
> > I would add a reference to the comment in risp_core_job_prepare() that
> > explains why MMIO has to be used for buffers < 16 entries.
> >
> > 	 * use MMIO to write the configuration. This might be needed to work
> > 	 * around limitations of the VSPX ConfigDMA as explained in
> >          * risp_core_job_prepare().
>
> Good idea!
>
> >
> >
> > > +	 */
> > > +	if (have_config && !vspx_job->config.pairs) {
> > > +		for (unsigned int i = 0; i < params_buf[0]; i++)
> > > +			risp_core_write(core, params_buf[2 + i * 2] & 0xffff,
> > > +					params_buf[3 + i * 2]);
> > > +
> > > +		/* Disable ConfigDMA. */
> > > +		have_config = false;
> > > +	}
> > > +
> > > +	ctrl0 = risp_cs_read(core, ISP_CS_STREAMER_CONFIG_DMA_CONTROL_REG) &
> > > +		~ISP_CS_STREAMER_CONFIG_DMA_ENABLE0;
> > > +	ctrl1 = risp_cs_read(core, ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_REG) &
> > > +		~(ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_ENABLE1 | 0xffff);
> > > +	ctrl2 = 0;
> > > +
> > > +	if (have_config) {
> > > +		ctrl0 |= ISP_CS_STREAMER_CONFIG_DMA_ENABLE0;
> > > +		ctrl1 |= ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_ENABLE1 |
> > > +			(params_buf[2] & 0xffff);
> > > +		ctrl2 = params_buf[3];
> > > +	}
> > > +
> > > +	risp_cs_write(core, ISP_CS_STREAMER_CONFIG_DMA_CONTROL_REG, ctrl0);
> > > +	risp_cs_write(core, ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_REG, ctrl1);
> > > +	risp_cs_write(core, ISP_CS_STREAMER_CONFIG_DMA_CONTROL2_REG, ctrl2);
> > > +}
> > > +
> > > +static void risp_core_job_run_output(struct rcar_isp_core *core,
> > > +				     struct risp_buffer *buf)
> > > +{
> > > +	const struct v4l2_format *fmt = &core->io[RISP_CORE_OUTPUT1].format;
> > > +	dma_addr_t mem;
> > > +	u32 reg;
> > > +
> > > +	for (unsigned int frame = 0; frame < 4; frame++) {
> > > +		reg = ISPCORE_DMA_IMAGE_FRAME_BASE_ADDRESS_COMP0(0, frame);
> > > +		mem = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
> > > +		risp_core_write(core, reg, mem);
> > > +
> > > +		/* Only NV16 uses 2 planes. */
> > > +		if (fmt->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_NV16M)
> > > +			continue;
> > > +
> > > +		reg = ISPCORE_DMA_IMAGE_FRAME_BASE_ADDRESS_COMP1(0, frame);
> > > +		mem = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 1);
> > > +		risp_core_write(core, reg, mem);
> > > +	}
> > > +}
> > > +
> > > +static void risp_core_job_run(struct rcar_isp_core *core)
> > > +{
> > > +	struct rcar_isp_job *job;
> > > +
> > > +	lockdep_assert_held(&core->lock);
> > > +
> > > +	/* ISP not yet started, nothing to do. */
> > > +	if (!core->streaming)
> > > +		return;
> > > +
> > > +	/* If we have active buffers in the ISP core, nothing to do. */
> > > +	if (core->vspx.job)
> > > +		return;
> > > +
> > > +	job = list_first_entry_or_null(&core->risp_jobs,
> > > +				       struct rcar_isp_job,
> > > +				       job_queue);
> > > +	if (!job)
> > > +		return;
> > > +
> > > +	list_del(&job->job_queue);
> > > +
> > > +	core->vspx.job = job;
> > > +
> > > +	/* Program the ISP register before kicking the VSPX. */
> > > +	for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
> > > +		struct risp_buffer *buf = job->buffers[i];
> > > +
> > > +		switch (i) {
> > > +		case RISP_CORE_PARAMS:
> > > +			risp_core_job_run_params(core, &job->vspx_job, buf);
> > > +			break;
> > > +		case RISP_CORE_OUTPUT1:
> > > +			risp_core_job_run_output(core, buf);
> > > +			break;
> > > +		}
> > > +	}
> > > +
> > > +	if (vsp1_isp_job_run(core->vspx.dev, &job->vspx_job)) {
> > > +		/*
> > > +		 * Release all buffers in this job if running on the VSPX
> > > +		 * failed. Userspace should recover from this, no new jobs are
> > > +		 * scheduled.
> > > +		 */
> > > +		for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
> > > +			struct risp_buffer *buf = job->buffers[i];
> > > +
> > > +			vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
> > > +		}
> > > +
> > > +		vsp1_isp_job_release(core->vspx.dev, &job->vspx_job);
> > > +		core->vspx.job = NULL;
> > > +		kfree(job);
> > > +
> > > +		dev_err(core->dev, "Failed to run job");
> > > +	}
> > > +}
> > > +
> > > +static int risp_core_pixfmt_to_vspx(u32 pixfmt)
> > > +{
> > > +	switch (pixfmt) {
> > > +	case V4L2_PIX_FMT_SBGGR8:
> > > +	case V4L2_PIX_FMT_SGBRG8:
> > > +	case V4L2_PIX_FMT_SGRBG8:
> > > +	case V4L2_PIX_FMT_SRGGB8:
> > > +		return V4L2_PIX_FMT_GREY;
> > > +	case V4L2_PIX_FMT_SBGGR10:
> > > +	case V4L2_PIX_FMT_SGBRG10:
> > > +	case V4L2_PIX_FMT_SGRBG10:
> > > +	case V4L2_PIX_FMT_SRGGB10:
> > > +		return V4L2_PIX_FMT_Y10;
> > > +	case V4L2_PIX_FMT_SBGGR12:
> > > +	case V4L2_PIX_FMT_SGBRG12:
> > > +	case V4L2_PIX_FMT_SGRBG12:
> > > +	case V4L2_PIX_FMT_SRGGB12:
> > > +		return V4L2_PIX_FMT_Y12;
> > > +	default:
> > > +		return -EINVAL;
> > > +	}
> > > +}
> > > +
> > > +int risp_core_job_prepare(struct rcar_isp_core *core)
> > > +{
> > > +	struct vsp1_isp_job_desc *vspx_job;
> > > +	int vspx_pixfmt = -EINVAL;
> > > +	struct rcar_isp_job *job;
> > > +	int ret;
> > > +
> > > +	lockdep_assert_held(&core->io_lock);
> > > +
> > > +	for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
> > > +		if (list_empty(&core->io[i].buffers))
> > > +			return 0;
> > > +	}
> > > +
> > > +	/* Memory is released when the job is consumed. */
> > > +	job = kzalloc(sizeof(*job), GFP_KERNEL);
> > > +	if (!job)
> > > +		return -ENOMEM;
> > > +
> > > +	vspx_job = &job->vspx_job;
> > > +
> > > +	for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
> > > +		struct risp_buffer *buf;
> > > +
> > > +		/*
> > > +		 * Extract buffer from the IO queue and save a reference in
> > > +		 * the job description. Buffers will be completed when the
> > > +		 * corresponding frame will be completed by the ISP.
> > > +		 */
> > > +		buf = list_first_entry_or_null(&core->io[i].buffers,
> > > +					       struct risp_buffer, list);
> > > +		if (!buf) {
> >
> > This shouldn't happen, right ? we just checked above that we have
> > buffers and we're helding io_lock.
>
> Correct, it should not happen.
>
> >
> > If we want to be defensive it doesn't hurt to keep this (maybe we
> > should even WARN_ON(!buf) as if this happens, something has really
> > gone wrong
>
> I prefers to be defensive here, will add WARN_ON(!buf). I will also add
> a comment this is done as defensive programming.

Thanks, good idea

>
> >
> > > +			ret = -EINVAL;
> > > +			goto error_return_buffers;
> > > +		}
> > > +
> > > +		switch (i) {
> > > +		case RISP_CORE_INPUT1: {
> > > +			u32 isp_pixfmt = core->io[i].format.fmt.pix_mp.pixelformat;
> > > +
> > > +			vspx_pixfmt = risp_core_pixfmt_to_vspx(isp_pixfmt);
> > > +
> > > +			vspx_job->img.fmt = core->io[i].format.fmt.pix_mp;
> > > +			vspx_job->img.fmt.pixelformat = vspx_pixfmt;
> > > +			vspx_job->img.mem =
> > > +				vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf,
> > > +							      0);
> > > +			break;
> > > +		}
> > > +		case RISP_CORE_PARAMS: {
> > > +			/*
> > > +			 * Work around undocumented behavior of the ConfigDMA
> > > +			 * interface by using MMIO if 16 or less pairs are to
> > > +			 * be programmed.
> > > +			 *
> > > +			 * Programing 15 or less pairs corrupts the image data
> > > +			 * following the config buffer, programing exactly 16
> > > +			 * pairs freeze the whole VSPX.
> > > +			 */
> > > +			u32 *params_buf = (u32 *)buf->vsp_buffer.cpu_addr;
> > > +
> > > +			if (params_buf[0] <= 16) {
> > > +				vspx_job->config.pairs = 0;
> > > +			} else {
> > > +				vspx_job->config.pairs = params_buf[0];
> > > +				vspx_job->config.mem = buf->vsp_buffer.dma_addr;
> > > +			}
> > > +			break;
> > > +		}
> > > +		}
> > > +
> > > +		list_del(&buf->list);
> > > +		job->buffers[i] = buf;
> > > +	}
> > > +
> > > +	if (vspx_pixfmt < 0) {
> > > +		ret = -EINVAL;
> > > +		goto error_return_buffers;
> > > +	}
> > > +
> > > +	ret = vsp1_isp_job_prepare(core->vspx.dev, vspx_job);
> > > +	if (ret)
> > > +		goto error_return_buffers;
> > > +
> > > +	scoped_guard(spinlock_irqsave, &core->lock) {
> > > +		list_add_tail(&job->job_queue, &core->risp_jobs);
> > > +		risp_core_job_run(core);
> > > +	}
> > > +
> > > +	return 0;
> > > +
> > > +error_return_buffers:
> > > +	for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
> > > +		if (!job->buffers[i])
> > > +			continue;
> > > +
> > > +		vb2_buffer_done(&job->buffers[i]->vb.vb2_buf,
> > > +				VB2_BUF_STATE_ERROR);
> > > +	}
> > > +	kfree(job);
> > > +	return ret;
> > > +}
> > > +
> > > +static int risp_core_config_output(struct rcar_isp_core *core,
> > > +				   unsigned int index,
> > > +				   const struct v4l2_pix_format_mplane *pix)
> > > +{
> > > +	/* For all frame capture slots. */
> > > +	for (unsigned int frame = 0; frame < 4; frame++) {
> > > +		switch (pix->pixelformat) {
> > > +		case V4L2_PIX_FMT_NV16M:
> > > +			risp_core_write(core,
> > > +					ISPCORE_DMA_IMAGE_FRAME_MODE(index, frame),
> > > +					1);
> > > +			risp_core_write(core,
> > > +					ISPCORE_DMA_IMAGE_FRAME_PIXEL_POSITION(index, frame),
> > > +					0 << 24 | 0 << 16 | 4 << 8 | 16 << 0);
> > > +			risp_core_write(core,
> > > +					ISPCORE_DMA_IMAGE_FRAME_PIXEL_BITWIDTH_MINUS1(index, frame),
> > > +					0 << 24 | 0 << 16 | 7 << 8 | 7 << 0);
> > > +			risp_core_write(core,
> > > +					ISPCORE_DMA_IMAGE_FRAME_PIXEL_BPP(index, frame),
> > > +					0 << 28 | 0 << 24 |
> > > +					0 << 20 | 0 << 16 |
> > > +					3 << 12 | 0 << 8 |
> > > +					3 << 4  | 0 << 0);
> > > +
> > > +			risp_core_write(core,
> > > +					ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP0(index, frame),
> > > +					pix->plane_fmt[0].bytesperline);
> > > +			risp_core_write(core,
> > > +					ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP1(index, frame),
> > > +					pix->plane_fmt[0].bytesperline);
> > > +			break;
> > > +		case V4L2_PIX_FMT_XBGR32:
> > > +			risp_core_write(core,
> > > +					ISPCORE_DMA_IMAGE_FRAME_MODE(index, frame),
> > > +					0);
> > > +			risp_core_write(core,
> > > +					ISPCORE_DMA_IMAGE_FRAME_PIXEL_POSITION(index, frame),
> > > +					0 << 24 | 0 << 16 | 0 << 8 | 0 << 0);
> > > +			risp_core_write(core,
> > > +					ISPCORE_DMA_IMAGE_FRAME_PIXEL_BITWIDTH_MINUS1(index, frame),
> > > +					0 << 24 | 0 << 16 | 0 << 8 | 23 << 0);
> > > +			risp_core_write(core,
> > > +					ISPCORE_DMA_IMAGE_FRAME_PIXEL_BPP(index, frame),
> > > +					0 << 28 | 0 << 24 |
> > > +					0 << 20 | 0 << 16 |
> > > +					0 << 12 | 0 << 8 |
> > > +					3 << 4  | 2 << 0);
> > > +
> > > +			risp_core_write(core,
> > > +					ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP0(index, frame),
> > > +					pix->plane_fmt[0].bytesperline);
> > > +			break;
> > > +		default:
> > > +			return -EINVAL;
> > > +		}
> > > +
> > > +		risp_core_write(core,
> > > +				ISPCORE_DMA_IMAGE_FRAME_AXI_ID(index, frame),
> > > +				0);
> > > +	}
> > > +
> > > +	/* Set image out flush EOF. */
> > > +	risp_core_write(core, ISPCORE_DMA_IMAGE_FLUSH_OUT_REG(index),
> > > +			pix->plane_fmt[0].bytesperline <<
> > > +			ISPCORE_DMA_IMAGE_FLUSH_OUT_PADDING_PIXEL_EOF_SHIFT);
> > > +
> > > +	/* Enable DMA and set burst length. */
> > > +	risp_core_write(core, ISPCORE_DMA_IMAGE_AXI_CONFIG_REG(index),
> > > +			BIT(31) | 7);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static u32 risp_core_pix2bus(const struct rcar_isp_core_io *io)
> > > +{
> > > +	switch (io->format.fmt.pix_mp.pixelformat) {
> > > +	case V4L2_PIX_FMT_SBGGR8:
> > > +		return MEDIA_BUS_FMT_SBGGR8_1X8;
> > > +	case V4L2_PIX_FMT_SGBRG8:
> > > +		return MEDIA_BUS_FMT_SGBRG8_1X8;
> > > +	case V4L2_PIX_FMT_SGRBG8:
> > > +		return MEDIA_BUS_FMT_SGRBG8_1X8;
> > > +	case V4L2_PIX_FMT_SRGGB8:
> > > +		return MEDIA_BUS_FMT_SRGGB8_1X8;
> > > +	case V4L2_PIX_FMT_SBGGR10:
> > > +		return MEDIA_BUS_FMT_SBGGR10_1X10;
> > > +	case V4L2_PIX_FMT_SGBRG10:
> > > +		return MEDIA_BUS_FMT_SGBRG10_1X10;
> > > +	case V4L2_PIX_FMT_SGRBG10:
> > > +		return MEDIA_BUS_FMT_SGRBG10_1X10;
> > > +	case V4L2_PIX_FMT_SRGGB10:
> > > +		return MEDIA_BUS_FMT_SRGGB10_1X10;
> > > +	case V4L2_PIX_FMT_SBGGR12:
> > > +		return MEDIA_BUS_FMT_SBGGR12_1X12;
> > > +	case V4L2_PIX_FMT_SGBRG12:
> > > +		return MEDIA_BUS_FMT_SGBRG12_1X12;
> > > +	case V4L2_PIX_FMT_SGRBG12:
> > > +		return MEDIA_BUS_FMT_SGRBG12_1X12;
> > > +	case V4L2_PIX_FMT_SRGGB12:
> > > +		return MEDIA_BUS_FMT_SRGGB12_1X12;
> > > +	case V4L2_PIX_FMT_XBGR32:
> > > +		return MEDIA_BUS_FMT_RGB888_1X24;
> > > +	case V4L2_PIX_FMT_NV16M:
> > > +		return MEDIA_BUS_FMT_YUYV12_1X24;
> > > +	default:
> > > +		return 0;
> > > +	}
> > > +}
> > > +
> > > +static void risp_core_try_next_job(struct rcar_isp_core *core)
> > > +{
> > > +	lockdep_assert_held(&core->lock);
> > > +
> > > +	struct rcar_isp_job *job = core->vspx.job;
> > > +
> > > +	/* If the ISP or the VSPX is not done with the job, wait. */
> > > +	if (!job || !job->done_isp || !job->done_vspx)
> > > +		return;
> > > +
> > > +	core->vspx.job = NULL;
> > > +	kfree(job);
> > > +
> > > +	core->sequence++;
> > > +
> > > +	/* Kickoff processing of next frame (if any). */
> > > +	risp_core_job_run(core);
> > > +}
> > > +
> > > +static void risp_core_svspx_frame_end(void *data)
> >
> > Should this be called risp_core_vspsx ...
>
> No ;-)
>

Why would you like to keep it named 'svspx' ? I might have forgo the
reason

> >
> > > +{
> > > +	struct rcar_isp_core *core = data;
> > > +
> > > +	guard(spinlock_irqsave)(&core->lock);
> > > +
> > > +	/*
> > > +	 * In tear-down the ISP may report a frame end event but we have already
> > > +	 * freed the job. It is safe to ignore the end of frame event.
> > > +	 */
> > > +	if (!core->vspx.job)
> > > +		return;
> > > +
> > > +	core->vspx.job->done_vspx = true;
> > > +	risp_core_try_next_job(core);
> > > +}
> > > +
> > > +int risp_core_start_streaming(struct rcar_isp_core *core)
> > > +{
> > > +	struct vsp1_vspx_frame_end vspx_fe = {
> > > +		.vspx_frame_end = risp_core_svspx_frame_end,
> > > +		.frame_end_data = core,
> > > +	};
> > > +
> > > +	struct v4l2_mbus_framefmt inputfmt = {
> > > +		.width = core->io[RISP_CORE_INPUT1].format.fmt.pix_mp.width,
> > > +		.height = core->io[RISP_CORE_INPUT1].format.fmt.pix_mp.height,
> > > +		.code = risp_core_pix2bus(&core->io[RISP_CORE_INPUT1]),
> > > +		.field = V4L2_FIELD_NONE,
> > > +		.colorspace = V4L2_COLORSPACE_RAW,
> > > +		.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT,
> > > +		.quantization = V4L2_QUANTIZATION_DEFAULT,
> > > +		.xfer_func = V4L2_XFER_FUNC_DEFAULT,
> >
> > Please adjust the colorspace related fields according to the format.
>
> Will do.
>
> > I -think- using _DEFAULT is discouraged ?
>
> Not sure to be honest.
>
> >
> > > +	};
> > > +
> > > +	struct v4l2_mbus_framefmt hvout = {
> > > +		.width = core->io[RISP_CORE_OUTPUT1].format.fmt.pix_mp.width,
> > > +		.height = core->io[RISP_CORE_OUTPUT1].format.fmt.pix_mp.height,
> > > +		.code = risp_core_pix2bus(&core->io[RISP_CORE_OUTPUT1]),
> > > +		.field = V4L2_FIELD_NONE,
> > > +		.colorspace = V4L2_COLORSPACE_SRGB,
> > > +		.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT,
> > > +		.quantization = V4L2_QUANTIZATION_DEFAULT,
> > > +		.xfer_func = V4L2_XFER_FUNC_DEFAULT,
> > > +	};
> > > +
> > > +	scoped_guard(mutex, &core->io_lock) {
> > > +		for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
> > > +			if (!core->io[i].streaming)
> > > +				return 0;
> > > +		}
> > > +
> > > +		/*
> > > +		 * The state core->streaming is protected by core->lock, which
> > > +		 * is not held yet. It is however safe to read it here since
> > > +		 * core->io_lock is held both in risp_core_stop_streaming() and
> > > +		 * here, the only two places the variable is modified.
> > > +		 *
> > > +		 * With this small implied dependency on the two locks for write
> > > +		 * access, the interrupt handler can safely depend sole on the
> > > +		 * spinlock core->lock for read access to core->streaming.
> > > +		 *
> > > +		 * The gain is an interrupt handler which can hold the spinlock
> > > +		 * and a start/stop procedure which can reset the ISP using the
> > > +		 * reset_control_reset() API, The later which can not be called
> > > +		 * from a context that may sleep.
> > > +		 *
> > > +		 * All other locations core->streaming is read and _all_
> > > +		 * locations where it is written core->lock is held.
> > > +		 */
> > > +		if (core->streaming)
> > > +			return 0;
> > > +
> > > +		/* Reset and wait for ISP core to initialize itself. */
> > > +		reset_control_reset(core->rstc);
> > > +		usleep_range(2000, 4000);
> > > +
> > > +		scoped_guard(spinlock_irqsave, &core->lock) {
> > > +			int ret;
> > > +
> > > +			risp_core_write(core, ISP_CORE_ISPCORE_INT_ENABLE, 1);
> > > +
> > > +			/* Configure output DMA */
> > > +			risp_core_config_output(core, 0,
> > > +						&core->io[RISP_CORE_OUTPUT1].format.fmt.pix_mp);
> > > +
> > > +			risp_cs_write(core, ISP_CS_STREAMER_VBLANK_REG, inputfmt.width * 25);
> > > +			risp_cs_write(core, ISP_CS_STREAMER_HBLANK_REG, 64);
> > > +
> > > +			/* Enable ISP Streaming bridge. */
> > > +			risp_cs_write(core, ISP_CS_STREAMER_MODE_REG,
> > > +				      ISP_CS_STREAMER_MODE_STREAMER_EN);
> > > +
> > > +			/* Start RPP ISP */
> > > +			ret = rppx1_start(core->rpp, &inputfmt, &hvout, NULL);
> > > +			if (ret)
> > > +				return ret;
> > > +
> > > +			core->vspx.job = NULL;
> > > +			core->sequence = 0;
> > > +			core->streaming = true;
> > > +		}
> > > +
> > > +		/* Start VSPX */
> > > +		vsp1_isp_start_streaming(core->vspx.dev, &vspx_fe);
> > > +
> > > +		scoped_guard(spinlock_irqsave, &core->lock) {
> > > +			risp_core_job_run(core);
> > > +		}
> > > +	}
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +void risp_core_stop_streaming(struct rcar_isp_core *core)
> > > +{
> > > +	struct rcar_isp_job *job, *tmp;
> > > +
> > > +	/*
> > > +	 * This function releases buffers and jobs: make sure the queues mutex
> > > +	 * is held.
> > > +	 */
> > > +	lockdep_assert_held(&core->io_lock);
> > > +
> > > +	scoped_guard(spinlock_irqsave, &core->lock) {
> > > +		/* Stop is called by each vdev, only act on the first call. */
> > > +		if (!core->streaming)
> > > +			return;
> > > +
> > > +		/* Stop queueing jobs to VSPX. */
> > > +		core->streaming = false;
> > > +	}
> > > +
> > > +	/* Wait for active VSPX job to finish. */
> > > +	for (unsigned int retry = 0; retry <= 10; retry++) {
> > > +		if (!core->vspx.job)
> > > +			break;
> > > +
> > > +		usleep_range(2000, 4000);
> > > +	}
> > > +
> > > +	if (core->vspx.job)
> > > +		dev_err(core->dev, "Failed to complete running job");
> > > +
> > > +	/* Free all buffers and switch of the hardware. */
> >
> > "switch off"
>
> Thanks.
>
> >
> > > +	scoped_guard(spinlock_irqsave, &core->lock) {
> > > +		/* Free all jobs and buffers. */
> > > +		list_for_each_entry_safe(job, tmp, &core->risp_jobs, job_queue) {
> > > +			vsp1_isp_job_release(core->vspx.dev, &job->vspx_job);
> > > +
> > > +			for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
> > > +				struct risp_buffer *buf = job->buffers[i];
> > > +
> > > +				vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
> > > +			}
> > > +
> > > +			list_del(&job->job_queue);
> > > +			kfree(job);
> > > +		}
> > > +
> > > +		rppx1_stop(core->rpp);
> > > +		risp_cs_write(core, ISP_CS_STREAMER_MODE_REG, 0);
> > > +		risp_core_write(core, ISP_CORE_ISPCORE_INT_ENABLE, 0);
> > > +	}
> > > +
> > > +	vsp1_isp_stop_streaming(core->vspx.dev);
> >
> > I'm not really going to review again the locking and vspx interfacing
> > as we spent quite some time in order to make it LOCKDEP-safe and
> > reliable. Speaking of which, please add:
> >
> > [ VSPX interfacing + locking sanitizing ]
> > Co-developed-by: Jacopo Mondi <jacopo.mondi+renesas@ideasonboard.com>
>
> Thank you.
>
> >
> > > +}
> > > +
> > > +static irqreturn_t risp_core_irq(int irq, void *data)
> > > +{
> > > +	struct rcar_isp_core *core = data;
> > > +	struct rcar_isp_job *job;
> > > +	u32 status;
> > > +
> > > +	status = risp_core_read(core, ISP_CORE_ISPCORE_INT_STATUS);
> > > +	if (!(status & BIT(0)))
> > > +		return IRQ_NONE;
> > > +
> > > +	if (!rppx1_interrupt(core->rpp, &status))
> > > +		return IRQ_HANDLED;
> > > +
> > > +	guard(spinlock_irqsave)(&core->lock);
> > > +
> > > +	job = core->vspx.job;
> > > +	if (!job)
> > > +		return IRQ_HANDLED;
> > > +
> > > +	for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
> > > +		struct risp_buffer *buf;
> > > +
> > > +		buf = job->buffers[i];
> > > +
> > > +		switch (i) {
> > > +		case RISP_CORE_STATS:
> > > +			rppx1_stats_fill_isr(core->rpp, status,
> > > +					     vb2_plane_vaddr(&buf->vb.vb2_buf, 0));
> > > +			fallthrough;
> > > +		case RISP_CORE_OUTPUT1:
> > > +		case RISP_CORE_INPUT1:
> > > +			buf->vb.sequence = core->sequence;
> > > +			buf->vb.vb2_buf.timestamp = ktime_get_ns();
> > > +			fallthrough;
> > > +		case RISP_CORE_PARAMS:
> > > +			vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
> > > +			break;
> > > +		}
> > > +	}
> > > +
> > > +	core->vspx.job->done_isp = true;
> > > +	risp_core_try_next_job(core);
> > > +
> > > +	return IRQ_HANDLED;
> > > +}
> > > +
> > > +static const struct v4l2_subdev_ops risp_core_subdev_ops = {
> > > +};
> > > +
> > > +static int risp_core_create_subdev(struct rcar_isp_core *core)
> > > +{
> > > +	struct v4l2_subdev *subdev = &core->subdev;
> > > +	int ret;
> > > +
> > > +	subdev->owner = THIS_MODULE;
> > > +	subdev->dev = core->dev;
> > > +	v4l2_subdev_init(subdev, &risp_core_subdev_ops);
> > > +	v4l2_set_subdevdata(subdev, core->dev);
> > > +	snprintf(subdev->name, sizeof(subdev->name), "%s %s core",
> > > +		 KBUILD_MODNAME, dev_name(core->dev));
> > > +	subdev->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
> > > +
> > > +	subdev->entity.function = MEDIA_ENT_F_VID_MUX;
> > > +
> > > +	core->pads[RISP_CORE_INPUT1].flags = MEDIA_PAD_FL_SINK;
> > > +	core->pads[RISP_CORE_PARAMS].flags = MEDIA_PAD_FL_SINK;
> > > +	core->pads[RISP_CORE_STATS].flags = MEDIA_PAD_FL_SOURCE;
> > > +	core->pads[RISP_CORE_OUTPUT1].flags = MEDIA_PAD_FL_SOURCE;
> > > +
> > > +	ret = media_entity_pads_init(&subdev->entity, RISP_CORE_NUM_PADS,
> > > +				     core->pads);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	dev_info(core->dev, "Registered ISP core\n");
> >
> > I would demote it to _dbg at most. And you've not registered yet if
> > I'm not mistaken
>
> Removed.
>
> >
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +int risp_core_registered(struct rcar_isp_core *core, struct v4l2_subdev *sd)
> > > +{
> > > +	int ret;
> > > +
> > > +	core->v4l2_dev.mdev = sd->v4l2_dev->mdev;
> > > +
> > > +	/* Register ISP Core subdevice. */
> > > +	ret = v4l2_device_register_subdev(&core->v4l2_dev, &core->subdev);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
> > > +		ret = risp_core_io_create(core->dev, core, &core->io[i], i);
> > > +		if (ret)
> > > +			return ret;
> >
> > Should the subdev be unregistered and the already created video device
> > destroyed ?
>
> Yes it should!
>
> And the function to do so should be renamed
> 's/risp_core_io_destory/risp_core_io_destroy/g' too ;-)

eheh good catch!

>
> >
> > > +	}
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int risp_core_probe_resources(struct rcar_isp_core *core,
> > > +				     struct platform_device *pdev)
> > > +{
> > > +	struct platform_device *vspx;
> > > +	struct device_node *of_vspx;
> > > +	struct resource *res;
> > > +	int ret;
> > > +
> > > +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core");
> > > +	if (!res)
> > > +		return -ENODEV;
> > > +
> > > +	core->rppaddr = res->start;
> > > +	core->base = devm_ioremap_resource(&pdev->dev, res);
> > > +	if (IS_ERR(core->base))
> > > +		return PTR_ERR(core->base);
> > > +
> > > +	ret = platform_get_irq_byname(pdev, "core");
> > > +	if (ret < 0)
> > > +		return -ENODEV;
> > > +
> > > +	ret = devm_request_irq(&pdev->dev, ret, risp_core_irq, IRQF_SHARED,
> > > +			       KBUILD_MODNAME, core);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	core->clk = devm_clk_get(&pdev->dev, "core");
> > > +	if (IS_ERR(core->clk))
> > > +		return -ENODEV;
> > > +
> > > +	core->rstc = devm_reset_control_get(&pdev->dev, "core");
> > > +	if (IS_ERR(core->rstc))
> > > +		return -ENODEV;
> > > +
> > > +	of_vspx = of_parse_phandle(pdev->dev.of_node, "renesas,vspx", 0);
> > > +	if (!of_vspx)
> > > +		return -ENODEV;
> > > +
> > > +	vspx = of_find_device_by_node(of_vspx);
> > > +	if (!vspx)
> > > +		return -ENODEV;
> > > +
> > > +	/* Attach to VSP-X */
> > > +	core->vspx.dev = &vspx->dev;
> > > +
> > > +	ret = vsp1_isp_init(&vspx->dev);
> > > +	if (ret < 0)
> > > +		return ret;
> > > +
> > > +	/* Attach ro the RPP library
> >
> > s/ro/to
>
> Thanks.
>
> >
> > > +	 *
> > > +	 * 1. Start and wait for the ISP to startup.
> > > +	 * 2. Attach the RPP library and talk with the RPP ISP.
> > > +	 * 3. Turn off ISP.
> > > +	 * 4. Fail if the RPP is unhappy with the hardware.
> > > +	 */
> > > +	ret = clk_prepare_enable(core->clk);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	usleep_range(2000, 4000);
> > > +
> > > +	core->rpp = rppx1_create(core->base, &pdev->dev);
> > > +
> > > +	clk_disable_unprepare(core->clk);
> > > +
> > > +	if (!core->rpp)
> > > +		return -ENODEV;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +int risp_core_probe(struct rcar_isp_core *core, struct platform_device *pdev,
> > > +		    void __iomem *csbase, struct reset_control *csrstc)
> > > +{
> > > +	int ret;
> > > +
> > > +	core->dev = &pdev->dev;
> > > +	core->csrstc = csrstc;
> > > +	core->csbase = csbase;
> > > +
> > > +	ret = risp_core_probe_resources(core, pdev);
> > > +	if (ret) {
> > > +		core->base = NULL;
> > > +		return ret;
> > > +	}
> > > +
> > > +	ret = v4l2_device_register(core->dev, &core->v4l2_dev);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	ret = risp_core_create_subdev(core);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	mutex_init(&core->io_lock);
> > > +	spin_lock_init(&core->lock);
> > > +	INIT_LIST_HEAD(&core->risp_jobs);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +void risp_core_remove(struct rcar_isp_core *core)
> > > +{
> > > +	/* If we did not probe the ISP core, nothing to do. */
> > > +	if (!core->base)
> > > +		return;
> > > +
> > > +	dev_info(core->dev, "Remove ISP Core\n");
> > > +
> > > +	for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++)
> > > +		risp_core_io_destory(&core->io[i]);
> > > +
> > > +	mutex_destroy(&core->io_lock);
> > > +	rppx1_destroy(core->rpp);
> > > +}
> > > diff --git a/drivers/media/platform/renesas/rcar-isp/csisp.c b/drivers/media/platform/renesas/rcar-isp/csisp.c
> > > index 8fb2cc3b5650..53ce47020d17 100644
> > > --- a/drivers/media/platform/renesas/rcar-isp/csisp.c
> > > +++ b/drivers/media/platform/renesas/rcar-isp/csisp.c
> > > @@ -11,14 +11,13 @@
> > >   */
> > >
> > >  #include <linux/module.h>
> > > -#include <linux/mutex.h>
> > >  #include <linux/of.h>
> > > -#include <linux/platform_device.h>
> > >  #include <linux/pm_runtime.h>
> > > -#include <linux/reset.h>
> > >
> > >  #include <media/mipi-csi2.h>
> > > -#include <media/v4l2-subdev.h>
> > > +#include <media/v4l2-async.h>
> > > +
> > > +#include "risp-core.h"
> > >
> > >  #define ISPINPUTSEL0_REG				0x0008
> > >  #define ISPINPUTSEL0_SEL_CSI0				BIT(31)
> > > @@ -158,6 +157,7 @@ struct rcar_isp {
> > >  	struct device *dev;
> > >  	void __iomem *csbase;
> > >  	struct reset_control *rstc;
> > > +	struct rcar_isp_core core;
> > >
> > >  	enum rcar_isp_input csi_input;
> > >
> > > @@ -451,6 +451,21 @@ static int risp_parse_dt(struct rcar_isp *isp)
> > >  	return ret;
> > >  }
> > >
> > > +/* -----------------------------------------------------------------------------
> > > + * ISP Core connection
> > > + */
> > > +
> > > +static int risp_cs_registered(struct v4l2_subdev *sd)
> > > +{
> > > +	struct rcar_isp *isp = sd_to_isp(sd);
> > > +
> > > +	return risp_core_registered(&isp->core, sd);
> > > +}
> > > +
> > > +static const struct v4l2_subdev_internal_ops risp_cs_internal_ops = {
> > > +	.registered = risp_cs_registered,
> >
> > Back on the module/load unload thing I hinted previously, this is
> > called when the vin's notifier matches on the cs subdev. Does
> > unloading and then reloading the VIN module cause issues ?
>
> Same as above, yes it does and this is why this is not allowed ATM. I
> will fix this for VIN and then make sure it works for all other drivers
> in the R-Car pipeline.

Just to be clear (and again, I don't think it shall be fixed now),
does disallowing module unbinding from sysfs prevents a module from
being unloaded ?

>
> >
> > > +};
> > > +
> > >  /* -----------------------------------------------------------------------------
> > >   * Platform Device Driver
> > >   */
> > > @@ -477,7 +492,7 @@ static int risp_probe_resources(struct rcar_isp *isp,
> > >  	if (IS_ERR(isp->csbase))
> > >  		return PTR_ERR(isp->csbase);
> > >
> > > -	isp->rstc = devm_reset_control_get(&pdev->dev, NULL);
> > > +	isp->rstc = devm_reset_control_get_shared(&pdev->dev, NULL);
> > >
> > >  	return PTR_ERR_OR_ZERO(isp->rstc);
> > >  }
> > > @@ -541,14 +556,31 @@ static int risp_probe(struct platform_device *pdev)
> > >  	if (ret)
> > >  		goto error_notifier;
> > >
> > > -	ret = v4l2_async_register_subdev(&isp->subdev);
> > > -	if (ret < 0)
> > > +	ret = risp_core_probe(&isp->core, pdev, isp->csbase, isp->rstc);
> > > +	switch (ret) {
> > > +	case 0:
> > > +		/* The device have an ISP core. */
> > > +		isp->subdev.internal_ops = &risp_cs_internal_ops;
> > > +		break;
> > > +	case -ENODEV:
> > > +		/* The device don't have an ISP core, that is OK. */
> > > +		ret = 0;
> > > +		break;
> > > +	default:
> > > +		/* Something went wrong registering the ISP core. */
> > >  		goto error_subdev;
> > > +	}
> > > +
> > > +	ret = v4l2_async_register_subdev(&isp->subdev);
> > > +	if (ret < 0)
> > > +		goto error_core;
> > >
> > >  	dev_info(isp->dev, "Using CSI-2 input: %u\n", isp->csi_input);
> > >
> > >  	return 0;
> > >
> > > +error_core:
> > > +	risp_core_remove(&isp->core);
> > >  error_subdev:
> > >  	v4l2_subdev_cleanup(&isp->subdev);
> > >  error_notifier:
> > > @@ -564,6 +596,8 @@ static void risp_remove(struct platform_device *pdev)
> > >  {
> > >  	struct rcar_isp *isp = platform_get_drvdata(pdev);
> > >
> > > +	risp_core_remove(&isp->core);
> > > +
> > >  	v4l2_async_nf_unregister(&isp->notifier);
> > >  	v4l2_async_nf_cleanup(&isp->notifier);
> > >
> > > diff --git a/drivers/media/platform/renesas/rcar-isp/risp-core.h b/drivers/media/platform/renesas/rcar-isp/risp-core.h
> > > new file mode 100644
> > > index 000000000000..4c848551c1ef
> > > --- /dev/null
> > > +++ b/drivers/media/platform/renesas/rcar-isp/risp-core.h
> > > @@ -0,0 +1,170 @@
> > > +/* SPDX-License-Identifier: GPL-2.0+ */
> > > +
> > > +#ifndef __RCAR_ISP__
> > > +#define __RCAR_ISP__
> > > +
> > > +#include <linux/clk.h>
> > > +#include <linux/mutex.h>
> > > +#include <linux/platform_device.h>
> > > +#include <linux/reset.h>
> >
> > #include <linux/spinlock.h>
>
> Thanks.
>
> >
> > > +#include <linux/videodev2.h>
> > > +
> > > +#include <media/v4l2-device.h>
> > > +#include <media/v4l2-subdev.h>
> > > +#include <media/videobuf2-core.h>
> > > +#include <media/videobuf2-dma-contig.h>
> > > +
> > > +#include <media/rppx1.h>
> > > +#include <media/vsp1.h>
> > > +
> > > +struct rcar_isp_core;
> > > +
> > > +enum risp_core_pads {
> > > +	RISP_CORE_INPUT1,
> > > +	RISP_CORE_PARAMS,
> > > +	RISP_CORE_STATS,
> > > +	RISP_CORE_OUTPUT1,
> > > +	RISP_CORE_NUM_PADS,
> > > +};
> > > +
> > > +/**
> > > + * struct risp_buffer - Describe an IO buffer
> > > + * @vb:		The VB2 buffer
> > > + * @list:	List of buffers queued to the IO queue
> > > + * @vsp_buffer:	Buffer mapped from VSP-X, only used for params IO
> > > + */
> > > +struct risp_buffer {
> > > +	struct vb2_v4l2_buffer vb;
> > > +	struct list_head list;
> > > +	struct vsp1_isp_buffer_desc vsp_buffer;
> > > +};
> > > +
> > > +/**
> > > + * struct rcar_isp_core_io - Information for a IO video devices
> > > + * @core:	Backlink to the common ISP core structure
> > > + *
> > > + * @lock:	Protects @vdev, @pad and @queue + open/close fops
> > > + * @vdev:	V4L2 video device associated with this IO port
> > > + * @pad:	Media pad for @vdev
> > > + * @queue:	VB2 buffers queue for $@vdev
> > > + *
> > > + * @qlock:	Protects @streaming and @buffers
> > > + * @streaming:	Flag to indicate if device is streaming, or not
> > > + * @buffers:	List of buffers queued to the device
> > > + *
> > > + * @format:	The active V4L2 format
> > > + */
> > > +struct rcar_isp_core_io {
> > > +	struct rcar_isp_core *core;
> > > +
> > > +	struct mutex lock; /* See KDoc block. */
> > > +	struct video_device vdev;
> > > +	struct media_pad pad;
> > > +	struct vb2_queue queue;
> > > +
> > > +	bool streaming;
> > > +	struct list_head buffers;
> > > +
> > > +	struct v4l2_format format;
> > > +};
> > > +
> > > +/**
> > > + * struct rcar_isp_job - R-Car ISP job description
> > > + *
> > > + * Both done_isp and done_vspx shall be set before the job can be considered
> > > + * completely done.
> > > + *
> > > + * @buffers: IO buffers that form a job
> > > + * @vspx_job: VSPX job description
> > > + * @job_queue: list handle
> > > + * @done_isp: Flag to indicate the ISP is done with the job
> > > + * @done_vspx: Flag to indicate the VSPX is done with the job
> > > + */
> > > +struct rcar_isp_job {
> > > +	struct risp_buffer *buffers[RISP_CORE_NUM_PADS];
> > > +	struct vsp1_isp_job_desc vspx_job;
> > > +	struct list_head job_queue;
> > > +	bool done_isp;
> > > +	bool done_vspx;
> > > +};
> > > +
> > > +/**
> > > + * struct rcar_isp_vspx - R-Car ISP job description
> > > + *
> > > + * @dev: Device reference to VSPX
> > > + * @job: Job currently being processed by VSPX
> > > + */
> > > +struct rcar_isp_vspx {
> > > +	struct device *dev;
> > > +	struct rcar_isp_job *job;
> > > +};
> > > +
> > > +/**
> > > + * struct rcar_isp_core - ISP Core
> > > + * @dev:	(OF) device
> > > + * @rppaddr:	Hardware address of the RPP ISP (from OF)
> > > + * @clk:	The clock for the ISP CORE
> > > + * @rstc:	The reset for the ISP Core
> > > + * @csrstc:	The reset for the ISP Channel Selector
> > > + *
> > > + * @base:	MMIO base of the ISP CORE
> > > + * @csbase:	MMIO base of the ISP CS
> > > + *
> > > + * @subdev:	V4L2 subdevice to represent the ISP CORE
> > > + * @pads:	Media pad for @subdev
> > > + *
> > > + * @v4l2_dev:	V4L2 device
> > > + * @rpp:	Handle to the RPP ISP connected to the ISP CORE
> > > + *
> > > + * @io_lock:	Protect io[*].streaming and io[*].buffers
> > > + * @io:		Array of IO ports to the ISP CORE
> > > + *
> > > + * @lock:	Protects @vspx, @risp_jobs, @sequence and @streaming
> > > + * @vspx:	Handle to the resources used by VSPX connected to the ISP CORE
> > > + * @risp_jobs:	Queue of VSPX transfer jobs
> > > + * @sequence:	V4L2 buffers sequence number
> > > + * @streaming:	Tracks if the device is streaming
> > > + */
> > > +struct rcar_isp_core {
> > > +	struct device *dev;
> > > +
> > > +	u32 rppaddr;
> > > +	struct clk *clk;
> > > +
> > > +	struct reset_control *rstc;
> > > +	struct reset_control *csrstc;
> > > +
> > > +	void __iomem *base;
> > > +	void __iomem *csbase;
> > > +
> > > +	struct v4l2_subdev subdev;
> > > +	struct media_pad pads[RISP_CORE_NUM_PADS];
> > > +
> > > +	struct v4l2_device v4l2_dev;
> > > +	struct rppx1 *rpp;
> > > +
> > > +	struct mutex io_lock; /* See KDoc block. */
> > > +	struct rcar_isp_core_io io[RISP_CORE_NUM_PADS];
> > > +
> > > +	spinlock_t lock;  /* See KDoc block. */
> > > +	struct rcar_isp_vspx vspx;
> > > +	struct list_head risp_jobs;
> > > +	unsigned int sequence;
> > > +	bool streaming;
> > > +};
> > > +
> > > +int risp_core_probe(struct rcar_isp_core *core, struct platform_device *pdev,
> > > +		    void __iomem *csbase, struct reset_control *csrstc);
> > > +void risp_core_remove(struct rcar_isp_core *core);
> > > +int risp_core_registered(struct rcar_isp_core *core, struct v4l2_subdev *sd);
> > > +
> > > +int risp_core_job_prepare(struct rcar_isp_core *core);
> > > +
> > > +int risp_core_start_streaming(struct rcar_isp_core *core);
> > > +void risp_core_stop_streaming(struct rcar_isp_core *core);
> > > +
> > > +int risp_core_io_create(struct device *dev, struct rcar_isp_core *core,
> > > +			struct rcar_isp_core_io *io, unsigned int pad);
> > > +void risp_core_io_destory(struct rcar_isp_core_io *io);
> > > +
> > > +#endif
> > > --
> >
> > Overall, this driver is robust and has been thoroughly tested in the
> > last 2 years, so I would be happy to see it merged soon.
>
> Me too.
>
> >
> > Of all the comments I had, only two things need to be clarified:
> > - The IO power routines
>
> Fixed.
>
> > - Module loading/unloading
>
> This is a larger issue for all drivers in the R-Car capture pipeline and
> the root cause is the VIN driver and V4L2 lifetime management of vdev's
> registered at v4l-async complete and not at probe time. I have a series
> which once merged I will be allowed to registered vdev at probe time
> (review comment from Hans once I tried to fix this).
>
> Once this is fixed for VIN I will enable bind attributes for all drivers
> (rcar-vin, rcar-csi2 and rcar-isp) and verify this works. As it is now I
> can not test this without first fixing VIN.
>
> >
> > everything else is minor and I hope doesn't cause any rework.
>
> Not too bad. Thanks for pushing the colorspace fields I had neglected
> them.

Yeah, we should actually clarify if using _DEFAULT for colorspace
fields is ok. Anyway, being precise and explicit is certainly better.

Thanks
  j

>
> >
> > Thanks
> >   j
> >
> > > 2.54.0
> > >
> > >
>
> --
> Kind Regards,
> Niklas Söderlund
>

  reply	other threads:[~2026-06-03  9:11 UTC|newest]

Thread overview: 39+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-04  1:05 [v8 00/14] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Niklas Söderlund
2026-05-04  1:05 ` [v8 01/14] media: Add RPP_X1_PARAMS and RPP_X1_STATS meta formats Niklas Söderlund
2026-05-06  7:45   ` Jacopo Mondi
2026-05-04  1:05 ` [v8 02/14] media: rppx1: Add framework to support Dreamchip RPPX1 ISP Niklas Söderlund
2026-05-06  8:53   ` Jacopo Mondi
2026-05-16 15:17     ` Niklas Söderlund
2026-06-03  8:27       ` Jacopo Mondi
2026-05-04  1:05 ` [v8 03/14] media: rcar-isp: Add support for ISPCORE Niklas Söderlund
2026-05-06 15:02   ` Jacopo Mondi
2026-05-16 20:16     ` Niklas Söderlund
2026-06-03  9:11       ` Jacopo Mondi [this message]
2026-06-03 12:45         ` Niklas Söderlund
2026-05-04  1:05 ` [v8 04/14] media: rppx1: wbmeas: Add support for white balance measurement Niklas Söderlund
2026-05-06 13:58   ` Antoine Bouyer
2026-05-06 14:22     ` Jacopo Mondi
2026-05-16 20:27       ` Niklas Söderlund
2026-05-04  1:05 ` [v8 05/14] media: rppx1: awbg: Add support for white balance gain settings Niklas Söderlund
2026-05-04  1:05 ` [v8 06/14] media: rppx1: exm: Add support for exposure measurement Niklas Söderlund
2026-05-04  1:05 ` [v8 07/14] media: rppx1: hist: Add support histogram measurement Niklas Söderlund
2026-05-06 15:51   ` Jacopo Mondi
2026-05-16 20:31     ` Niklas Söderlund
2026-05-04  1:05 ` [v8 08/14] media: rppx1: bls: Add support for black level compensation Niklas Söderlund
2026-05-06 15:25   ` Jacopo Mondi
2026-05-16 20:44     ` Niklas Söderlund
2026-06-03  9:35       ` Jacopo Mondi
2026-05-04  1:05 ` [v8 09/14] media: rppx1: ccor: Add support for color correction matrix Niklas Söderlund
2026-05-04  1:05 ` [v8 10/14] media: rppx1: lsc: Add support for lens shade correction Niklas Söderlund
2026-05-04  1:05 ` [v8 11/14] media: rppx1: ga: Add support for gamma out correction Niklas Söderlund
2026-05-04  1:05 ` [v8 12/14] media: rppx1: db: Add support for debayering filters Niklas Söderlund
2026-05-06 15:54   ` Jacopo Mondi
2026-05-16 20:47     ` Niklas Söderlund
2026-05-04  1:05 ` [v8 13/14] media: rppx1: bd: Add support for bilateral denoising Niklas Söderlund
2026-05-04  1:05 ` [v8 14/14] media: rppx1: lin: Add support for gamma sensor linearization Niklas Söderlund
2026-05-06 15:57   ` Jacopo Mondi
2026-05-16 20:56     ` Niklas Söderlund
2026-05-06 12:19 ` [v8 00/14] media: Add support for R-Car ISP using Dreamchip RPPX1 ISP Geert Uytterhoeven
2026-05-06 12:29   ` Niklas Söderlund
2026-05-06 12:49     ` Jacopo Mondi
2026-05-06 12:56       ` Geert Uytterhoeven

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ah_qnWSHVx6oN25g@zed \
    --to=jacopo.mondi@ideasonboard.com \
    --cc=jai.luthra+renesas@ideasonboard.com \
    --cc=kuninori.morimoto.gx@renesas.com \
    --cc=laurent.pinchart@ideasonboard.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-media@vger.kernel.org \
    --cc=linux-renesas-soc@vger.kernel.org \
    --cc=mchehab@kernel.org \
    --cc=niklas.soderlund+renesas@ragnatech.se \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox