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
>
next prev 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