Devicetree
 help / color / mirror / Atom feed
From: Frank Li <Frank.li@oss.nxp.com>
To: Antoine Bouyer <antoine.bouyer@nxp.com>
Cc: julien.vuillaumier@nxp.com, alexi.birlinger@nxp.com,
	daniel.baluta@nxp.com, peng.fan@nxp.com, frank.li@nxp.com,
	jacopo.mondi@ideasonboard.com, laurent.pinchart@ideasonboard.com,
	mchehab@kernel.org, robh@kernel.org, krzk+dt@kernel.org,
	conor+dt@kernel.org, michael.riesch@collabora.com,
	anthony.mcgivern@arm.com, linux-media@vger.kernel.org,
	linux-kernel@vger.kernel.org, devicetree@vger.kernel.org,
	imx@lists.linux.dev, ai.luthra@ideasonboard.com,
	paul.elder@ideasonboard.com, geert@linux-m68k.org,
	sakari.ailus@linux.intel.com, hverkuil+cisco@kernel.org
Subject: Re: [PATCH v3 6/8] media: platform: Add NXP Neoisp Image Signal Processor
Date: Fri, 12 Jun 2026 14:25:04 -0500	[thread overview]
Message-ID: <aixdEPsghlc2S7Zl@SMW015318> (raw)
In-Reply-To: <20260612132039.2089051-7-antoine.bouyer@nxp.com>

On Fri, Jun 12, 2026 at 03:20:37PM +0200, Antoine Bouyer wrote:
> First NXP neoisp driver version with the following contents:
>
> This driver was initially inspired from raspberrypi pisp_be driver. It
> reuses same approach for ISP job scheduling.
>
> The Neoisp driver supports:
> * 8, 10, 12, 14 and 16-bits RAW Bayer images input.
> * Monochrome sensors input.
> * RGB/YUV, IR and Greyscale output formats.
>
> The neoisp features are:
> * Provides single context to limit amount of v4l2 devices.
> * Supports M2M operations.
> * Support SDR and HDR modes.
> * Supports generic v4l2-isp framework for extensible Parameters and
> Statistics buffers.
> * Provides a `core_media_register` API to register neoisp's media entities
> into another media graph.
> * A module parameter to run in standalone mode with its own media device.
>
> Co-developed-by: Alexi Birlinger <alexi.birlinger@nxp.com>
> Signed-off-by: Alexi Birlinger <alexi.birlinger@nxp.com>
> Signed-off-by: Antoine Bouyer <antoine.bouyer@nxp.com>
> ---
...
>
> +MEDIA DRIVERS FOR NXP NEOISP
> +M:	Antoine Bouyer <antoine.bouyer@nxp.com>
> +S:	Maintained
> +F:	Documentation/admin-guide/media/nxp-neoisp*
> +F:	Documentation/devicetree/bindings/media/nxp,imx95-neoisp.yaml
> +F:  Documentation/userspace-api/media/v4l/metafmt-nxp-neoisp.rst

Use tab.

> +F:	drivers/media/platform/nxp/neoisp/*
> +F:	include/uapi/linux/media/nxp/nxp_neoisp.h
> +
...
> +#define NEOISP_MIN_H			64U
> +#define NEOISP_MAX_W			4096U
> +#define NEOISP_MAX_H			4096U
> +#define NEOISP_MAX_BPP			4U
> +#define NEOISP_ALIGN_W			3
> +#define NEOISP_ALIGN_H			3
> +#define NEOISP_DEF_W			640U
> +#define NEOISP_DEF_H			480U

look like needn't "U" for these const.

> +
> +#define NEOISP_SUSPEND_TIMEOUT_MS	500
> +
...
> +
> +/*
> + * Extract offset and size in bytes from memory region map
> + */
> +static inline void get_offsize(enum isp_block_map_e map, u32 *offset, u32 *size)

Needn't inline. check others, compiler will auto do it and also find unused
function if no inline.

> +{
> +	*offset = ISP_GET_OFF(map);
> +	*size = ISP_GET_SZ(map);
> +}
> +
...
> +
> +static const struct v4l2_frmsize_stepwise neoisp_frmsize_stepwise = {
> +	.min_width = NEOISP_MIN_W,
> +	.min_height = NEOISP_MIN_H,
> +	.max_width = NEOISP_MAX_W,
> +	.max_height = NEOISP_MAX_H,
> +	.step_width = 1UL << NEOISP_ALIGN_W,
> +	.step_height = 1UL << NEOISP_ALIGN_H,
> +};

why put this into neoisp_fmt.h? if two c file include it, these data will
be duplicated

