Re: [PATCH v8] media: vsp1: Add VSPX support

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

 



Hi Laurent

On Fri, May 02, 2025 at 11:26:44PM +0300, Laurent Pinchart wrote:
> Hi Jacopo,
>
> Thank you for the patch.
>
> On Fri, May 02, 2025 at 03:23:10PM +0200, Jacopo Mondi wrote:
> > Add support for VSPX, a specialized version of the VSP2 that
> > transfers data to the ISP. The VSPX is composed of two RPF units
> > to read data from external memory and an IIF instance that performs
> > transfer towards the ISP.
> >
> > The VSPX is supported through a newly introduced vsp1_vspx.c file that
> > exposes two interfaces: vsp1_vspx interface, declared in vsp1_vspx.h
> > for the vsp1 core to initialize and cleanup the VSPX, and a vsp1_isp
> > interface, declared in include/media/vsp1.h for the ISP driver to
> > control the VSPX operations.
> >
> > Signed-off-by: Jacopo Mondi <jacopo.mondi+renesas@xxxxxxxxxxxxxxxx>
> > ---
> > The VSPX is a VSP2 function that reads data from external memory using
> > two RPF instances and feed it to the ISP.
> >
> > The VSPX includes an IIF unit (ISP InterFace) modeled in the vsp1 driver
> > as a new, simple, entity type.
> >
> > IIF is part of VSPX, a version of the VSP2 IP specialized for ISP
> > interfacing. To prepare to support VSPX, support IIF first by
> > introducing a new entity and by adjusting the RPF/WPF drivers to
> > operate correctly when an IIF is present.
> >
> > Changes in v8:
> > - Remove patches already collected by Laurent in
> >   [GIT PULL FOR v6.16] Renesas media drivers changes
> >
> > - Rebased on
> >   https://gitlab.freedesktop.org/linux-media/users/pinchartl.git #renesas-next
> >
> > - Changes to the VSPX interface towards the ISP
> >   - Split start/stop_streaming
> >   - Add vsp1_isp_jobs_release() to release pending jobs
> >   - Add vsp1_isp_free_buffer()
> >   - Remove vsp1_isp_configure() and compute partitions on job creation
> >
> > - Driver changes
> >   - Drop irq-driver flow
> >     The VSPX used to schedule new jobs as soon as processing the last
> >     one is done. This doesn't work well with the R-Car ISP design
> >     for two reasons:
> >     - The ISP needs per-job registers programming
> >     - The ISP and VSPX job queues have to stay in sync
> >
> > - Minors
> >   - Remove the jobs_lock as a single lock is fine
> >   - Protect against VSPX/ISP irq races in job_run() by checking the
> >     VSPX 'busy' register and remove the 'processing' flag
> >   - Manually set the pipeline state to STOPPED before scheduling a new
> >     job without waiting for frame_end
> >
> > Changes in v7:
> > - Include VSPX driver in the series
> > - Use existing VSP1 formats and remove patches extending formats on RPF
> > - Rework VSPX driver to split jobs creation and scheduling in two
> >   different API entry points
> > - Fix VSPX stride using the user provided bytesperline and using the
> >   buffer size for ConfigDMA buffers
> > - Link to v6: https://lore.kernel.org/r/20250321-v4h-iif-v6-0-361e9043026a@xxxxxxxxxxxxxxxx
> >
> > Changes in v6:
> > - Little cosmetic change as suggested by Laurent
> > - Collect tags
> > - Link to v5: https://lore.kernel.org/r/20250319-v4h-iif-v5-0-0a10456d792c@xxxxxxxxxxxxxxxx
> >
> > Changes in v5:
> > - Drop additional empty line 5/6
> > - Link to v4: https://lore.kernel.org/r/20250318-v4h-iif-v4-0-10ed4c41c195@xxxxxxxxxxxxxxxx
> >
> > Changes in v4:
> > - Fix SWAP bits for RAW10, RAW12 and RAW16
> > - Link to v3: https://lore.kernel.org/r/20250317-v4h-iif-v3-0-63aab8982b50@xxxxxxxxxxxxxxxx
> >
> > Changes in v3:
> > - Drop 2/6 from v2
> > - Add 5/7 to prepare for a new implementation of 6/7
> > - Individual changelog per patch
> > - Add 7/7
> > - Link to v2: https://lore.kernel.org/r/20250224-v4h-iif-v2-0-0305e3c1fe2d@xxxxxxxxxxxxxxxx
> >
> > Changes in v2:
> > - Collect tags
> > - Address review comments from Laurent, a lot of tiny changes here and
> >   there but no major redesign worth an entry in the patchset changelog
>
> You've come a long way since v1, I think we're getting close to a good
> implementation.
>