...
> +		bit = NEO_PIPE_CONF_SOFT_RESET_HARD_RESET;
> +
> +	neoisp_wr(neoispd, NEO_PIPE_CONF_SOFT_RESET, bit);
> +
> +	/* Wait for auto-clear */
> +	do {
> +		usleep_range(1, 2);
> +		val = neoisp_rd(neoispd, NEO_PIPE_CONF_SOFT_RESET);
> +		count--;
> +	} while ((val & bit) && count);

Use read_poll_timeout()

> +
> +	if (val & bit)
> +		dev_warn(neoispd->dev, "%s reset incomplete\n",
> +			 is_hw ? "hw" : "sw");
> +}
...
> +		/*
> +		 * Take a copy of streaming_map: nodes activated after this
> +		 * point are ignored when preparing this job
> +		 */
> +		streaming_map = neoispd->streaming_map;
> +	}
> +
> +	job = kzalloc(sizeof(*job), GFP_KERNEL);

use kzalloc_obj()

> +	if (!job)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < NEOISP_NODES_COUNT; i++) {
...
> +static irqreturn_t neoisp_irq_handler(int irq, void *dev_id)
> +{
> +	struct neoisp_dev_s *neoispd = (struct neoisp_dev_s *)dev_id;
> +	struct neoisp_buffer_s **buf = neoispd->queued_job.buf;
> +	u64 ts = ktime_get_ns();
> +	u32 irq_status = 0;
> +	u32 irq_clear = 0;
> +	bool done = false;
> +	int i;
> +
> +	irq_status = neoisp_rd(neoispd, NEO_PIPE_CONF_INT_STAT0);
> +
> +	if (irq_status & NEO_PIPE_CONF_INT_STAT0_S_FS1) {
> +		dev_dbg(neoispd->dev, "Neo IRQ FS1 !\n");
> +		irq_clear |= NEO_PIPE_CONF_INT_STAT0_S_FS1;
> +		done = false;
> +	}
> +
> +	if (irq_status & NEO_PIPE_CONF_INT_STAT0_S_FS2) {
> +		dev_dbg(neoispd->dev, "Neo IRQ FS2 !\n");
> +		irq_clear |= NEO_PIPE_CONF_INT_STAT0_S_FS2;
> +		done = false;
> +	}
> +
> +	if (irq_status & NEO_PIPE_CONF_INT_STAT0_S_FD1) {
> +		dev_dbg(neoispd->dev, "Neo IRQ FD1 !\n");
> +		irq_clear |= NEO_PIPE_CONF_INT_STAT0_S_FD1;
> +		done = false;
> +	}
> +
> +	if (irq_status & NEO_PIPE_CONF_INT_STAT0_S_STATD) {
> +		dev_dbg(neoispd->dev, "Neo IRQ STATD !\n");
> +		irq_clear |= NEO_PIPE_CONF_INT_STAT0_S_STATD;
> +		done = false;
> +	}
> +
> +	if (irq_status & NEO_PIPE_CONF_INT_STAT0_S_DRCD) {
> +		dev_dbg(neoispd->dev, "Neo IRQ DRCD !\n");
> +		neoisp_ctx_get_stats(neoispd, buf[NEOISP_STATS_NODE]);
> +		irq_clear |= NEO_PIPE_CONF_INT_STAT0_S_DRCD;
> +		done = false;
> +	}
> +
> +	if (irq_status & NEO_PIPE_CONF_INT_STAT0_S_BUS_ERR) {
> +		irq_clear |= NEO_PIPE_CONF_INT_STAT0_S_BUS_ERR;
> +		dev_err(neoispd->dev, "Neo IRQ BUS ERR!\n");
> +		done = true;
> +	}
> +
> +	if (irq_status & NEO_PIPE_CONF_INT_STAT0_S_TRIG_ERR) {
> +		dev_err(neoispd->dev, "Neo IRQ TRIG ERR !\n");
> +		irq_clear |= NEO_PIPE_CONF_INT_STAT0_S_TRIG_ERR;
> +		done = true;
> +	}
> +
> +	if (irq_status & NEO_PIPE_CONF_INT_STAT0_S_CSI_TERR) {
> +		dev_err(neoispd->dev, "Neo IRQ TRIG CSI Trigger ERR !\n");
> +		irq_clear |= NEO_PIPE_CONF_INT_STAT0_S_CSI_TERR;
> +		done = true;
> +	}
> +
> +	if (irq_status & NEO_PIPE_CONF_INT_STAT0_S_FD2) {
> +		dev_dbg(neoispd->dev, "Neo IRQ FD2 !\n");
> +		irq_clear |= NEO_PIPE_CONF_INT_STAT0_S_FD2;
> +		done = true;
> +	}
> +
> +	if (irq_status & NEO_PIPE_CONF_INT_STAT0_BUSY)
> +		dev_err(neoispd->dev, "Neo is busy !\n");
> +
> +	neoisp_wr(neoispd, NEO_PIPE_CONF_INT_STAT0, irq_clear);

Did you share irq with other device?

you can use directly
	neoisp_wr(neoispd, NEO_PIPE_CONF_INT_STAT0, irq_status);

and if there are unexpected irq happen, which is not in your check list,
it will cause irq storm.

	so need more if block

	just

	if (irq_status & (NEO_PIPE_CONF_INT_STAT0_S_BUS_ER | irq_status & NEO_PIPE_CONF_INT_STAT0_S_BUS_ER ...)
		done = true;

default done is falue.

> +
> +	if (done) {
> +		for (i = 0; i < NEOISP_NODES_COUNT; i++) {
> +			if (!buf[i])
> +				continue;
> +
> +			buf[i]->vb.sequence = neoispd->frame_sequence;
> +			buf[i]->vb.vb2_buf.timestamp = ts;
> +			vb2_buffer_done(&buf[i]->vb.vb2_buf, VB2_BUF_STATE_DONE);
> +
> +			/* To prevent double buffer handling in case of spurius interrupt */
> +			buf[i] = NULL;
> +		}
> +		/* Update frame_sequence */
> +		neoispd->frame_sequence++;
> +		/* Check if there's more to do before going to sleep */
> +		neoisp_schedule(neoispd, true);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
...
> +static int neoisp_init_node(struct neoisp_dev_s *neoispd, u32 id)
> +{
> +	bool output = node_desc_is_output(&node_desc[id]);
> +	struct neoisp_node_s *node = &neoispd->node[id];
> +	struct media_entity *entity = &node->vfd.entity;
> +	struct media_pad *mpad;
> +	struct video_device *vdev = &node->vfd;
> +	struct vb2_queue *q = &node->queue;
> +	int ret;

please try keep revise christmas tree order for nice look.

> +
> +	node->id = id;
> +	node->neoisp = neoispd;
> +	node->buf_type = node_desc[id].buf_type;
> +
...
> +{
> +	struct v4l2_device *v4l2_dev = &neoispd->v4l2_dev;
> +	u32 num_registered = 0;
> +	int ret;
> +
> +	mutex_init(&neoispd->queue_lock);

suppose this function call from probe, so you can use devm_mutex_init()

> +
> +	/* Register v4l2_device and media_device */
> +	v4l2_dev->mdev = mdev;
> +	strscpy(v4l2_dev->name, NEOISP_NAME, sizeof(v4l2_dev->name));
...
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to request irq: %d\n", ret);
> +		goto err_irq;
> +	}
> +
> +	pm_runtime_set_autosuspend_delay(dev, NEOISP_SUSPEND_TIMEOUT_MS);
> +	pm_runtime_use_autosuspend(dev);
> +	pm_runtime_enable(dev);

devm_pm_runtime_enable();

> +	ret = pm_runtime_resume_and_get(dev);

you can use below code now, needn't goto branch

	PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
	if (PM_RUNTIME_ACQUIRE_ERR(&pm))
		return -ENXIO;


> +	if (ret < 0) {
> +		dev_err(dev, "Unable to resume the device: %d\n", ret);
> +		goto err_pm_runtime_disable;
> +	}
> +
> +	ret = neoisp_init_devices(neoispd);
> +	if (ret)
> +		goto err_pm_runtime_suspend;
> +
> +	spin_lock_init(&neoispd->hw_lock);
> +	neoisp_init_hw(neoispd);
> +	neoisp_set_default_context(neoispd);
> +
> +	pm_runtime_mark_last_busy(dev);

Needn't call this now

> +	pm_runtime_put_autosuspend(dev);
> +
> +	return 0;
> +
> +err_pm_runtime_suspend:
> +	pm_runtime_put(dev);
> +err_pm_runtime_disable:
> +	pm_runtime_dont_use_autosuspend(dev);
> +	pm_runtime_disable(dev);
> +err_irq:
> +	dev_err(dev, "probe: error %d\n", ret);
> +	return ret;
> +}
> +
> +static void neoisp_remove(struct platform_device *pdev)
> +{
> +	struct neoisp_dev_s *neoispd = platform_get_drvdata(pdev);
> +
> +	neoisp_destroy_devices(neoispd);
> +
> +	if (standalone_mdev)
> +		media_device_cleanup(&neoispd->mdev);
> +
> +	pm_runtime_dont_use_autosuspend(neoispd->dev);
> +	pm_runtime_disable(neoispd->dev);
> +}
> +
> +static int __maybe_unused neoisp_runtime_suspend(struct device *dev)

Needn't __maybe now

> +{
> +	struct neoisp_dev_s *neoispd = dev_get_drvdata(dev);
> +
> +	clk_bulk_disable_unprepare(neoispd->num_clks, neoispd->clks);
> +
> +	return 0;
> +}
> +
...
> +
> +static const struct dev_pm_ops neoisp_pm = {
> +	SET_SYSTEM_SLEEP_PM_OPS(neoisp_pm_suspend, neoisp_pm_resume)
> +	SET_RUNTIME_PM_OPS(neoisp_runtime_suspend, neoisp_runtime_resume, NULL)

Use new macro
RUNTIME_PM_OPS

> +};
> +
...
> +
> +static struct platform_driver neoisp_driver = {
> +	.probe  = neoisp_probe,
> +	.remove = neoisp_remove,
> +	.driver = {
> +		.name = NEOISP_NAME,
> +		.pm = &neoisp_pm,

pm_ptr(&neoisp_pm)

> +		.of_match_table = neoisp_dt_ids,
> +	},
> +};
> +
> +module_platform_driver(neoisp_driver);
> +
> +MODULE_DESCRIPTION("NXP NEOISP Hardware");
> +MODULE_AUTHOR("Antoine Bouyer <antoine.bouyer@nxp.com>");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/media/platform/nxp/neoisp/neoisp_nodes.h b/drivers/media/platform/nxp/neoisp/neoisp_nodes.h
> new file mode 100644
> index 000000000000..54986fe13b33
> --- /dev/null
> +++ b/drivers/media/platform/nxp/neoisp/neoisp_nodes.h
> @@ -0,0 +1,54 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * NEOISP nodes description
> + *
> + * Copyright 2023-2026 NXP
> + */
> +
> +#ifndef __NXP_NEOISP_NODES_H
> +#define __NXP_NEOISP_NODES_H
> +
> +#include <linux/videodev2.h>
> +
> +#include "neoisp.h"
> +
> +static const struct neoisp_node_desc_s node_desc[NEOISP_NODES_COUNT] = {

These const data should not appear in header files.

Frank
>

  parent reply	other threads:[~2026-06-12 19:25 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-12 13:20 [PATCH v3 0/8] media: Add iMX95 neoisp driver Antoine Bouyer
2026-06-12 13:20 ` [PATCH v3 1/8] dt-bindings: media: Add nxp neoisp support Antoine Bouyer
2026-06-12 15:39   ` Frank Li
2026-06-12 13:20 ` [PATCH v3 2/8] media: v4l2-ctrls: Add user control base for NXP neoisp controls Antoine Bouyer
2026-06-12 16:52   ` Frank Li
2026-06-12 13:20 ` [PATCH v3 3/8] media: Add meta formats supported by NXP neoisp driver Antoine Bouyer
2026-06-12 16:55   ` Frank Li
2026-06-12 13:20 ` [PATCH v3 4/8] media: uapi: Add NXP NEOISP user interface header file Antoine Bouyer
2026-06-12 13:34   ` sashiko-bot
2026-06-12 18:31   ` Frank Li
2026-06-12 13:20 ` [PATCH v3 5/8] media: Documentation: Add NXP neoisp driver documentation Antoine Bouyer
2026-06-12 13:20 ` [PATCH v3 6/8] media: platform: Add NXP Neoisp Image Signal Processor Antoine Bouyer
2026-06-12 13:41   ` sashiko-bot
2026-06-12 19:25   ` Frank Li [this message]
2026-06-12 13:20 ` [PATCH v3 7/8] media: platform: neoisp: Add debugfs support Antoine Bouyer
2026-06-12 13:34   ` sashiko-bot
2026-06-12 16:49   ` Frank Li
2026-06-12 13:20 ` [PATCH v3 8/8] arm64: dts: freescale: imx95: Add NXP neoisp device tree node Antoine Bouyer

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=aixdEPsghlc2S7Zl@SMW015318 \
    --to=frank.li@oss.nxp.com \
    --cc=ai.luthra@ideasonboard.com \
    --cc=alexi.birlinger@nxp.com \
    --cc=anthony.mcgivern@arm.com \
    --cc=antoine.bouyer@nxp.com \
    --cc=conor+dt@kernel.org \
    --cc=daniel.baluta@nxp.com \
    --cc=devicetree@vger.kernel.org \
    --cc=frank.li@nxp.com \
    --cc=geert@linux-m68k.org \
    --cc=hverkuil+cisco@kernel.org \
    --cc=imx@lists.linux.dev \
    --cc=jacopo.mondi@ideasonboard.com \
    --cc=julien.vuillaumier@nxp.com \
    --cc=krzk+dt@kernel.org \
    --cc=laurent.pinchart@ideasonboard.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-media@vger.kernel.org \
    --cc=mchehab@kernel.org \
    --cc=michael.riesch@collabora.com \
    --cc=paul.elder@ideasonboard.com \
    --cc=peng.fan@nxp.com \
    --cc=robh@kernel.org \
    --cc=sakari.ailus@linux.intel.com \
    /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