Thank you and Niklas for reviews and testing

> > ---
> >  drivers/media/platform/renesas/vsp1/Makefile    |   1 +
> >  drivers/media/platform/renesas/vsp1/vsp1.h      |   1 +
> >  drivers/media/platform/renesas/vsp1/vsp1_drv.c  |  13 +-
> >  drivers/media/platform/renesas/vsp1/vsp1_regs.h |   1 +
> >  drivers/media/platform/renesas/vsp1/vsp1_vspx.c | 664 ++++++++++++++++++++++++
> >  drivers/media/platform/renesas/vsp1/vsp1_vspx.h |  16 +
> >  include/media/vsp1.h                            |  75 +++
> >  7 files changed, 770 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/media/platform/renesas/vsp1/Makefile b/drivers/media/platform/renesas/vsp1/Makefile
> > index de8c802e1d1a..2057c8f7be47 100644
> > --- a/drivers/media/platform/renesas/vsp1/Makefile
> > +++ b/drivers/media/platform/renesas/vsp1/Makefile
> > @@ -6,5 +6,6 @@ vsp1-y					+= vsp1_clu.o vsp1_hsit.o vsp1_lut.o
> >  vsp1-y					+= vsp1_brx.o vsp1_sru.o vsp1_uds.o
> >  vsp1-y					+= vsp1_hgo.o vsp1_hgt.o vsp1_histo.o
> >  vsp1-y					+= vsp1_iif.o vsp1_lif.o vsp1_uif.o
> > +vsp1-y					+= vsp1_vspx.o
> >
> >  obj-$(CONFIG_VIDEO_RENESAS_VSP1)	+= vsp1.o
> > diff --git a/drivers/media/platform/renesas/vsp1/vsp1.h b/drivers/media/platform/renesas/vsp1/vsp1.h
> > index 263024639dd2..1872e403f26b 100644
> > --- a/drivers/media/platform/renesas/vsp1/vsp1.h
> > +++ b/drivers/media/platform/renesas/vsp1/vsp1.h
> > @@ -110,6 +110,7 @@ struct vsp1_device {
> >  	struct media_entity_operations media_ops;
> >
> >  	struct vsp1_drm *drm;
> > +	struct vsp1_vspx *vspx;
> >  };
> >
> >  int vsp1_device_get(struct vsp1_device *vsp1);
> > diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drv.c b/drivers/media/platform/renesas/vsp1/vsp1_drv.c
> > index d13e9b31aa7c..e338ab8af292 100644
> > --- a/drivers/media/platform/renesas/vsp1/vsp1_drv.c
> > +++ b/drivers/media/platform/renesas/vsp1/vsp1_drv.c
> > @@ -38,6 +38,7 @@
> >  #include "vsp1_uds.h"
> >  #include "vsp1_uif.h"
> >  #include "vsp1_video.h"
> > +#include "vsp1_vspx.h"
> >
> >  /* -----------------------------------------------------------------------------
> >   * Interrupt Handling
> > @@ -488,7 +489,10 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
> >
> >  		ret = media_device_register(mdev);
> >  	} else {
> > -		ret = vsp1_drm_init(vsp1);
> > +		if (vsp1->info->version == VI6_IP_VERSION_MODEL_VSPX_GEN4)
> > +			ret = vsp1_vspx_init(vsp1);
> > +		else
> > +			ret = vsp1_drm_init(vsp1);
> >  	}
> >
> >  done:
> > @@ -846,6 +850,13 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
> >  		.uif_count = 2,
> >  		.wpf_count = 1,
> >  		.num_bru_inputs = 5,
> > +	}, {
> > +		.version = VI6_IP_VERSION_MODEL_VSPX_GEN4,
> > +		.model = "VSP2-X",
> > +		.gen = 4,
> > +		.features = VSP1_HAS_IIF,
> > +		.rpf_count = 2,
> > +		.wpf_count = 1,
> >  	},
> >  };
> >
> > diff --git a/drivers/media/platform/renesas/vsp1/vsp1_regs.h b/drivers/media/platform/renesas/vsp1/vsp1_regs.h
> > index 86e47c2d991f..10cfbcd1b6e0 100644
> > --- a/drivers/media/platform/renesas/vsp1/vsp1_regs.h
> > +++ b/drivers/media/platform/renesas/vsp1/vsp1_regs.h
> > @@ -799,6 +799,7 @@
> >  #define VI6_IP_VERSION_MODEL_VSPDL_GEN3	(0x19 << 8)
> >  #define VI6_IP_VERSION_MODEL_VSPBS_GEN3	(0x1a << 8)
> >  #define VI6_IP_VERSION_MODEL_VSPD_GEN4	(0x1c << 8)
> > +#define VI6_IP_VERSION_MODEL_VSPX_GEN4	(0x1d << 8)
> >  /* RZ/G2L SoCs have no version register, So use 0x80 as the model version */
> >  #define VI6_IP_VERSION_MODEL_VSPD_RZG2L	(0x80 << 8)
> >
> > diff --git a/drivers/media/platform/renesas/vsp1/vsp1_vspx.c b/drivers/media/platform/renesas/vsp1/vsp1_vspx.c
> > new file mode 100644
> > index 000000000000..6edb5e4929d9
> > --- /dev/null
> > +++ b/drivers/media/platform/renesas/vsp1/vsp1_vspx.c
> > @@ -0,0 +1,664 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * vsp1_vspx.c  --  R-Car Gen 4 VSPX
> > + *
> > + * Copyright (C) 2025 Ideas On Board Oy
> > + * Copyright (C) 2025 Renesas Electronics Corporation
> > + */
> > +
> > +#include "vsp1_vspx.h"
> > +
> > +#include <linux/cleanup.h>
> > +#include <linux/container_of.h>
> > +#include <linux/delay.h>
> > +#include <linux/device.h>
> > +#include <linux/dma-mapping.h>
> > +#include <linux/list.h>
> > +#include <linux/slab.h>
> > +#include <linux/spinlock.h>
> > +
> > +#include <media/media-entity.h>
> > +#include <media/v4l2-subdev.h>
> > +#include <media/vsp1.h>
> > +
> > +#include "vsp1_dl.h"
> > +#include "vsp1_iif.h"
> > +#include "vsp1_pipe.h"
> > +#include "vsp1_rwpf.h"
> > +
> > +/*
> > + * struct vsp1_vspx_pipeline - VSPX pipeline
> > + * @pipe: the VSP1 pipeline
> > + * @partition: the pre-calculated partition used by the pipeline
> > + * @vspx_lock: protect access to the VSPX configuration and the jobs queue
>
> As there's a single lock, you could name it just "lock".
>

it can indeed

> > + * @enabled: the enable flag
> > + * @jobs: jobs queue
> > + * @vspx_frame_end: frame end callback
> > + * @frame_end_data: data for the frame end callback
> > + */
> > +struct vsp1_vspx_pipeline {
> > +	struct vsp1_pipeline pipe;
> > +	struct vsp1_partition partition;
> > +
> > +	/* Protects the pipeline configuration */
> > +	spinlock_t vspx_lock;
> > +	bool enabled;
> > +
> > +	struct list_head jobs;
> > +
> > +	void (*vspx_frame_end)(void *frame_end_data);
> > +	void *frame_end_data;
> > +};
> > +
> > +static inline struct vsp1_vspx_pipeline *
> > +to_vsp1_vspx_pipeline(struct vsp1_pipeline *pipe)
> > +{
> > +	return container_of(pipe, struct vsp1_vspx_pipeline, pipe);
> > +}
> > +
> > +/*
> > + * struct vsp1_vspx_job - VSPX transfer job
> > + * @dl: display list populated by vsp1_isp_job_prepare()
> > + * @job_queue: list handle
> > + */
> > +struct vsp1_vspx_job {
> > +	struct vsp1_dl_list *dl;
> > +	struct list_head job_queue;
> > +};
> > +
> > +/*
> > + * struct vsp1_vspx - VSPX device
> > + * @vsp1: the VSP1 device
> > + * @pipe: the VSPX pipeline
> > + */
> > +struct vsp1_vspx {
> > +	struct vsp1_device *vsp1;
> > +	struct vsp1_vspx_pipeline pipe;
> > +};
> > +
> > +static const struct v4l2_pix_format_mplane vspx_default_fmt = {
> > +	.width = 1920,
> > +	.height = 1080,
> > +	.pixelformat = V4L2_PIX_FMT_SRGGB8,
> > +	.field = V4L2_FIELD_NONE,
> > +	.num_planes = 1,
> > +	.plane_fmt = {
> > +		[0] = {
> > +			.sizeimage = 1920 * 1080,
> > +			.bytesperline = 1920,
> > +		},
> > +	},
> > +};
> > +
> > +/*
> > + * Apply the given width, height and fourcc to the subdevice inside the
> > + * VSP1 entity.
>
> s/entity/RWPF/ as you only deal with RPFs and WPFs here. Or
>
> /* Apply the given width, height and fourcc to the RWPF's subdevice */
>

ack

> > + */
> > +static int vsp1_vspx_rwpf_set_subdev_fmt(struct vsp1_device *vsp1,
> > +					 struct vsp1_rwpf *rwpf,
> > +					 u32 isp_fourcc,
> > +					 unsigned int width,
> > +					 unsigned int height)
> > +{
> > +	struct vsp1_entity *ent = &rwpf->entity;
> > +	const struct vsp1_format_info *fmtinfo;
> > +	struct v4l2_subdev_format format = {};
> > +	u32 vspx_fourcc;
> > +
> > +	switch (isp_fourcc) {
> > +	case V4L2_PIX_FMT_GREY:
> > +		/* 8 bit RAW Bayer image. */
> > +		vspx_fourcc = V4L2_PIX_FMT_RGB332;
> > +		break;
> > +	case V4L2_PIX_FMT_Y10:
> > +	case V4L2_PIX_FMT_Y12:
> > +	case V4L2_PIX_FMT_Y16:
> > +		/* 10, 12 and 16 bit RAW Bayer image. */
> > +		vspx_fourcc = V4L2_PIX_FMT_RGB565;
> > +		break;
> > +	case V4L2_META_FMT_GENERIC_8:
> > +		/* ConfigDMA parameters buffer. */
> > +		vspx_fourcc = V4L2_PIX_FMT_XBGR32;
> > +		break;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +
> > +	fmtinfo = vsp1_get_format_info(vsp1, vspx_fourcc);
> > +	rwpf->fmtinfo = fmtinfo;
>
> 	rwpf->fmtinfo = vsp1_get_format_info(vsp1, vspx_fourcc);
>
> and drop the local variable.
>

ok

> > +
> > +	format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> > +	format.pad = RWPF_PAD_SINK;
> > +	format.format.width = width;
> > +	format.format.height = height;
> > +	format.format.field = V4L2_FIELD_NONE;
> > +	format.format.code = fmtinfo->mbus;
> > +
> > +	return v4l2_subdev_call(&ent->subdev, pad, set_fmt, NULL, &format);
> > +}
> > +
> > +/* Configure RPF0 for ConfigDMA and RAW image transfer. */
>
> s/and/or/
>
> But you also configure the WPF, so I'd write
>
> /* Configure the RPF -> IIF -> WPF pipeline for ConfigDMA or RAW image transfer. */
>
> and rename the function to vsp1_vspx_pipeline_configure().

ok

>
> > +static int vsp1_vspx_rpf0_configure(struct vsp1_device *vsp1,
> > +				    dma_addr_t addr, u32 isp_fourcc,
> > +				    unsigned int width, unsigned int height,
> > +				    unsigned int stride,
> > +				    unsigned int iif_sink_pad,
> > +				    struct vsp1_dl_list *dl,
> > +				    struct vsp1_dl_body *dlb)
> > +{
> > +	struct vsp1_vspx_pipeline *vspx_pipe = &vsp1->vspx->pipe;
> > +	struct vsp1_pipeline *pipe = &vspx_pipe->pipe;
> > +	struct vsp1_rwpf *rpf0 = pipe->inputs[0];
> > +	int ret;
> > +
> > +	ret = vsp1_vspx_rwpf_set_subdev_fmt(vsp1, rpf0, isp_fourcc, width,
> > +					    height);
> > +	if (ret)
> > +		return ret;
> > +
> > +	ret = vsp1_vspx_rwpf_set_subdev_fmt(vsp1, pipe->output, isp_fourcc,
> > +					    width, height);
> > +	if (ret)
> > +		return ret;
> > +
> > +	vsp1_pipeline_calculate_partition(pipe, &pipe->part_table[0],
> > +					  width, 0);
> > +
>
> You should also set rwpf->format.num_planes to 1 here.
>

ack

> > +	rpf0->format.plane_fmt[0].bytesperline = stride;
> > +
> > +	/*
> > +	 * Connect RPF0 to the IIF sink pad corresponding to the config or image
> > +	 * path.
> > +	 */
> > +	rpf0->entity.sink_pad = iif_sink_pad;
> > +
> > +	pipe->part_table[0].rpf[0].width = width;
> > +	pipe->part_table[0].rpf[0].height = height;
>
> Isn't this handled by vsp1_pipeline_calculate_partition() ?
>

I don't see it happening in the vsp1_pipeline_calculate_partition()
call chain...

vsp1_pipeline_calculate_partition() calls
vsp1_pipeline_propagate_partition() which calls the 'partition' op on
entities in the pipeline.

The RPF implementation of the 'partition' op however initializes the
crop retangles on the entity, but not the part_table[].

To be honest that partition part is still a bit obscure to me, so I
might be missing something indeed

> > +
> > +	rpf0->mem.addr[0] = addr;
> > +	rpf0->mem.addr[1] = 0;
> > +	rpf0->mem.addr[2] = 0;
> > +
> > +	vsp1_entity_route_setup(&rpf0->entity, pipe, dlb);
> > +	vsp1_entity_configure_stream(&rpf0->entity, rpf0->entity.state, pipe,
> > +				     dl, dlb);
> > +	vsp1_entity_configure_partition(&rpf0->entity, pipe,
> > +					&pipe->part_table[0], dl, dlb);
> > +
> > +	return 0;
> > +}
> > +
> > +/* -----------------------------------------------------------------------------
> > + * Interrupt handling
> > + */
> > +
> > +static void vsp1_vspx_pipeline_frame_end(struct vsp1_pipeline *pipe,
> > +					 unsigned int completion)
> > +{
> > +	struct vsp1_vspx_pipeline *vspx_pipe = to_vsp1_vspx_pipeline(pipe);
> > +
> > +	if (vspx_pipe->vspx_frame_end)
> > +		vspx_pipe->vspx_frame_end(vspx_pipe->frame_end_data);
> > +}
> > +
> > +/* -----------------------------------------------------------------------------
> > + * ISP Driver API (include/media/vsp1.h)
> > + */
> > +
> > +/**
> > + * vsp1_isp_init() - Initialize the VSPX
> > + *
> > + * @dev: The VSP1 struct device
> > + *
> > + * Return: %0 on success or a negative error code on failure
> > + */
> > +int vsp1_isp_init(struct device *dev)
> > +{
> > +	struct vsp1_device *vsp1 = dev_get_drvdata(dev);
> > +
> > +	if (!vsp1)
> > +		return -EPROBE_DEFER;
> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(vsp1_isp_init);
> > +
> > +/**
> > + * vsp1_isp_get_bus_master - Get VSPX bus master
> > + *
> > + * The VSPX accesses memory through an FCPX instance. When allocating memory
> > + * buffers that will have to be accessed by the VSPX the 'struct device' of
> > + * the FCPX should be used. Use this function to get a reference to it.
>
> The function description should go after parameters. Same elsewhere
> where applicable.
>

Ah indeed, I got all of these wrong :/

> > + *
> > + * @dev: The VSP1 struct device
> > + *
> > + * Return: a pointer to the bus master's device
> > + */
> > +struct device *vsp1_isp_get_bus_master(struct device *dev)
> > +{
> > +	struct vsp1_device *vsp1 = dev_get_drvdata(dev);
> > +
> > +	if (!vsp1)
> > +		return ERR_PTR(-ENODEV);
> > +
> > +	return vsp1->bus_master;
> > +}
> > +EXPORT_SYMBOL_GPL(vsp1_isp_get_bus_master);
> > +
> > +/**
> > + * vsp1_isp_alloc_buffer - Allocate a buffer in the VSPX address space
> > + *
> > + * Allocate a buffer that will be later accessed by the VSPX. Buffers allocated
> > + * using vsp1_isp_alloc_buffer() shall be released with a call to
> > + * vsp1_isp_free_buffer().
>
> I would explain somewhere that this is meant for ISP configuration
> parameters.
>

I'll add

      * This function is used by the ISP driver to allocate memory for
      * the ConfigDMA parameters buffer.

> > + *
> > + * @dev: The VSP1 struct device
> > + * @size: The size of the buffer to be allocated by the VSPX
> > + * @buffer_desc: The allocated buffer descriptor, will be filled with the
> > + *		 buffer CPU-mapped address, the bus address and the allocated
> > + *		 buffer size
> > + *
> > + * Return: %0 on success or a negative error code on failure
> > + */
> > +int vsp1_isp_alloc_buffer(struct device *dev, size_t size,
> > +			  struct vsp1_isp_buffer_desc *buffer_desc)
> > +{
> > +	struct device *bus_master = vsp1_isp_get_bus_master(dev);
> > +
> > +	if (IS_ERR_OR_NULL(bus_master))
> > +		return -ENODEV;
> > +
> > +	buffer_desc->cpu_addr = dma_alloc_coherent(bus_master, size,
> > +						   &buffer_desc->dma_addr,
> > +						   GFP_KERNEL);
> > +	if (!buffer_desc->cpu_addr)
> > +		return -ENOMEM;
> > +
> > +	buffer_desc->size = size;
> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(vsp1_isp_alloc_buffer);
> > +
> > +/**
> > + * vsp1_isp_free_buffer - Release a buffer allocated by vsp1_isp_alloc_buffer()
> > + *
> > + * Release memory in the VSPX address space allocated by
> > + * vsp1_isp_alloc_buffer().
> > + *
> > + * @dev: The VSP1 struct device
> > + * @buffer_desc: The descriptor of the buffer to release as returned by
> > + *		 vsp1_isp_alloc_buffer()
> > + */
> > +void vsp1_isp_free_buffer(struct device *dev,
> > +			  struct vsp1_isp_buffer_desc *buffer_desc)
> > +{
> > +	struct device *bus_master = vsp1_isp_get_bus_master(dev);
> > +
> > +	if (IS_ERR_OR_NULL(bus_master))
> > +		return;
> > +
> > +	dma_free_coherent(bus_master, buffer_desc->size, buffer_desc->cpu_addr,
> > +			  buffer_desc->dma_addr);
> > +}
> > +
> > +/**
> > + * vsp1_isp_start_streaming - Start processing VSPX jobs
> > + *
> > + * Start the VSPX and prepare for accepting buffer transfer job requests.
> > + *
> > + * @dev: The VSP1 struct device
> > + * @frame_end: The frame end callback description
> > + *
> > + * Return: %0 on success or a negative error code on failure
> > + */
> > +int vsp1_isp_start_streaming(struct device *dev,
> > +			     struct vsp1_vspx_frame_end *frame_end)
> > +{
> > +	struct vsp1_device *vsp1 = dev_get_drvdata(dev);
> > +	struct vsp1_vspx_pipeline *vspx_pipe = &vsp1->vspx->pipe;
> > +	struct vsp1_pipeline *pipe = &vspx_pipe->pipe;
> > +	unsigned long flags;
> > +	u32 value;
> > +	int ret;
> > +
> > +	spin_lock_irqsave(&vspx_pipe->vspx_lock, flags);
>
> Can this function ever be called with interrupts disabled ? If no, you
> can use spin_lock_irq().
>

I think the question here should rather be: do we need to disable
local interrupts at all when calling this function ? As the VSPX
workflow is now driven by ISP and there are no contentions between any
of the driver functions and the VSPX interrupt handler I guess I can
use spin_lock() and spin_unlock() everywhere and replace the
irqsave/irqrestore versions ?

> > +
> > +	if (vspx_pipe->enabled) {
> > +		spin_unlock_irqrestore(&vspx_pipe->vspx_lock, flags);
> > +		return 0;
>
> Shouldn't this be an error ?
>

Should it ? I don't think it's an error conceptually, as the ISP
driver is currently designed however a single call to this function
should happen, so I can flag this as an error.

However, as S_STREAM(1) is called on every video node, I would not
rule out a design where each video dev tries to start the VSPX. The
ones that arrive late will just hit a nop.

> > +	}
> > +
> > +	if (!frame_end) {
>
> You can move this check before taking the lock.
>

True that

> > +		spin_unlock_irqrestore(&vspx_pipe->vspx_lock, flags);
> > +		return -EINVAL;
> > +	}
> > +
> > +	vspx_pipe->vspx_frame_end = frame_end->vspx_frame_end;
> > +	vspx_pipe->frame_end_data = frame_end->frame_end_data;
>
> Move this just before setting ->enabled to true, so you won't override
> the values if the function returns an error in the checks below.
>
> > +
> > +	/* Make sure VSPX is not active. */
>
> This should never happen unless there's a serious bug in the driver,
> right ? I would make that explicit in the comment:
>
> 	/*
> 	 * Make sure VSPX is not active. This should never happen in normal
> 	 * usage.
> 	 */

ok

>
> > +	value = vsp1_read(vsp1, VI6_CMD(0));
> > +	if (value & VI6_CMD_STRCMD) {
> > +		dev_err(vsp1->dev,
> > +			"%s: Starting of WPF0 already reserved\n", __func__);
> > +		spin_unlock_irqrestore(&vspx_pipe->vspx_lock, flags);
> > +		return -EBUSY;
> > +	}
> > +
> > +	value = vsp1_read(vsp1, VI6_STATUS);
> > +	if (value & VI6_STATUS_SYS_ACT(0)) {
> > +		dev_err(vsp1->dev,
> > +			"%s: WPF0 has not entered idle state\n", __func__);
> > +		spin_unlock_irqrestore(&vspx_pipe->vspx_lock, flags);
> > +		return -EBUSY;
> > +	}
> > +
> > +	vspx_pipe->enabled = true;
> > +
> > +	spin_unlock_irqrestore(&vspx_pipe->vspx_lock, flags);
>
> and close the guarded scope here
>
> 	}
>

I found it to be less readable because of the increased scoping.

> > +
> > +	/* Enable the VSP1 and prepare for streaming. */
> > +	vsp1_pipeline_dump(pipe, "VSPX job");
> > +
> > +	ret = vsp1_device_get(vsp1);
>
> This should be done before you read the registers.
>

Ah right. I guess this function should be then re-designed.


> > +	if (ret < 0) {
> > +		guard(spinlock_irqsave)(&vspx_pipe->vspx_lock);
> > +		vspx_pipe->enabled = false;
> > +		return ret;
> > +	}
> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(vsp1_isp_start_streaming);
> > +
> > +/**
> > + * vsp1_isp_stop_streaming - Stop the VSPX
> > + *
> > + * @dev: The VSP1 struct device
> > + */
> > +void vsp1_isp_stop_streaming(struct device *dev)
> > +{
> > +	struct vsp1_device *vsp1 = dev_get_drvdata(dev);
> > +	struct vsp1_vspx_pipeline *vspx_pipe = &vsp1->vspx->pipe;
> > +	struct vsp1_pipeline *pipe = &vspx_pipe->pipe;
> > +	unsigned long flags;
> > +
> > +	spin_lock_irqsave(&vspx_pipe->vspx_lock, flags);
> > +
> > +	if (!vspx_pipe->enabled) {
> > +		spin_unlock_irqrestore(&vspx_pipe->vspx_lock, flags);
> > +		return;
> > +	}
>
> 	scoped_guard(spinlock_irqsave)(&vspx_pipe->vspx_lock, flags) {
> 		if (!vspx_pipe->enabled)
> 			return;
>
> 		...
>
> (or spinlock_irq, see comment in vsp1_isp_start_streaming()).
>

Am I mistaken that the _irq version you suggest still disables
local interrupts, which is something I shouldn't need

-------------------------------------------------------------------------------

[Index of Archives]     [Linux Samsung SOC]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]

  Powered by Linux