* Re: [PATCH v4 07/10] Input: cap11xx - refactor code for better CAP1114 support.
From: sashiko-bot @ 2026-06-17 20:57 UTC (permalink / raw)
To: Jun Yan; +Cc: conor+dt, devicetree, dmitry.torokhov, linux-input, robh
In-Reply-To: <20260617150318.753148-8-jerrysteve1101@gmail.com>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [High] Bitmask truncation in `cap11xx_led_set` causing silent failure for LEDs >= 8.
- [High] Regmap cache staleness due to missing volatile register definition for the dynamic `sensor_input_reg_base`.
Pre-existing issues:
- [High] Buffer overflow and Fortify Source panic when reading `calib_sensitivities` for devices with more than 8 channels.
--
commit 837f207153012dfc0860b6d46ad5d3ae4f7b3004
Author: Jun Yan <jerrysteve1101@gmail.com>
Extend cap11xx_hw_model structure to support CAP1114
This patch extends the cap11xx_hw_model structure to support CAP1114
with different register offsets and hardware characteristics.
> diff --git a/drivers/input/keyboard/cap11xx.c b/drivers/input/keyboard/cap11xx.c
> index 1db4a9090705..0f19ee036e78 100644
> --- a/drivers/input/keyboard/cap11xx.c
> +++ b/drivers/input/keyboard/cap11xx.c
[ ... ]
> @@ -211,8 +215,8 @@ static int cap11xx_init_keys(struct cap11xx_priv *priv)
> }
>
> if (!of_property_read_u32_array(node, "microchip,input-threshold",
> - priv->thresholds, priv->model->num_channels)) {
> + priv->thresholds, priv->model->num_sensor_thresholds)) {
> - for (i = 0; i < priv->model->num_channels; i++) {
> + for (i = 0; i < priv->model->num_sensor_thresholds; i++) {
> if (priv->thresholds[i] > 127) {
> dev_err(dev, "Invalid input-threshold value %u\n",
> priv->thresholds[i]);
[Severity: High]
This is a pre-existing issue, but looking at cap11xx_init_keys(), could
reading "microchip,calib-sensitivity" overflow the calib_sensitivities array
for devices with more than 8 channels?
In cap11xx_init_keys():
} else if (!of_property_read_u32_array(node, "microchip,calib-sensitivity",
priv->calib_sensitivities,
priv->model->num_channels)) {
The calib_sensitivities array is statically allocated to 8 elements in
struct cap11xx_priv. For models with more than 8 channels (like the CAP1114
which has 14), could this cause a buffer overflow into the keycodes array
and trigger a kernel panic if FORTIFY_SOURCE is enabled?
[ ... ]
> @@ -308,7 +314,7 @@ static irqreturn_t cap11xx_thread_func(int irq_num, void *data)
> if (ret < 0)
> goto out;
>
> - ret = regmap_read(priv->regmap, CAP11XX_REG_SENSOR_INPUT, &status);
> + ret = regmap_read(priv->regmap, priv->model->sensor_input_reg_base, &status);
> if (ret < 0)
> goto out;
[Severity: High]
Will this result in reading stale cached values instead of the actual
hardware state?
The interrupt handler now dynamically reads from sensor_input_reg_base.
However, cap11xx_volatile_reg() still hardcodes CAP11XX_REG_SENSOR_INPUT as
a volatile register.
Since the new dynamic base register (and the subsequent register for devices
with more than 8 channels) is no longer marked volatile, will the regmap
cache return stale data and break input events for these models?
[ ... ]
> @@ -362,7 +368,7 @@ static int cap11xx_led_set(struct led_classdev *cdev,
> * 0 (OFF) and 1 (ON).
> */
> return regmap_update_bits(priv->regmap,
> - CAP11XX_REG_LED_OUTPUT_CONTROL,
> + priv->model->led_output_control_reg_base,
> BIT(led->reg),
> value ? BIT(led->reg) : 0);
> }
[Severity: High]
Will this bitmask be truncated for LEDs with an index of 8 or higher?
The bitmask passed to regmap_update_bits() is BIT(led->reg). Since models
like CAP1114 support more than 8 LEDs, and the register operations are 8-bit,
BIT(led->reg) will exceed 8 bits for higher-index LEDs.
The initialization code in cap11xx_init_leds() caps the LED clearance mask to
8 bits:
GENMASK(min(num_leds, 8) - 1, 0)
Does cap11xx_led_set() also need an update to handle devices with more than
8 LEDs to avoid silently failing?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260617150318.753148-1-jerrysteve1101@gmail.com?part=7
^ permalink raw reply
* Re: [PATCH v5 1/8] media: v4l2-fwnode: Extract common helper __v4l2_async_register_subdev_fwnode()
From: sashiko-bot @ 2026-06-17 20:51 UTC (permalink / raw)
To: Frank.Li; +Cc: imx, robh, devicetree, Frank.Li, conor+dt
In-Reply-To: <20260617-imx8qxp_pcam-v5-1-7fa6c8e7fba7@nxp.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The commit message incorrectly states the function from which the common helper is extracted.
--
commit 1d02bf2bbb834a499240f695cd83dc1f1f8db5fb
Author: Frank Li <Frank.Li@nxp.com>
media: v4l2-fwnode: Extract common helper __v4l2_async_register_subdev_fwnode()
This commit refactors the subdev registration logic to extract a common
helper, __v4l2_async_register_subdev_fwnode(). It allows the notifier
parsing function to be passed as a parameter so that different fwnode
parsing implementations can reuse the same registration code.
> Extract __v4l2_async_register_subdev_fwnode() from
> __media_async_register_subdev_1to1() and make the notifier parsing
> function a parameter.
[Severity: Medium]
Is this function name correct? The commit message states the helper is
extracted from __media_async_register_subdev_1to1(), but the diff shows
the logic is actually being extracted from
__v4l2_async_register_subdev_sensor().
Should the commit message be updated to match the actual function being
modified in the code?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260617-imx8qxp_pcam-v5-0-7fa6c8e7fba7@nxp.com?part=1
^ permalink raw reply
* Re: [PATCH v4 1/2] dt-bindings: arm: xen: Convert to DT schema
From: sashiko-bot @ 2026-06-17 20:51 UTC (permalink / raw)
To: Tejas Mutalikdesai; +Cc: conor+dt, devicetree, robh
In-Reply-To: <20260617185518.43500-1-tejasmutalikdesai@gmail.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] The `$nodename` pattern requires a unit address, which breaks Linux's hardcoded Xen node detection.
--
commit e4969fb4c5852849c9ecbfff9b63719a0d10dc05
Author: Tejas Mutalikdesai <tejasmutalikdesai@gmail.com>
dt-bindings: arm: xen: Convert to DT schema
Convert the Xen ARM device tree binding documentation from the legacy
plain-text format to the DT schema format.
> diff --git a/Documentation/devicetree/bindings/arm/xen.yaml b/Documentation/devicetree/bindings/arm/xen.yaml
> new file mode 100644
> index 0000000000000..a22e950566c23
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/arm/xen.yaml
> @@ -0,0 +1,109 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
[ ... ]
> +description:
> + Xen ARM virtual platforms shall have a top-level "hypervisor" node with
> + the properties defined below.
> +
> +properties:
> + $nodename:
> + pattern: "^hypervisor@[0-9a-f]+$"
[Severity: Critical]
Does enforcing a unit address here break existing kernel expectations?
The Linux kernel seems to strictly expect the node to be named exactly
"hypervisor" without a unit address.
In arch/arm/xen/enlighten.c:fdt_find_hyper_node(), the code uses:
if (depth != 1 || strcmp(uname, "hypervisor") != 0)
And in drivers/firmware/efi/fdtparams.c:dt_params[], the path is hardcoded:
.path = "/hypervisor/uefi",
If a hypervisor or devicetree generator updates its output to comply with this
new schema and adds a unit address (e.g., hypervisor@b0000000), won't the
kernel silently fail to detect Xen and its UEFI runtime services?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260617185518.43500-1-tejasmutalikdesai@gmail.com?part=1
^ permalink raw reply
* Re: [PATCH v7 4/4] arm64: tegra: Reorder reg and reg-names to match bindings
From: sashiko-bot @ 2026-06-17 20:50 UTC (permalink / raw)
To: Thierry Reding; +Cc: conor+dt, linux-pci, devicetree, robh
In-Reply-To: <20260617-tegra264-pcie-v7-4-eae7ae964629@nvidia.com>
> From: Thierry Reding <treding@nvidia.com>
>
> The ECAM region cannot be the first entry in the "reg" property, because
> in that case the unit-address wouldn't match the first entry. The order
> of the nodes can also not be changed to match the ECAM entry because the
> ECAM region is global and outside of any of the control busses.
>
> Signed-off-by: Thierry Reding <treding@nvidia.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260617-tegra264-pcie-v7-0-eae7ae964629@nvidia.com?part=4
^ permalink raw reply
* Re: [PATCH 1/2] dt-bindings: clock: renesas,r9a09g077/87: Add PCLKRTC clock ID
From: Lad, Prabhakar @ 2026-06-17 20:49 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: Michael Turquette, Stephen Boyd, Brian Masney, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Magnus Damm, linux-kernel,
linux-renesas-soc, linux-clk, devicetree, Biju Das,
Fabrizio Castro, Lad Prabhakar
In-Reply-To: <CAMuHMdXQ3S37L7JAJ42Bag0UPa15Q7n-nNCcbV9FXTtTs4aihQ@mail.gmail.com>
Hi Geert,
Thank you for the review.
On Wed, Jun 17, 2026 at 11:19 AM Geert Uytterhoeven
<geert@linux-m68k.org> wrote:
>
> Hi Prabhakar,
>
> Thanks for your patch!
>
> On Mon, 15 Jun 2026 at 16:40, Prabhakar <prabhakar.csengg@gmail.com> wrote:
> > From: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> >
> > Add the peripheral clock ID definition for the Real-Time Clock (PCLKRTC)
> > on the Renesas RZ/T2H (R9A09G077) and RZ/N2H (R9A09G087) SoCs.
> >
> > Note that the LCDC_CLKD clock is utilized as the operating clock source
>
> PCLKRTC
>
Oops my bad!
> > for the RTC IP.
> >
> > Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
>
> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
> i.e. will queue in a branch shared by renesas-clk and renesas-dts
> with the above fixed.
>
Thank you for taking care of this issue.
Cheers,
Prabhakar
> Gr{oetje,eeting}s,
>
> Geert
>
> --
> Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
>
> In personal conversations with technical people, I call myself a hacker. But
> when I'm talking to journalists I just say "programmer" or something like that.
> -- Linus Torvalds
^ permalink raw reply
* Re: [PATCH v7 3/4] PCI: tegra: Add Tegra264 support
From: sashiko-bot @ 2026-06-17 20:48 UTC (permalink / raw)
To: Thierry Reding; +Cc: robh, linux-pci, devicetree, conor+dt
In-Reply-To: <20260617-tegra264-pcie-v7-3-eae7ae964629@nvidia.com>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] Unbinding the driver causes a NULL pointer dereference crash if the PCIe link was down during probe.
- [High] Kernel panic during system suspend if the PCIe link failed to come up.
- [Medium] The runtime PM usage counter is leaked if `pm_runtime_get_sync()` fails.
--
commit 606e8e105f99594322aaccfdb36dcac03cb19d29
Author: Thierry Reding <treding@nvidia.com>
PCI: tegra: Add Tegra264 support
Add a driver for the PCIe controller found on NVIDIA Tegra264 SoCs. The
driver is very small, with its main purpose being to set up the address
translation registers and then creating a standard PCI host using ECAM.
> diff --git a/drivers/pci/controller/pcie-tegra264.c b/drivers/pci/controller/pcie-tegra264.c
> new file mode 100644
> index 0000000000000..e2d295ea4403f
> --- /dev/null
> +++ b/drivers/pci/controller/pcie-tegra264.c
[ ... ]
> +static int tegra264_pcie_probe(struct platform_device *pdev)
> +{
[ ... ]
> + err = devm_pm_runtime_set_active_enabled(dev);
> + if (err < 0) {
> + dev_err_probe(dev, err, "failed to enable runtime PM\n");
> + goto put_bpmp;
> + }
> +
> + err = pm_runtime_get_sync(dev);
> + if (err < 0) {
> + dev_err_probe(dev, err, "failed to power on device\n");
> + goto put_bpmp;
[Severity: Medium]
According to the runtime PM API, pm_runtime_get_sync() increments the usage
counter even if it returns an error. If it fails here, we jump to put_bpmp,
skipping the put_pm label which calls pm_runtime_put_sync(). Does this leak
the usage counter and prevent the device from ever entering runtime suspend?
> + }
> +
> + /* sanity check that programmed ranges match what's in DT */
> + if (!tegra264_pcie_check_ranges(pdev)) {
> + err = -EINVAL;
> + goto put_pm;
> + }
[ ... ]
> + tegra264_pcie_init(pcie);
> +
> + if (!pcie->link_up)
> + return 0;
> +
> + err = pci_host_probe(bridge);
> + if (err < 0) {
> + dev_err_probe(dev, err, "failed to register host\n");
> + goto free_ecam;
> + }
> +
> + return 0;
> +
> +free_ecam:
> + pci_ecam_free(pcie->cfg);
> +put_pm:
> + pm_runtime_put_sync(dev);
> +put_bpmp:
> + tegra_bpmp_put(pcie->bpmp);
> +
> + return err;
> +}
> +
> +static void tegra264_pcie_remove(struct platform_device *pdev)
> +{
> + struct tegra264_pcie *pcie = platform_get_drvdata(pdev);
> +
> + /*
> + * If we undo tegra264_pcie_init() then link goes down and need
> + * controller reset to bring up the link again. Remove intention is
> + * to clean up the root bridge and re-enumerate during bind.
> + */
> + pci_lock_rescan_remove();
> + pci_stop_root_bus(pcie->bridge->bus);
> + pci_remove_root_bus(pcie->bridge->bus);
[Severity: High]
If the link is down during probe, tegra264_pcie_probe() returns 0 without
calling pci_host_probe(), leaving pcie->bridge->bus as NULL. When the
driver is later unbound, tegra264_pcie_remove() unconditionally passes
pcie->bridge->bus to pci_stop_root_bus() and pci_remove_root_bus().
Will this cause a NULL pointer dereference? Is a check needed to ensure the
bus exists before tearing it down?
> + pci_unlock_rescan_remove();
> +
> + pm_runtime_put_sync(&pdev->dev);
> + tegra_bpmp_put(pcie->bpmp);
> + pci_ecam_free(pcie->cfg);
> +}
> +
> +static int tegra264_pcie_suspend(struct device *dev)
> +{
> + struct tegra264_pcie *pcie = dev_get_drvdata(dev);
> + int err;
> +
> + tegra264_pcie_deinit(pcie);
[Severity: High]
If the link fails to come up in tegra264_pcie_init() and the port isn't
hotplug-capable, the driver powers off the controller and sets
pcie->link_up = false.
If the system is later suspended, tegra264_pcie_suspend() unconditionally
calls tegra264_pcie_deinit(), which attempts to read from the xtl register
space:
static void tegra264_pcie_deinit(struct tegra264_pcie *pcie)
{
...
value = readl(pcie->xtl + XTL_RC_MGMT_PERST_CONTROL);
Will this hardware access to a powered-off controller cause a kernel panic?
Should we check pcie->link_up before calling tegra264_pcie_deinit(),
similar to what is done in tegra264_pcie_resume()?
> +
> + if (pcie->wake_gpio && device_may_wakeup(dev)) {
> + err = enable_irq_wake(pcie->wake_irq);
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260617-tegra264-pcie-v7-0-eae7ae964629@nvidia.com?part=3
^ permalink raw reply
* Re: [PATCH v9 3/5] iio: adc: versal-sysmon: add I2C driver
From: sashiko-bot @ 2026-06-17 20:47 UTC (permalink / raw)
To: Salih Erim; +Cc: robh, conor+dt, devicetree
In-Reply-To: <20260617180147.3370346-4-salih.erim@amd.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Non-atomic I2C read operation splits the command write and data read into two distinct I2C transactions, risking state corruption from interleaved bus traffic.
--
commit ae0266701d933c996d4059785f81513fb71dc331
Author: Salih Erim <salih.erim@amd.com>
iio: adc: versal-sysmon: add I2C driver
This commit adds an I2C transport driver for the Versal SysMon block. It
provides an I2C slave interface that allows an external master to read
voltage and temperature measurements through the same register map used by
the MMIO path.
> diff --git a/drivers/iio/adc/versal-sysmon-i2c.c b/drivers/iio/adc/versal-sysmon-i2c.c
> new file mode 100644
> index 0000000000000..e9a7629159ab1
> --- /dev/null
> +++ b/drivers/iio/adc/versal-sysmon-i2c.c
[ ... ]
> +static int sysmon_i2c_reg_read(void *context, unsigned int reg,
> + unsigned int *val)
> +{
> + struct i2c_client *client = context;
> + u8 write_buf[8] = { };
> + u8 read_buf[4];
> + int ret;
> +
> + put_unaligned_le16(reg >> 2, &write_buf[SYSMON_I2C_REG_OFS]);
> + write_buf[SYSMON_I2C_INSTR_OFS] = SYSMON_I2C_INSTR_READ;
> +
> + ret = i2c_master_send(client, write_buf, sizeof(write_buf));
> + if (ret < 0)
> + return ret;
> + if (ret != sizeof(write_buf))
> + return -EIO;
> +
> + ret = i2c_master_recv(client, read_buf, sizeof(read_buf));
[Severity: High]
Does this sequence release the I2C bus lock between the command write and the
response read?
Using i2c_master_send() followed by i2c_master_recv() will independently
acquire and release the I2C adapter lock, generating a STOP condition on the
bus between the two operations.
If other devices share this I2C bus, could an interleaved transaction reset
the SysMon's internal state machine and cause the subsequent read to return
corrupted data?
Should this be implemented using a single i2c_transfer() with two messages
(using a Repeated START), or explicitly wrapped in i2c_lock_bus() and
i2c_unlock_bus() if a STOP condition is strictly required by the hardware?
> + if (ret < 0)
> + return ret;
> + if (ret != sizeof(read_buf))
> + return -EIO;
> +
> + *val = get_unaligned_le32(read_buf);
> +
> + return 0;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260617180147.3370346-1-salih.erim@amd.com?part=3
^ permalink raw reply
* Re: [PATCH v3 4/5] clk: renesas: rzv2h-cpg: Extract PLL calculation math into a library
From: Lad, Prabhakar @ 2026-06-17 20:45 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: Michael Turquette, Stephen Boyd, Brian Masney, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Magnus Damm, linux-kernel,
linux-renesas-soc, linux-clk, devicetree, Biju Das,
Fabrizio Castro, Lad Prabhakar
In-Reply-To: <CAMuHMdVN7dfF6bah0Ca=G-uHZwJ7JDyQb5NoE59PPfAuXp+mHg@mail.gmail.com>
Hi Geert,
Thank you for the review.
On Wed, Jun 17, 2026 at 11:05 AM Geert Uytterhoeven
<geert@linux-m68k.org> wrote:
>
> Hi Prabhakar,
>
> On Mon, 15 Jun 2026 at 12:48, Prabhakar <prabhakar.csengg@gmail.com> wrote:
> > From: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> >
> > Move the common PLL and divider parameter calculation logic from the
> > core rzv2h-cpg driver into a standalone library file.
> >
> > Introduce the CLK_RZV2H_CPG_LIB Kconfig configuration symbol and create
> > rzv2h-cpg-lib.c to house rzv2h_cpg_get_pll_pars() and
> > rzv2h_cpg_get_pll_divs_pars().
> >
> > Keep rzv2h_get_pll_pars() and rzv2h_get_pll_divs_pars() in the original
> > driver as wrappers that call into the new library helper endpoints.
> > These wrappers are maintained for this cycle because they are actively
> > referenced by the DSI driver; they will be safely removed in a subsequent
> > cycle once the DSI driver is updated to use the new APIs from the library,
> > preventing cross-subsystem build breakages.
> >
> > This restructuring allows other Renesas SoC clock drivers, such as the
> > upcoming RZ/T2H and RZ/N2H platforms that utilize similar LCDC clock
> > divider mathematical logic, to share the iterative calculation helper
> > infrastructure without duplication.
> >
> > Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
>
> Thanks for your patch!
>
> > --- a/include/linux/clk/renesas.h
> > +++ b/include/linux/clk/renesas.h
> > @@ -213,4 +213,27 @@ static inline bool rzv2h_get_pll_divs_pars(const struct rzv2h_pll_limits *limits
> > }
> > #endif
> >
> > +#ifdef CONFIG_CLK_RZV2H_CPG_LIB
> > +bool rzv2h_cpg_get_pll_pars(const struct rzv2h_pll_limits *limits,
> > + struct rzv2h_pll_pars *pars, u64 freq_millihz);
> > +
> > +bool rzv2h_cpg_get_pll_divs_pars(const struct rzv2h_pll_limits *limits,
> > + struct rzv2h_pll_div_pars *pars,
> > + const u8 *table, u8 table_size, u64 freq_millihz);
> > +#else
> > +static inline bool rzv2h_cpg_get_pll_pars(const struct rzv2h_pll_limits *limits,
> > + struct rzv2h_pll_pars *pars,
> > + u64 freq_millihz)
> > +{
> > + return false;
> > +}
> > +
> > +static inline bool rzv2h_cpg_get_pll_divs_pars(const struct rzv2h_pll_limits *limits,
> > + struct rzv2h_pll_div_pars *pars,
> > + const u8 *table, u8 table_size,
> > + u64 freq_millihz)
> > +{
> > + return false;
> > +}
> > +#endif
> > #endif
>
> What about just dropping the old functions, and adding two simple
> compatibility defines in the header file:
>
> #define rzv2h_get_pll_pars rzv2h_cpg_get_pll_pars
> #define rzv2h_get_pll_divs_pars rzv2h_cpg_get_pll_divs_pars
>
Thats neat!
> That way there is less code to change in the next phase.
>
Agreed.
Cheers,
Prabhakar
> Gr{oetje,eeting}s,
>
> Geert
>
> --
> Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
>
> In personal conversations with technical people, I call myself a hacker. But
> when I'm talking to journalists I just say "programmer" or something like that.
> -- Linus Torvalds
>
^ permalink raw reply
* Re: [PATCH v4 3/3] regulator: qcom-refgen: add support for the IPQ9650 SoC
From: sashiko-bot @ 2026-06-17 20:44 UTC (permalink / raw)
To: Kathiravan Thirumoorthy; +Cc: robh, conor+dt, devicetree
In-Reply-To: <20260617-ipq9650_refgen-v4-3-c505ea6c6661@oss.qualcomm.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Critical] Unclocked MMIO read in get_status callback causes system crash.
- [Medium] Silent failure on missing clocks due to inadequate DT validation.
--
commit 90356084cf49412e26be88f69da7b99d50c27462
Author: Kathiravan Thirumoorthy <kathiravan.thirumoorthy@oss.qualcomm.com>
regulator: qcom-refgen: add support for the IPQ9650 SoC
This commit adds support for the IPQ9650 SoC to the qcom-refgen regulator
driver. It introduces explicit clock handling for this block and a get_status
callback to report the regulator status.
> diff --git a/drivers/regulator/qcom-refgen-regulator.c b/drivers/regulator/qcom-refgen-regulator.c
> index 6a3795469927e..cc72e5a43ba62 100644
> --- a/drivers/regulator/qcom-refgen-regulator.c
> +++ b/drivers/regulator/qcom-refgen-regulator.c
[ ... ]
> +static int qcom_ipq9650_refgen_disable(struct regulator_dev *rdev)
> +{
> + struct qcom_refgen_drvdata *drvdata = rdev_get_drvdata(rdev);
> +
> + clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clks);
> +
> + drvdata->is_enabled = false;
[Severity: Critical]
Does this disable the clocks before updating the internal state?
If an is_enabled check were added to get_status() to prevent unclocked reads,
could a concurrent call to get_status() still crash the system if it
interleaves after clk_bulk_disable_unprepare() but before is_enabled is
set to false?
> +
> + return 0;
> +}
[ ... ]
> +static int qcom_ipq9650_refgen_get_status(struct regulator_dev *rdev)
> +{
> + u32 val;
> +
> + regmap_read(rdev->regmap, REFGEN_REG_REFGEN_STATUS, &val);
[Severity: Critical]
Can this unclocked MMIO read cause a system crash?
The get_status() callback reads the REFGEN_REG_REFGEN_STATUS register without
ensuring the regulator's clocks are enabled. Since user space can trigger a
read of /sys/class/regulator/regulator.X/status at any time, which invokes
get_status() locklessly, a disabled regulator could cause this regmap_read()
to trigger a synchronous external abort and hang the bus.
> + if (FIELD_GET(REFGEN_STATUS_OUT_MASK, val))
> + return REGULATOR_STATUS_ON;
> +
> + return REGULATOR_STATUS_OFF;
> +}
[ ... ]
> @@ -106,10 +192,23 @@ static int qcom_refgen_probe(struct platform_device *pdev)
[ ... ]
> + if (data->has_clocks) {
> + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
> + if (!drvdata)
> + return -ENOMEM;
> +
> + drvdata->num_clks = devm_clk_bulk_get_all(dev, &drvdata->clks);
> + if (drvdata->num_clks < 0)
[Severity: Medium]
Will this silently fail if the clocks are missing from the device tree?
If the clocks property is omitted, devm_clk_bulk_get_all() returns 0.
Since this code only checks for drvdata->num_clks < 0, it treats the missing
clocks as a success, and subsequent calls to clk_bulk_prepare_enable(0, ...)
will silently succeed without enabling any clocks.
Should this explicitly verify drvdata->num_clks > 0 since clocks are mandatory
for the IPQ9650 PHY to function properly?
> + return dev_err_probe(dev, drvdata->num_clks,
> + "failed to get clocks\n");
> + }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260617-ipq9650_refgen-v4-0-c505ea6c6661@oss.qualcomm.com?part=3
^ permalink raw reply
* Re: [PATCH v7 2/4] PCI: Use standard wait times for PCIe link monitoring
From: sashiko-bot @ 2026-06-17 20:40 UTC (permalink / raw)
To: Thierry Reding; +Cc: robh, conor+dt, devicetree, linux-pci
In-Reply-To: <20260617-tegra264-pcie-v7-2-eae7ae964629@nvidia.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [High] Missing cleanup of chained IRQ handlers and IRQ domains on error paths in multiple PCIe drivers leads to Use-After-Free.
- [High] advk_pcie_wait_for_link() fails to guarantee the mandatory 100ms post-reset delay before configuration requests.
--
commit 1edbe267835d4abccd02c4abc064267f402a8ce2
Author: Thierry Reding <treding@nvidia.com>
PCI: Use standard wait times for PCIe link monitoring
Instead of defining the wait values for each driver, use common values
defined in the core pci.h header file. Convert all drivers to msleep()
and use the existing definition.
> diff --git a/drivers/pci/controller/mobiveil/pcie-mobiveil.c b/drivers/pci/controller/mobiveil/pcie-mobiveil.c
> index 62ecbaeb0a604..e8346851c49b6 100644
> --- a/drivers/pci/controller/mobiveil/pcie-mobiveil.c
> +++ b/drivers/pci/controller/mobiveil/pcie-mobiveil.c
> @@ -218,11 +218,11 @@ int mobiveil_bringup_link(struct mobiveil_pcie *pcie)
> int retries;
>
> /* check if the link is up or not */
> - for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
> + for (retries = 0; retries < PCIE_LINK_WAIT_MAX_RETRIES; retries++) {
> if (mobiveil_pcie_link_up(pcie))
> return 0;
>
> - usleep_range(LINK_WAIT_MIN, LINK_WAIT_MAX);
> + msleep(PCIE_LINK_WAIT_SLEEP_MS);
> }
>
> dev_err(&pcie->pdev->dev, "link never came up\n");
[Severity: High]
This isn't a bug introduced by this patch, but is there a potential
Use-After-Free in the error paths handling these link bring-up failures?
In the caller mobiveil_pcie_host_probe(), if mobiveil_bringup_link() fails, it
returns directly:
ret = mobiveil_bringup_link(pcie);
if (ret) {
dev_info(dev, "link bring-up failed\n");
return ret;
}
Since chained handlers are set up earlier using
irq_set_chained_handler_and_data(..., pcie), returning an error causes the
devres-managed pcie struct to be freed. If the interrupt fires after this, won't
the active handler dereference the freed pcie pointer?
A similar issue appears in nwl_pcie_probe() if nwl_wait_for_link() fails. Should
these error paths unregister the chained handlers and call irq_domain_remove()
to prevent this UAF and resource leak?
[ ... ]
> diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c
> index fd9c7d53e8a78..272c5c8fc1e5c 100644
> --- a/drivers/pci/controller/pci-aardvark.c
> +++ b/drivers/pci/controller/pci-aardvark.c
[ ... ]
> @@ -350,13 +347,13 @@ static int advk_pcie_wait_for_link(struct advk_pcie *pcie)
> int retries;
>
> /* check if the link is up or not */
> - for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
> + for (retries = 0; retries < PCIE_LINK_WAIT_MAX_RETRIES; retries++) {
> if (advk_pcie_link_up(pcie)) {
> pci_host_common_link_train_delay(pcie->link_gen);
> return 0;
> }
>
> - usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
> + msleep(PCIE_LINK_WAIT_SLEEP_MS);
> }
[Severity: High]
This is a pre-existing issue, but does advk_pcie_wait_for_link() fail to
guarantee the mandatory 100ms post-reset delay before configuration requests?
The PCIe Base Specification mandates a minimum 100ms delay after a conventional
reset (e.g., PERST# deassertion) before the host can send Configuration
Requests. The caller advk_pcie_setup_hw() asserts PERST# and delegates the
post-reset delay to this function.
If the link trains rapidly and reaches L0 early, this loop returns immediately.
For link speeds of Gen2 or lower, pci_host_common_link_train_delay() is a no-op,
so no additional delay is added.
Does this allow the driver to issue Configuration Requests before the endpoint's
100ms initialization period has elapsed, potentially causing enumeration failures
for compliant endpoints?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260617-tegra264-pcie-v7-0-eae7ae964629@nvidia.com?part=2
^ permalink raw reply
* Re: [PATCH v4 03/10] dt-bindings: input: microchip,cap11xx: Update datasheet URL and LED reg range
From: sashiko-bot @ 2026-06-17 20:31 UTC (permalink / raw)
To: Jun Yan; +Cc: devicetree, linux-input, robh, dmitry.torokhov, conor+dt
In-Reply-To: <20260617150318.753148-4-jerrysteve1101@gmail.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Incomplete Refactoring / Code and Commit Message Mismatch
--
commit e8499e0ca62db6a2ee6dc0986e7c04ea1337f6c7
Author: Jun Yan <jerrysteve1101@gmail.com>
Add datasheet links for all supported CAP11xx variants.
Update LED node regex and replace enum constraints with minimum/maximum
for LED reg ranges in preparation for CAP1114 support. Drop unnecessary
led unit-address pattern.
> diff --git a/Documentation/devicetree/bindings/input/microchip,cap11xx.yaml b/Documentation/devicetree/bindings/input/microchip,cap11xx.yaml
> index 7ade03f1b32b..eabf06a1163e 100644
> --- a/Documentation/devicetree/bindings/input/microchip,cap11xx.yaml
> +++ b/Documentation/devicetree/bindings/input/microchip,cap11xx.yaml
[ ... ]
> @@ -131,7 +140,9 @@ patternProperties:
>
> properties:
> reg:
> - enum: [0, 1, 2, 3, 4, 5, 6, 7]
> + description: LED channel number
> + minimum: 0
> + maximum: 7
[Severity: Medium]
Does this fully prepare the schema for CAP1114 support as mentioned in the
commit message?
While the constraints on the reg property were updated here, the main
patternProperties definition for the LED node just above this still uses
"^led@[0-7]$".
Since CAP1114 has 11 LED channels, wouldn't the schema fail validation for
nodes like led@8 and led@9 because the top-level pattern wasn't also updated?
[ ... ]
> @@ -158,7 +169,7 @@ allOf:
> - microchip,cap1298
> then:
> patternProperties:
> - "^led@[0-7]$": false
> + "^led@": false
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260617150318.753148-1-jerrysteve1101@gmail.com?part=3
^ permalink raw reply
* Re: [PATCH v7 1/4] dt-bindings: pci: Strictly distinguish C0 from C1-C5
From: sashiko-bot @ 2026-06-17 20:25 UTC (permalink / raw)
To: Thierry Reding; +Cc: robh, devicetree, linux-pci, conor+dt
In-Reply-To: <20260617-tegra264-pcie-v7-1-eae7ae964629@nvidia.com>
> From: Thierry Reding <treding@nvidia.com>
>
> Instead of using the ECAM registers as the first entry, strictly make a
> distinction between C0 and C1-C5. This is needed because otherwise the
> unit address doesn't match the first "reg" entry. We also cannot change
> the ordering of these nodes to follow the ECAM addresses because that
> would put them outside of their "control bus" hierarchy since the ECAM
> address space is a global one outside of any of the control busses.
>
> Signed-off-by: Thierry Reding <treding@nvidia.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260617-tegra264-pcie-v7-0-eae7ae964629@nvidia.com?part=1
^ permalink raw reply
* Re: [PATCH v3] dt-bindings: arm: xen: Convert to DT schema
From: sashiko-bot @ 2026-06-17 20:21 UTC (permalink / raw)
To: Tejas Mutalikdesai; +Cc: devicetree, robh, conor+dt
In-Reply-To: <20260617152830.51382-1-tejasmutalikdesai@gmail.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] The new DT schema strictly enforces a unit address for the `hypervisor` node name (`^hypervisor@[0-9a-f]+$`). This causes `make dtbs_check` validation failures for existing in-tree DTS files and, if fixed in the DTS, will break Xen detection in the Linux kernel.
--
commit 8184816dad4691fc686e16cca1b38c8628e65beb
Author: Tejas <tejasmutalikdesai@gmail.com>
dt-bindings: arm: xen: Convert to DT schema
Convert the Xen ARM device tree binding documentation from the legacy
plain-text format to the DT schema format.
> diff --git a/Documentation/devicetree/bindings/arm/xen.yaml b/Documentation/devicetree/bindings/arm/xen.yaml
> new file mode 100644
> index 0000000000000..a22e950566c23
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/arm/xen.yaml
[ ... ]
> +description:
> + Xen ARM virtual platforms shall have a top-level "hypervisor" node with
> + the properties defined below.
> +
> +properties:
> + $nodename:
> + pattern: "^hypervisor@[0-9a-f]+$"
[Severity: High]
Will this strict node name pattern break existing device trees and kernel
parsing?
Existing in-tree device trees (like arch/arm/boot/dts/xen/xenvm-4.2.dts) use
the node name "hypervisor" without a unit address, which will now fail
schema validation under this pattern.
If device trees are updated to include a unit address to satisfy this new
schema, will it break Xen paravirtualization on ARM? The kernel's FDT
parsing explicitly requires an exact string match without a unit address
during early initialization:
arch/arm/xen/enlighten.c:fdt_find_hyper_node() {
...
if (depth != 1 || strcmp(uname, "hypervisor") != 0)
return 0;
...
}
> +
> + compatible:
> + description:
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260617152830.51382-1-tejasmutalikdesai@gmail.com?part=1
^ permalink raw reply
* Re: [PATCH v4 02/10] Input: cap11xx - remove unused register macros
From: sashiko-bot @ 2026-06-17 20:21 UTC (permalink / raw)
To: Jun Yan; +Cc: linux-input, dmitry.torokhov, robh, devicetree, conor+dt
In-Reply-To: <20260617150318.753148-3-jerrysteve1101@gmail.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [Medium] The patch fails to remove unused definitions in `cap11xx_volatile_reg` despite the commit message claiming otherwise.
Pre-existing issues:
- [High] The threaded interrupt handler unconditionally returns IRQ_HANDLED even when I2C operations fail to clear the hardware interrupt source, risking an IRQ storm.
--
commit 8aa29f5d8b4eefa859454cdb0496fecf07ff5aa8
Author: Jun Yan <jerrysteve1101@gmail.com>
Remove unused register address macros and unused definitions in
the cap11xx_reg_defaults array and cap11xx_volatile_reg.
This cleanup reduces code clutter and makes the driver easier to
maintain without affecting functionality.
> Remove unused register address macros and unused definitions in
> the cap11xx_reg_defaults array and cap11xx_volatile_reg.
[Severity: Medium]
Will this patch actually remove the unused definitions in
cap11xx_volatile_reg?
The commit message states they are removed, but the patch doesn't modify
that function.
> diff --git a/drivers/input/keyboard/cap11xx.c b/drivers/input/keyboard/cap11xx.c
> index 485d8ba97723..fae26f035186 100644
> --- a/drivers/input/keyboard/cap11xx.c
> +++ b/drivers/input/keyboard/cap11xx.c
> @@ -20,53 +20,24 @@
> #define CAP11XX_REG_MAIN_CONTROL_GAIN_SHIFT (6)
> #define CAP11XX_REG_MAIN_CONTROL_GAIN_MASK (0xc0)
> #define CAP11XX_REG_MAIN_CONTROL_DLSEEP BIT(4)
> -#define CAP11XX_REG_GENERAL_STATUS 0x02
> #define CAP11XX_REG_SENSOR_INPUT 0x03
> -#define CAP11XX_REG_NOISE_FLAG_STATUS 0x0a
> #define CAP11XX_REG_SENOR_DELTA(X) (0x10 + (X))
[Severity: Medium]
Should this macro also have been removed?
Because cap11xx_volatile_reg() was not updated, this macro and its
corresponding case entries:
case CAP11XX_REG_SENOR_DELTA(0):
case CAP11XX_REG_SENOR_DELTA(1):
were left in the file.
[ ... ]
> static bool cap11xx_volatile_reg(struct device *dev, unsigned int reg)
[Severity: High]
This isn't a bug introduced by this patch, but could the error handling
in cap11xx_thread_func() risk an IRQ storm?
If an I2C communication error occurs during the interrupt handler:
cap11xx_thread_func() {
...
if (ret < 0)
goto out;
...
out:
return IRQ_HANDLED;
}
Because this uses IRQF_ONESHOT, returning IRQ_HANDLED instructs the IRQ
subsystem to unmask the interrupt. Since the device's interrupt status
was never cleared due to the I2C failure, the interrupt line will remain
asserted.
Will this cause the interrupt to immediately fire again when unmasked,
creating an infinite loop?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260617150318.753148-1-jerrysteve1101@gmail.com?part=2
^ permalink raw reply
* Re: [PATCH v3 1/5] clk: renesas: rzv2h-cpg: Use per-SoC PLL reference frequency for calculations
From: Lad, Prabhakar @ 2026-06-17 20:17 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: Michael Turquette, Stephen Boyd, Brian Masney, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Magnus Damm, linux-kernel,
linux-renesas-soc, linux-clk, devicetree, Biju Das,
Fabrizio Castro, Lad Prabhakar
In-Reply-To: <CAMuHMdWBeaTB4QK5vvB0CQm+yrX1ps3V2EtFu0Q4a0JG_94f7g@mail.gmail.com>
Hi Geert,
Thank you for the review.
On Wed, Jun 17, 2026 at 10:48 AM Geert Uytterhoeven
<geert@linux-m68k.org> wrote:
>
> Hi Prabhakar,
>
> On Mon, 15 Jun 2026 at 12:48, Prabhakar <prabhakar.csengg@gmail.com> wrote:
> > From: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> >
> > Introduce a per-SoC PLL reference input frequency parameter to avoid
> > relying on a hardcoded 24MHz constant during PLL configuration math.
> >
> > Add an input_fref member to struct rzv2h_pll_limits. In the core
> > calculation helper rzv2h_get_pll_pars(), derive the base input clock
> > rate from limits->input_fref, utilizing the conditional ternary operator
> > to fall back to 24MHz if the struct field is left uninitialized (0), and
> > drop the obsolete macro RZ_V2H_OSC_CLK_IN_MEGA.
> >
> > This abstraction permits the reuse of the common PLL divider logic on
> > newer SoC platforms like the RZ/T2H, which feature a 48 MHz PLL reference
> > clock input instead of the 24 MHz signal used by RZ/V2H(P), without
> > disrupting existing platforms.
> >
> > Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
>
> Thanks for your patch!
>
> > --- a/include/linux/clk/renesas.h
> > +++ b/include/linux/clk/renesas.h
> > @@ -53,6 +53,9 @@ static inline void rzg2l_cpg_dsi_div_set_divider(u8 divider, int target) { }
> > * various parameters used to configure a PLL. These limits ensure
> > * the PLL operates within valid and stable ranges.
> > *
> > + * @input_fref: Reference input frequency to the PLL (in MHz). If set
>
> "in Hz", as pointed out by Sashiko.
>
Agreed.
> > + * to 0, a default value of 24MHz is used.
> > + *
I'll also replace `24MHz` to `24000000 Hz`.
Cheers,
Prabhakar
> > * @fout: Output frequency range (in MHz)
> > * @fout.min: Minimum allowed output frequency
> > * @fout.max: Maximum allowed output frequency
> > @@ -78,6 +81,8 @@ static inline void rzg2l_cpg_dsi_div_set_divider(u8 divider, int target) { }
> > * @k.max: Maximum delta-sigma value
> > */
> > struct rzv2h_pll_limits {
> > + u32 input_fref;
> > +
> > struct {
> > u32 min;
> > u32 max;
>
> The rest LGTM, so with the above fixed:
> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
>
> Gr{oetje,eeting}s,
>
> Geert
>
> --
> Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
>
> In personal conversations with technical people, I call myself a hacker. But
> when I'm talking to journalists I just say "programmer" or something like that.
> -- Linus Torvalds
^ permalink raw reply
* Re: [PATCH 3/9] firmware: imx: ele: Add API functions for OCOTP fuse access
From: Frank Li @ 2026-06-17 19:56 UTC (permalink / raw)
To: Frieder Schrempf
Cc: Pankaj Gupta, Peng Fan (OSS), Frieder Schrempf,
Srinivas Kandagatla, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Shawn Guo, devicetree, imx, linux-arm-kernel,
linux-kernel
In-Reply-To: <9c1d34ec-0b96-471e-8b60-6b4c852878bf@kontron.de>
On Wed, Jun 17, 2026 at 08:54:35AM +0200, Frieder Schrempf wrote:
> On 16.06.26 22:05, Frank Li wrote:
> > On Tue, Jun 16, 2026 at 07:59:54PM +0200, Frieder Schrempf wrote:
> >> On 16.06.26 17:36, Frank Li wrote:
> >>> On Tue, Jun 16, 2026 at 01:52:18PM +0200, Frieder Schrempf wrote:
> >>>> From: Frieder Schrempf <frieder.schrempf@kontron.de>
> >>>>
> >>>> The ELE S400 API provides read and write access to the OCOTP fuse
> >>>> registers. This adds the necessary API functions imx_se_read_fuse()
> >>>> and imx_se_write_fuse() to be used by other drivers such as the
> >>>> OCOTP S400 NVMEM driver.
> >>>>
> >>>> This is ported from the downstream vendor kernel.
> >>>>
> >>>> Signed-off-by: Frieder Schrempf <frieder.schrempf@kontron.de>
> >>>> ---
> >>>> drivers/firmware/imx/ele_base_msg.c | 122 ++++++++++++++++++++++++++++++++++++
> >>>> drivers/firmware/imx/ele_base_msg.h | 6 ++
> >>>> include/linux/firmware/imx/se_api.h | 3 +
> >>>> 3 files changed, 131 insertions(+)
> >>>>
> >>> ...
> >>>> +++ b/include/linux/firmware/imx/se_api.h
> >>>> @@ -11,4 +11,7 @@
> >>>> #define SOC_ID_OF_IMX8ULP 0x084d
> >>>> #define SOC_ID_OF_IMX93 0x9300
> >>>>
> >>>> +int imx_se_read_fuse(void *se_if_data, uint16_t fuse_id, u32 *value);
> >>>> +int imx_se_write_fuse(void *se_if_data, uint16_t fuse_id, u32 value);
> >>>> +
> >>>
> >>> This API should implement in fuse drivers. Other consume should use standard
> >>> fuse API to get value. If put here, it may bypass fuse driver.
> >>
> >> The reason this is here, is the downstream implementation in linux-imx
> >> and the current code organization.
> >
> > Downstream may not good enough, sometime, it is quick solution.
>
> Ok, but the code structure and API design has been upstreamed like this
> and the refactoring could have been done before, if downstream is known
> to not be well organized.
>
> >
> >> I thought there is some good reason
> >> to have shared functions and it looks like Pankaj structured it like
> >> this so all API functions live in ele_base_msg.c and the internal
> >> structs and defines in ele_base_msg.h and se_ctrl.h are not exposed to
> >> other drivers.
> >>
> >> If I would move this into imx-ocotp-ele.c, then I would also need to
> >> change how the code is organized and make the internal se_api functions
> >> exposed to other drivers. I don't know if that is really a good idea.
> >>
> >> I get your point but it looks like this contradicts the intention of
> >> having a clean API in the firmware driver.
> >
> > You can refer imx-ocotp-scu.c, structure should be similar, only difference
> > is that lower transfer APIs.
> Ok, this would mean that I expose the generic SE functions and structs
> required for fuse handling. In practice, I would remove
> imx_se_read_fuse() and imx_se_write_fuse() from se_api.h and instead add
> the following:
>
> struct se_msg_hdr { ... };
> struct se_api_msg { ... };
> struct se_if_priv;
> se_fill_cmd_msg_hdr( ... );
> se_msg_send_rcv( ... );
> se_val_rsp_hdr_n_status( ... );
>
> Then I would export the functions in ele_common.c and put the fuse
> read/write functions in the NVMEM driver.
>
> Is that what you want me to do?
Yes, Idealy, it should be children device under ele, ELE like a bus, which
previous lower level data transfer, ocotp should be base on top then it.
like spi/i2c, which provide low level data transfer.
Frank
>
> Pankaj (and maybe Peng), do you have any comments on this?
>
> Thanks!
^ permalink raw reply
* [PATCH v5 8/8] arm64: dts: imx8qxp-mek: add parallel ov5640 camera support
From: Frank.Li @ 2026-06-17 19:50 UTC (permalink / raw)
To: Sakari Ailus, Mauro Carvalho Chehab, Michael Riesch,
Laurent Pinchart, Frank Li, Martin Kepplinger-Novakovic,
Rui Miguel Silva, Purism Kernel Team, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam
Cc: linux-media, linux-kernel, imx, devicetree, linux-arm-kernel
In-Reply-To: <20260617-imx8qxp_pcam-v5-0-7fa6c8e7fba7@nxp.com>
From: Frank Li <Frank.Li@nxp.com>
Add parallel ov5640 nodes in imx8qxp-mek and create overlay file to enable
it because it can work at two mode: MIPI CSI and parallel mode.
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
changes in v4
- add hsync-active = <1>
changes in v3
- replace csi with cpi.
- use imx8qxp-mek-ov5640-cpi.dtso since csi use imx8qxp-mek-ov5640-csi.dtso
change in v2
- move ov5640 part to overlay file
- rename to imx8qxp-mek-ov5640-parallel.dtso
- remove data-lanes
---
arch/arm64/boot/dts/freescale/Makefile | 3 +
.../boot/dts/freescale/imx8qxp-mek-ov5640-cpi.dtso | 83 ++++++++++++++++++++++
2 files changed, 86 insertions(+)
diff --git a/arch/arm64/boot/dts/freescale/Makefile b/arch/arm64/boot/dts/freescale/Makefile
index 001ca3a12c0ae..3b9e9844f11ef 100644
--- a/arch/arm64/boot/dts/freescale/Makefile
+++ b/arch/arm64/boot/dts/freescale/Makefile
@@ -554,6 +554,9 @@ dtb-$(CONFIG_ARCH_MXC) += imx8qxp-mek-pcie-ep.dtb
imx8qxp-mek-ov5640-csi-dtbs := imx8qxp-mek.dtb imx8qxp-mek-ov5640-csi.dtbo
dtb-${CONFIG_ARCH_MXC} += imx8qxp-mek-ov5640-csi.dtb
+imx8qxp-mek-ov5640-cpi-dtbs := imx8qxp-mek.dtb imx8qxp-mek-ov5640-cpi.dtbo
+dtb-${CONFIG_ARCH_MXC} += imx8qxp-mek-ov5640-cpi.dtb
+
dtb-$(CONFIG_ARCH_MXC) += imx8qxp-tqma8xqp-mba8xx.dtb
dtb-$(CONFIG_ARCH_MXC) += imx8qxp-tqma8xqps-mb-smarc-2.dtb
dtb-$(CONFIG_ARCH_MXC) += imx8ulp-9x9-evk.dtb
diff --git a/arch/arm64/boot/dts/freescale/imx8qxp-mek-ov5640-cpi.dtso b/arch/arm64/boot/dts/freescale/imx8qxp-mek-ov5640-cpi.dtso
new file mode 100644
index 0000000000000..9fbdd798f17d6
--- /dev/null
+++ b/arch/arm64/boot/dts/freescale/imx8qxp-mek-ov5640-cpi.dtso
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright 2025 NXP
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/imx8-lpcg.h>
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/media/video-interfaces.h>
+#include <dt-bindings/pinctrl/pads-imx8qxp.h>
+
+&cm40_i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ov5640_pi: camera@3c {
+ compatible = "ovti,ov5640";
+ reg = <0x3c>;
+ clocks = <&pi0_misc_lpcg IMX_LPCG_CLK_0>;
+ clock-names = "xclk";
+ assigned-clocks = <&pi0_misc_lpcg IMX_LPCG_CLK_0>;
+ assigned-clock-rates = <24000000>;
+ AVDD-supply = <®_2v8>;
+ DOVDD-supply = <®_1v8>;
+ DVDD-supply = <®_1v5>;
+ pinctrl-0 = <&pinctrl_parallel_cpi>;
+ pinctrl-names = "default";
+ powerdown-gpios = <&lsio_gpio3 2 GPIO_ACTIVE_HIGH>;
+ reset-gpios = <&lsio_gpio3 3 GPIO_ACTIVE_LOW>;
+
+ port {
+ ov5640_pi_ep: endpoint {
+ bus-type = <MEDIA_BUS_TYPE_PARALLEL>;
+ bus-width = <8>;
+ hsync-active = <1>;
+ pclk-sample = <1>;
+ remote-endpoint = <¶llel_cpi_in>;
+ vsync-active = <0>;
+ };
+ };
+ };
+};
+
+&iomuxc {
+ pinctrl_parallel_cpi: parallelcpigrp {
+ fsl,pins = <
+ IMX8QXP_CSI_D00_CI_PI_D02 0xc0000041
+ IMX8QXP_CSI_D01_CI_PI_D03 0xc0000041
+ IMX8QXP_CSI_D02_CI_PI_D04 0xc0000041
+ IMX8QXP_CSI_D03_CI_PI_D05 0xc0000041
+ IMX8QXP_CSI_D04_CI_PI_D06 0xc0000041
+ IMX8QXP_CSI_D05_CI_PI_D07 0xc0000041
+ IMX8QXP_CSI_D06_CI_PI_D08 0xc0000041
+ IMX8QXP_CSI_D07_CI_PI_D09 0xc0000041
+
+ IMX8QXP_CSI_MCLK_CI_PI_MCLK 0xc0000041
+ IMX8QXP_CSI_PCLK_CI_PI_PCLK 0xc0000041
+ IMX8QXP_CSI_HSYNC_CI_PI_HSYNC 0xc0000041
+ IMX8QXP_CSI_VSYNC_CI_PI_VSYNC 0xc0000041
+ IMX8QXP_CSI_EN_LSIO_GPIO3_IO02 0xc0000041
+ IMX8QXP_CSI_RESET_LSIO_GPIO3_IO03 0xc0000041
+ >;
+ };
+};
+
+&isi {
+ status = "okay";
+};
+
+¶llel_cpi {
+ status = "okay";
+
+ ports {
+ port@0 {
+ parallel_cpi_in: endpoint {
+ hsync-active = <1>;
+ remote-endpoint = <&ov5640_pi_ep>;
+ };
+ };
+ };
+};
--
2.43.0
^ permalink raw reply related
* [PATCH v5 7/8] arm64: dts: imx8: add camera parallel interface (CPI) node
From: Frank.Li @ 2026-06-17 19:50 UTC (permalink / raw)
To: Sakari Ailus, Mauro Carvalho Chehab, Michael Riesch,
Laurent Pinchart, Frank Li, Martin Kepplinger-Novakovic,
Rui Miguel Silva, Purism Kernel Team, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam
Cc: linux-media, linux-kernel, imx, devicetree, linux-arm-kernel
In-Reply-To: <20260617-imx8qxp_pcam-v5-0-7fa6c8e7fba7@nxp.com>
From: Frank Li <Frank.Li@nxp.com>
Add camera parallel interface (CPI) node.
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
changes in v4
- none
changes in v3
- replace csi with cpi.
changes in v2
- update compatible string to match binding's change
---
arch/arm64/boot/dts/freescale/imx8-ss-img.dtsi | 13 +++++++++++
arch/arm64/boot/dts/freescale/imx8qxp-ss-img.dtsi | 27 +++++++++++++++++++++++
2 files changed, 40 insertions(+)
diff --git a/arch/arm64/boot/dts/freescale/imx8-ss-img.dtsi b/arch/arm64/boot/dts/freescale/imx8-ss-img.dtsi
index a72b2f1c4a1b2..b504f99f6acdb 100644
--- a/arch/arm64/boot/dts/freescale/imx8-ss-img.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8-ss-img.dtsi
@@ -222,6 +222,19 @@ irqsteer_parallel: irqsteer@58260000 {
status = "disabled";
};
+ parallel_cpi: cpi@58261000 {
+ compatible = "fsl,imx8qxp-pcif";
+ reg = <0x58261000 0x1000>;
+ clocks = <&pi0_pxl_lpcg IMX_LPCG_CLK_0>,
+ <&pi0_ipg_lpcg IMX_LPCG_CLK_4>;
+ clock-names = "pixel", "ipg";
+ assigned-clocks = <&clk IMX_SC_R_PI_0 IMX_SC_PM_CLK_PER>;
+ assigned-clock-parents = <&clk IMX_SC_R_PI_0_PLL IMX_SC_PM_CLK_PLL>;
+ assigned-clock-rates = <160000000>;
+ power-domains = <&pd IMX_SC_R_PI_0>;
+ status = "disabled";
+ };
+
pi0_ipg_lpcg: clock-controller@58263004 {
compatible = "fsl,imx8qxp-lpcg";
reg = <0x58263004 0x4>;
diff --git a/arch/arm64/boot/dts/freescale/imx8qxp-ss-img.dtsi b/arch/arm64/boot/dts/freescale/imx8qxp-ss-img.dtsi
index 232cf25dadfcd..5aae15540d6cb 100644
--- a/arch/arm64/boot/dts/freescale/imx8qxp-ss-img.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8qxp-ss-img.dtsi
@@ -62,6 +62,14 @@ isi_in_2: endpoint {
remote-endpoint = <&mipi_csi0_out>;
};
};
+
+ port@4 {
+ reg = <4>;
+
+ isi_in_4: endpoint {
+ remote-endpoint = <¶llel_cpi_out>;
+ };
+ };
};
};
@@ -95,3 +103,22 @@ &jpegenc {
&mipi_csi_1 {
status = "disabled";
};
+
+¶llel_cpi {
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ };
+
+ port@1 {
+ reg = <1>;
+
+ parallel_cpi_out: endpoint {
+ remote-endpoint = <&isi_in_4>;
+ };
+ };
+ };
+};
--
2.43.0
^ permalink raw reply related
* [PATCH v5 6/8] media: nxp: add V4L2 subdev driver for camera parallel interface (CPI)
From: Frank.Li @ 2026-06-17 19:50 UTC (permalink / raw)
To: Sakari Ailus, Mauro Carvalho Chehab, Michael Riesch,
Laurent Pinchart, Frank Li, Martin Kepplinger-Novakovic,
Rui Miguel Silva, Purism Kernel Team, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam
Cc: linux-media, linux-kernel, imx, devicetree, linux-arm-kernel,
Alice Yuan, Robert Chiras, Zhipeng Wang
In-Reply-To: <20260617-imx8qxp_pcam-v5-0-7fa6c8e7fba7@nxp.com>
From: Alice Yuan <alice.yuan@nxp.com>
Add a V4L2 sub-device driver for the CPI controller found on i.MX8QXP,
i.MX8QM, and i.MX93 SoCs. This controller supports parallel camera sensors
and enables image data capture through a parallel interface.
Signed-off-by: Alice Yuan <alice.yuan@nxp.com>
Signed-off-by: Robert Chiras <robert.chiras@nxp.com>
Signed-off-by: Zhipeng Wang <zhipeng.wang_1@nxp.com>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
Change in v5
- Use subdev_1to1 register function
- Use v4l2_subdev_get_frame_desc_passthrough
- Use dwc csi2 similar logic enable/disable stream
- Add route settup at imx_cpi_init_state()
- Remove V2 register layout support, add it later
change in v4
- remove unnecesary header file.
- use devm_bulk_clk_get().
- update kConfig i.MX8/i.MX9
- Remove define IMX_CPI_DEF_PIX_WIDTH ..., which used once only
- drop get_interface_ctrl_reg1_param
- drop uv-swap
- drop imx_cpi_link_setup by use immutable link.
- use enable/disable_stream() replace depericated .s_stream.
- remove dbg print and reg dump functions.
- use goto/.remove() to do manual cleanup.
- remove imx93 support. Add it later.
change in v3
- replace csi with cpi
- use __free(fwnode_handle) to simpilfy code
- remove imx91 driver data, which is the same as imx93
change in v2
- remove MODULE_ALIAS
- use devm_pm_runtime_enable() and cleanup remove function
- change output format to 1x16. controller convert 2x8 to 1x16 format
---
MAINTAINERS | 1 +
drivers/media/platform/nxp/Kconfig | 12 +
drivers/media/platform/nxp/Makefile | 1 +
drivers/media/platform/nxp/imx-parallel-cpi.c | 614 ++++++++++++++++++++++++++
4 files changed, 628 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index deeec900ad071..e28b395734ef5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -16263,6 +16263,7 @@ F: Documentation/devicetree/bindings/media/nxp,imx-mipi-csi2.yaml
F: Documentation/devicetree/bindings/media/nxp,imx7-csi.yaml
F: Documentation/devicetree/bindings/media/nxp,imx8mq-mipi-csi2.yaml
F: drivers/media/platform/nxp/imx-mipi-csis.c
+F: drivers/media/platform/nxp/imx-parallel-cpi.c
F: drivers/media/platform/nxp/imx7-media-csi.c
F: drivers/media/platform/nxp/imx8mq-mipi-csi2.c
diff --git a/drivers/media/platform/nxp/Kconfig b/drivers/media/platform/nxp/Kconfig
index 40e3436669e21..90f7c792003f2 100644
--- a/drivers/media/platform/nxp/Kconfig
+++ b/drivers/media/platform/nxp/Kconfig
@@ -39,6 +39,18 @@ config VIDEO_IMX_MIPI_CSIS
Video4Linux2 sub-device driver for the MIPI CSI-2 CSIS receiver
v3.3/v3.6.3 found on some i.MX7 and i.MX8 SoCs.
+config VIDEO_IMX_PARALLEL_CPI
+ tristate "NXP i.MX8/i.MX9 Parallel CPI Driver"
+ depends on ARCH_MXC || COMPILE_TEST
+ depends on VIDEO_DEV
+ select MEDIA_CONTROLLER
+ select V4L2_1TO1
+ select V4L2_FWNODE
+ select VIDEO_V4L2_SUBDEV_API
+ help
+ Video4Linux2 sub-device driver for PARALLEL CPI receiver found
+ on some iMX8 and iMX9 SoCs.
+
source "drivers/media/platform/nxp/imx8-isi/Kconfig"
# mem2mem drivers
diff --git a/drivers/media/platform/nxp/Makefile b/drivers/media/platform/nxp/Makefile
index 4d90eb7136525..5346919d2f108 100644
--- a/drivers/media/platform/nxp/Makefile
+++ b/drivers/media/platform/nxp/Makefile
@@ -7,5 +7,6 @@ obj-y += imx8-isi/
obj-$(CONFIG_VIDEO_IMX7_CSI) += imx7-media-csi.o
obj-$(CONFIG_VIDEO_IMX8MQ_MIPI_CSI2) += imx8mq-mipi-csi2.o
obj-$(CONFIG_VIDEO_IMX_MIPI_CSIS) += imx-mipi-csis.o
+obj-$(CONFIG_VIDEO_IMX_PARALLEL_CPI) += imx-parallel-cpi.o
obj-$(CONFIG_VIDEO_IMX_PXP) += imx-pxp.o
obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o
diff --git a/drivers/media/platform/nxp/imx-parallel-cpi.c b/drivers/media/platform/nxp/imx-parallel-cpi.c
new file mode 100644
index 0000000000000..00f5d5f47644b
--- /dev/null
+++ b/drivers/media/platform/nxp/imx-parallel-cpi.c
@@ -0,0 +1,614 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * i.MX Parallel CPI receiver driver.
+ *
+ * Copyright 2019-2025 NXP
+ *
+ */
+
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/limits.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-device-1to1.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+
+/* CI_PI INTERFACE CONTROL */
+#define IF_CTRL_REG_PL_ENABLE BIT(0)
+#define IF_CTRL_REG_PL_VALID BIT(1)
+#define IF_CTRL_REG_DATA_TYPE_SEL BIT(8)
+#define IF_CTRL_REG_DATA_TYPE(x) FIELD_PREP(GENMASK(13, 9), (x))
+
+#define DATA_TYPE_OUT_NULL 0x00
+#define DATA_TYPE_OUT_RGB 0x04
+#define DATA_TYPE_OUT_YUV444 0x08
+#define DATA_TYPE_OUT_YYU420_ODD 0x10
+#define DATA_TYPE_OUT_YYU420_EVEN 0x12
+#define DATA_TYPE_OUT_YYY_ODD 0x18
+#define DATA_TYPE_OUT_UYVY_EVEN 0x1a
+#define DATA_TYPE_OUT_RAW 0x1c
+
+#define IF_CTRL_REG_IF_FORCE_HSYNV_OVERRIDE 0x4
+#define IF_CTRL_REG_IF_FORCE_VSYNV_OVERRIDE 0x2
+#define IF_CTRL_REG_IF_FORCE_DATA_ENABLE_OVERRIDE 0x1
+
+/* CPI INTERFACE CONTROL REG */
+#define CPI_CTRL_REG_CPI_EN BIT(0)
+#define CPI_CTRL_REG_PIXEL_CLK_POL BIT(1)
+#define CPI_CTRL_REG_HSYNC_POL BIT(2)
+#define CPI_CTRL_REG_VSYNC_POL BIT(3)
+#define CPI_CTRL_REG_DE_POL BIT(4)
+#define CPI_CTRL_REG_PIXEL_DATA_POL BIT(5)
+#define CPI_CTRL_REG_CCIR_EXT_VSYNC_EN BIT(6)
+#define CPI_CTRL_REG_CCIR_EN BIT(7)
+#define CPI_CTRL_REG_CCIR_VIDEO_MODE BIT(8)
+#define CPI_CTRL_REG_CCIR_NTSC_EN BIT(9)
+#define CPI_CTRL_REG_CCIR_VSYNC_RESET_EN BIT(10)
+#define CPI_CTRL_REG_CCIR_ECC_ERR_CORRECT_EN BIT(11)
+#define CPI_CTRL_REG_HSYNC_FORCE_EN BIT(12)
+#define CPI_CTRL_REG_VSYNC_FORCE_EN BIT(13)
+#define CPI_CTRL_REG_GCLK_MODE_EN BIT(14)
+#define CPI_CTRL_REG_VALID_SEL BIT(15)
+#define CPI_CTRL_REG_RAW_OUT_SEL BIT(16)
+#define CPI_CTRL_REG_HSYNC_OUT_SEL BIT(17)
+#define CPI_CTRL_REG_HSYNC_PULSE(x) FIELD_PREP(GENMASK(21, 19), (x))
+#define CPI_CTRL_REG_UV_SWAP_EN BIT(22)
+#define CPI_CTRL_REG_DATA_TYPE_IN(x) FIELD_PREP(GENMASK(26, 23), (x))
+#define CPI_CTRL_REG_MASK_VSYNC_COUNTER(x) FIELD_PREP(GENMASK(28, 27), (x))
+#define CPI_CTRL_REG_SOFTRST BIT(31)
+
+/* CPI INTERFACE STATUS */
+#define CPI_STATUS_FIELD_TOGGLE BIT(0)
+#define CPI_STATUS_ECC_ERROR BIT(1)
+
+/* CPI INTERFACE CONTROL REG1 */
+#define CPI_CTRL_REG1_PIXEL_WIDTH(v) FIELD_PREP(GENMASK(15, 0), (v))
+#define CPI_CTRL_REG1_VSYNC_PULSE(v) FIELD_PREP(GENMASK(31, 16), (v))
+
+#define CPI_CTRL_V2_REG1_PIXEL_WIDTH(v) FIELD_PREP(GENMASK(16, 0), (v))
+#define CPI_CTRL_V2_REG1_VSYNC_PULSE(v) FIELD_PREP(GENMASK(31, 16), (v))
+
+/* Need match field DATA_TYPE_IN definition at CPI CTRL register */
+enum cpi_in_data_type {
+ CPI_IN_DT_UYVY_BT656_8 = 0x0,
+ CPI_IN_DT_UYVY_BT656_10,
+ CPI_IN_DT_RGB_8,
+ CPI_IN_DT_BGR_8,
+ CPI_IN_DT_YVYU_8 = 0x5,
+ CPI_IN_DT_YUV_8,
+ CPI_IN_DT_RAW_8 = 0x9,
+ CPI_IN_DT_RAW_10,
+};
+
+enum {
+ PI_GATE_CLOCK_MODE,
+ PI_CCIR_MODE,
+};
+
+enum {
+ PI_V1,
+};
+
+struct imx_cpi_plat_data {
+ u32 version;
+ u32 if_ctrl_reg;
+ u32 interface_status;
+ u32 interface_ctrl_reg;
+ u32 interface_ctrl_reg1;
+};
+
+struct imx_cpi_device {
+ struct device *dev;
+ void __iomem *regs;
+ struct clk_bulk_data *clks;
+ int num_clks;
+
+ struct v4l2_subdev_1to1 sd_1to1;
+
+ const struct imx_cpi_plat_data *pdata;
+
+ u32 enabled_streams;
+ u8 mode;
+};
+
+struct imx_cpi_pix_format {
+ u32 code;
+ u32 output;
+ u32 data_type;
+ u8 width;
+};
+
+static const struct imx_cpi_pix_format imx_cpi_formats[] = {
+ /* YUV formats. */
+ {
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .output = MEDIA_BUS_FMT_UYVY8_1X16,
+ .data_type = CPI_IN_DT_UYVY_BT656_8,
+ .width = 16,
+ }, {
+ .code = MEDIA_BUS_FMT_YUYV8_2X8,
+ .output = MEDIA_BUS_FMT_YUYV8_1X16,
+ .data_type = CPI_IN_DT_YVYU_8,
+ .width = 16,
+ },
+};
+
+static const struct imx_cpi_plat_data imx8qxp_pdata = {
+ .version = PI_V1,
+ .if_ctrl_reg = 0x0,
+ .interface_status = 0x20,
+ .interface_ctrl_reg = 0x10,
+ .interface_ctrl_reg1 = 0x30,
+};
+
+static const struct imx_cpi_pix_format *find_imx_cpi_format(u32 code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(imx_cpi_formats); i++)
+ if (code == imx_cpi_formats[i].code)
+ return &imx_cpi_formats[i];
+
+ return NULL;
+}
+
+static void imx_cpi_sw_reset(struct imx_cpi_device *pcpidev)
+{
+ const struct imx_cpi_plat_data *pdata = pcpidev->pdata;
+ u32 val;
+
+ /* Softwaret Reset */
+ val = readl(pcpidev->regs + pdata->interface_ctrl_reg);
+ val |= CPI_CTRL_REG_SOFTRST;
+ writel(val, pcpidev->regs + pdata->interface_ctrl_reg);
+
+ fsleep(500);
+ val = readl(pcpidev->regs + pdata->interface_ctrl_reg);
+ val &= ~CPI_CTRL_REG_SOFTRST;
+ writel(val, pcpidev->regs + pdata->interface_ctrl_reg);
+}
+
+static void imx_cpi_hw_config(struct imx_cpi_device *pcpidev,
+ const struct imx_cpi_pix_format *pcpidev_fmt)
+{
+ const struct imx_cpi_plat_data *pdata = pcpidev->pdata;
+ u32 flags = pcpidev->sd_1to1.vep.bus.parallel.flags;
+ bool hsync_pol = flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH;
+ bool vsync_pol = flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH;
+ u32 val;
+
+ /* Software Reset */
+ imx_cpi_sw_reset(pcpidev);
+
+ /* Config PL Data Type */
+ val = IF_CTRL_REG_DATA_TYPE(DATA_TYPE_OUT_YUV444);
+ val |= IF_CTRL_REG_PL_ENABLE | IF_CTRL_REG_PL_VALID;
+ writel(val, pcpidev->regs + pdata->if_ctrl_reg);
+
+ /* Config CTRL REG */
+ val = CPI_CTRL_REG_HSYNC_FORCE_EN | CPI_CTRL_REG_VSYNC_FORCE_EN;
+
+ val |= CPI_CTRL_REG_DATA_TYPE_IN(pcpidev_fmt->data_type) |
+ FIELD_PREP(CPI_CTRL_REG_HSYNC_POL, hsync_pol) |
+ FIELD_PREP(CPI_CTRL_REG_VSYNC_POL, vsync_pol) |
+ FIELD_PREP(CPI_CTRL_REG_PIXEL_CLK_POL, 0) |
+ CPI_CTRL_REG_MASK_VSYNC_COUNTER(3) |
+ CPI_CTRL_REG_HSYNC_PULSE(2);
+
+ if (pcpidev_fmt->code == MEDIA_BUS_FMT_YUYV8_2X8 ||
+ pcpidev_fmt->code == MEDIA_BUS_FMT_UYVY8_2X8)
+ val |= CPI_CTRL_REG_UV_SWAP_EN;
+
+ if (pcpidev->mode == PI_GATE_CLOCK_MODE) {
+ val |= CPI_CTRL_REG_GCLK_MODE_EN;
+ } else if (pcpidev->mode == PI_CCIR_MODE) {
+ val |= (CPI_CTRL_REG_CCIR_EN |
+ CPI_CTRL_REG_CCIR_VSYNC_RESET_EN |
+ CPI_CTRL_REG_CCIR_EXT_VSYNC_EN |
+ CPI_CTRL_REG_CCIR_ECC_ERR_CORRECT_EN);
+ }
+
+ writel(val, pcpidev->regs + pdata->interface_ctrl_reg);
+}
+
+static void imx_cpi_config_ctrl_reg1(struct imx_cpi_device *pcpidev,
+ const struct v4l2_mbus_framefmt *format)
+{
+ const struct imx_cpi_plat_data *pdata = pcpidev->pdata;
+ u32 pixel_width;
+ u32 vsync_pulse;
+ u32 val;
+
+ pixel_width = format->width - 1;
+ vsync_pulse = format->width << 1;
+
+ switch (pcpidev->pdata->version) {
+ case PI_V1:
+ val = CPI_CTRL_REG1_PIXEL_WIDTH(pixel_width) |
+ CPI_CTRL_REG1_VSYNC_PULSE(vsync_pulse);
+ break;
+ default:
+ val = 0; /* Never happen */
+ }
+
+ val = CPI_CTRL_REG1_PIXEL_WIDTH(pixel_width) |
+ CPI_CTRL_REG1_VSYNC_PULSE(vsync_pulse);
+ writel(val, pcpidev->regs + pdata->interface_ctrl_reg1);
+}
+
+static void imx_cpi_enable(struct imx_cpi_device *pcpidev)
+{
+ const struct imx_cpi_plat_data *pdata = pcpidev->pdata;
+ u32 val;
+
+ /* Enable CPI */
+ val = readl(pcpidev->regs + pdata->interface_ctrl_reg);
+ val |= CPI_CTRL_REG_CPI_EN;
+ writel(val, pcpidev->regs + pdata->interface_ctrl_reg);
+
+ /* Disable SYNC Force */
+ val = readl(pcpidev->regs + pdata->interface_ctrl_reg);
+ val &= ~(CPI_CTRL_REG_HSYNC_FORCE_EN | CPI_CTRL_REG_VSYNC_FORCE_EN);
+ writel(val, pcpidev->regs + pdata->interface_ctrl_reg);
+}
+
+static void imx_cpi_disable(struct imx_cpi_device *pcpidev)
+{
+ const struct imx_cpi_plat_data *pdata = pcpidev->pdata;
+ u32 val;
+
+ /* Enable Sync Force */
+ val = readl(pcpidev->regs + pdata->interface_ctrl_reg);
+ val |= CPI_CTRL_REG_HSYNC_FORCE_EN | CPI_CTRL_REG_VSYNC_FORCE_EN;
+ writel(val, pcpidev->regs + pdata->interface_ctrl_reg);
+
+ /* Disable CPI */
+ val = readl(pcpidev->regs + pdata->interface_ctrl_reg);
+ val &= ~CPI_CTRL_REG_CPI_EN;
+ writel(val, pcpidev->regs + pdata->interface_ctrl_reg);
+
+ /* Disable Pixel Link */
+ val = readl(pcpidev->regs + pdata->if_ctrl_reg);
+ val &= ~(IF_CTRL_REG_PL_VALID | IF_CTRL_REG_PL_ENABLE);
+ writel(val, pcpidev->regs + pdata->if_ctrl_reg);
+}
+
+static struct imx_cpi_device *sd_to_imx_cpi_device(struct v4l2_subdev *sdev)
+{
+ return container_of(sdev, struct imx_cpi_device, sd_1to1.sd);
+}
+
+static const struct media_entity_operations imx_cpi_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+ .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1,
+};
+
+static int imx_cpi_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct imx_cpi_pix_format const *pcpidev_fmt;
+ struct v4l2_mbus_framefmt *fmt;
+
+ /*
+ * The Parallel cpi can't transcode in any way, the source format
+ * can't be modified.
+ */
+ if (sdformat->pad == V4L2_SUBDEV_1TO1_PADS_SOURCE)
+ return v4l2_subdev_get_fmt(sd, sd_state, sdformat);
+
+ pcpidev_fmt = find_imx_cpi_format(sdformat->format.code);
+ if (!pcpidev_fmt)
+ pcpidev_fmt = &imx_cpi_formats[0];
+
+ fmt = v4l2_subdev_state_get_format(sd_state, sdformat->pad);
+
+ fmt->code = pcpidev_fmt->code;
+ fmt->width = sdformat->format.width;
+ fmt->height = sdformat->format.height;
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->colorspace = sdformat->format.colorspace;
+ fmt->quantization = sdformat->format.quantization;
+ fmt->xfer_func = sdformat->format.xfer_func;
+ fmt->ycbcr_enc = sdformat->format.ycbcr_enc;
+
+ sdformat->format = *fmt;
+
+ /* Propagate the format from sink to source. */
+ fmt = v4l2_subdev_state_get_format(sd_state, V4L2_SUBDEV_1TO1_PADS_SOURCE);
+ *fmt = sdformat->format;
+
+ /* The format on the source pad might change due to unpacking. */
+ fmt->code = pcpidev_fmt->output;
+
+ return 0;
+}
+
+static int imx_cpi_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_subdev_route routes[] = {
+ {
+ .sink_pad = V4L2_SUBDEV_1TO1_PADS_SINK,
+ .sink_stream = 0,
+ .source_pad = V4L2_SUBDEV_1TO1_PADS_SOURCE,
+ .source_stream = 0,
+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
+ },
+ };
+ struct v4l2_subdev_krouting routing = {
+ .len_routes = ARRAY_SIZE(routes),
+ .num_routes = ARRAY_SIZE(routes),
+ .routes = routes,
+ };
+ struct v4l2_mbus_framefmt *fmt;
+
+ fmt = v4l2_subdev_state_get_format(state, 0);
+
+ fmt->code = imx_cpi_formats[0].code;
+ fmt->width = 1920;
+ fmt->height = 1080;
+
+ fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
+ fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
+ fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(false,
+ fmt->colorspace,
+ fmt->ycbcr_enc);
+
+ return v4l2_subdev_set_routing_with_fmt(sd, state, &routing, fmt);
+}
+
+static int imx_cpi_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct imx_cpi_device *pcpidev = sd_to_imx_cpi_device(sd);
+ struct media_pad *sink_pad, *remote_pad;
+ struct device *dev = pcpidev->dev;
+ struct v4l2_subdev *remote_sd;
+ u64 mask;
+ int ret;
+
+ sink_pad = &sd->entity.pads[V4L2_SUBDEV_1TO1_PADS_SINK];
+ remote_pad = media_pad_remote_pad_first(sink_pad);
+ remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
+
+ mask = v4l2_subdev_state_xlate_streams(state, V4L2_SUBDEV_1TO1_PADS_SINK,
+ V4L2_SUBDEV_1TO1_PADS_SOURCE,
+ &streams_mask);
+
+ ret = v4l2_subdev_disable_streams(remote_sd, remote_pad->index, mask);
+ if (ret)
+ dev_err(dev, "failed to disable streams on remote subdev: %d\n", ret);
+
+ pcpidev->enabled_streams &= ~streams_mask;
+
+ if (!pcpidev->enabled_streams) {
+ imx_cpi_disable(pcpidev);
+ pm_runtime_put_autosuspend(dev);
+ }
+
+ return 0;
+}
+
+static int imx_cpi_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct imx_cpi_device *pcpidev = sd_to_imx_cpi_device(sd);
+ const struct imx_cpi_pix_format *pcpidev_fmt;
+ const struct v4l2_mbus_framefmt *format;
+ struct media_pad *sink_pad, *remote_pad;
+ struct device *dev = pcpidev->dev;
+ struct v4l2_subdev *remote_sd;
+ u64 mask;
+ int ret;
+
+ sink_pad = &sd->entity.pads[V4L2_SUBDEV_1TO1_PADS_SINK];
+ remote_pad = media_pad_remote_pad_first(sink_pad);
+ remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
+
+ mask = v4l2_subdev_state_xlate_streams(state, V4L2_SUBDEV_1TO1_PADS_SINK,
+ V4L2_SUBDEV_1TO1_PADS_SOURCE,
+ &streams_mask);
+
+ format = v4l2_subdev_state_get_format(state, V4L2_SUBDEV_1TO1_PADS_SINK);
+ pcpidev_fmt = find_imx_cpi_format(format->code);
+
+ if (!pcpidev->enabled_streams) {
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ imx_cpi_hw_config(pcpidev, pcpidev_fmt);
+ imx_cpi_config_ctrl_reg1(pcpidev, format);
+ imx_cpi_enable(pcpidev);
+ }
+
+ ret = v4l2_subdev_enable_streams(remote_sd, remote_pad->index, mask);
+ if (ret)
+ goto err_cpi_stop;
+
+ pcpidev->enabled_streams |= streams_mask;
+
+ return 0;
+
+err_cpi_stop:
+ /* Stop CSI hardware if no streams are enabled */
+ if (!pcpidev->enabled_streams)
+ imx_cpi_disable(pcpidev);
+
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+static int imx_cpi_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ /*
+ * The PARALLEL CPI can't transcode in any way, the source format
+ * is identical to the sink format.
+ */
+ if (code->pad == V4L2_SUBDEV_1TO1_PADS_SOURCE) {
+ struct v4l2_mbus_framefmt *fmt;
+
+ if (code->index > 0)
+ return -EINVAL;
+
+ fmt = v4l2_subdev_state_get_format(sd_state, code->pad);
+ code->code = fmt->code;
+ return 0;
+ }
+
+ if (code->pad != V4L2_SUBDEV_1TO1_PADS_SINK)
+ return -EINVAL;
+
+ if (code->index >= ARRAY_SIZE(imx_cpi_formats))
+ return -EINVAL;
+
+ code->code = imx_cpi_formats[code->index].code;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_video_ops imx_cpi_video_ops = {
+ .s_stream = v4l2_subdev_s_stream_helper,
+};
+
+static const struct v4l2_subdev_pad_ops imx_cpi_pad_ops = {
+ .enum_mbus_code = imx_cpi_enum_mbus_code,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = imx_cpi_set_fmt,
+ .get_frame_desc = v4l2_subdev_get_frame_desc_passthrough,
+ .enable_streams = imx_cpi_enable_streams,
+ .disable_streams = imx_cpi_disable_streams,
+};
+
+static const struct v4l2_subdev_ops imx_cpi_subdev_ops = {
+ .pad = &imx_cpi_pad_ops,
+ .video = &imx_cpi_video_ops,
+};
+
+static const struct v4l2_subdev_internal_ops imx_cpi_internal_ops = {
+ .init_state = imx_cpi_init_state,
+};
+
+/* ----------------------------------------------------------------------
+ * Suspend/resume
+ */
+
+static int imx_cpi_runtime_suspend(struct device *dev)
+{
+ struct imx_cpi_device *pcpidev = dev_get_drvdata(dev);
+
+ clk_bulk_disable_unprepare(pcpidev->num_clks, pcpidev->clks);
+
+ return 0;
+}
+
+static int imx_cpi_runtime_resume(struct device *dev)
+{
+ struct imx_cpi_device *pcpidev = dev_get_drvdata(dev);
+
+ return clk_bulk_prepare_enable(pcpidev->num_clks, pcpidev->clks);
+}
+
+static const struct dev_pm_ops imx_cpi_pm_ops = {
+ RUNTIME_PM_OPS(imx_cpi_runtime_suspend, imx_cpi_runtime_resume, NULL)
+};
+
+static void imx_cpi_remove(struct platform_device *pdev)
+{
+ struct imx_cpi_device *pcpidev = platform_get_drvdata(pdev);
+
+ media_async_subdev_1to1_cleanup(&pcpidev->sd_1to1);
+}
+
+static int imx_cpi_probe(struct platform_device *pdev)
+{
+ struct imx_cpi_device *pcpidev;
+ struct device *dev = &pdev->dev;
+ struct v4l2_subdev *sd;
+ int ret;
+
+ pcpidev = devm_kzalloc(dev, sizeof(*pcpidev), GFP_KERNEL);
+ if (!pcpidev)
+ return -ENOMEM;
+
+ pcpidev->dev = dev;
+ platform_set_drvdata(pdev, pcpidev);
+
+ pcpidev->pdata = of_device_get_match_data(dev);
+ pcpidev->mode = PI_GATE_CLOCK_MODE;
+
+ pcpidev->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(pcpidev->regs))
+ return dev_err_probe(dev, PTR_ERR(pcpidev->regs),
+ "Failed to get regs\n");
+
+ pcpidev->num_clks = devm_clk_bulk_get_all(dev, &pcpidev->clks);
+ if (pcpidev->num_clks < 0)
+ return pcpidev->num_clks;
+
+ sd = &pcpidev->sd_1to1.sd;
+
+ v4l2_subdev_init(sd, &imx_cpi_subdev_ops);
+
+ sd->internal_ops = &imx_cpi_internal_ops;
+ snprintf(sd->name, sizeof(sd->name), "parallel-%s",
+ dev_name(pcpidev->dev));
+
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+ sd->entity.ops = &imx_cpi_entity_ops;
+
+ sd->dev = pcpidev->dev;
+ pcpidev->sd_1to1.remote_bustype_cap_mask = BIT(V4L2_MBUS_PARALLEL);
+
+ pm_runtime_use_autosuspend(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
+ return media_async_register_subdev_1to1(&pcpidev->sd_1to1);
+}
+
+static const struct of_device_id imx_cpi_of_match[] = {
+ { .compatible = "fsl,imx8qxp-pcif", .data = &imx8qxp_pdata },
+ { },
+};
+
+MODULE_DEVICE_TABLE(of, imx_cpi_of_match);
+
+static struct platform_driver imx_cpi_driver = {
+ .probe = imx_cpi_probe,
+ .remove = imx_cpi_remove,
+ .driver = {
+ .of_match_table = imx_cpi_of_match,
+ .name = "imx-parallel-cpi",
+ .pm = pm_ptr(&imx_cpi_pm_ops),
+ },
+};
+
+module_platform_driver(imx_cpi_driver);
+
+MODULE_DESCRIPTION("i.MX9 Parallel CPI receiver driver");
+MODULE_LICENSE("GPL");
--
2.43.0
^ permalink raw reply related
* [PATCH v5 5/8] dt-bindings: media: add i.MX parallel CPI support
From: Frank.Li @ 2026-06-17 19:50 UTC (permalink / raw)
To: Sakari Ailus, Mauro Carvalho Chehab, Michael Riesch,
Laurent Pinchart, Frank Li, Martin Kepplinger-Novakovic,
Rui Miguel Silva, Purism Kernel Team, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam
Cc: linux-media, linux-kernel, imx, devicetree, linux-arm-kernel,
Alice Yuan, Krzysztof Kozlowski
In-Reply-To: <20260617-imx8qxp_pcam-v5-0-7fa6c8e7fba7@nxp.com>
From: Alice Yuan <alice.yuan@nxp.com>
Document the binding for parallel CPI controller found in i.MX8QXP, i.MX93
and i.MX91 SoCs.
Signed-off-by: Alice Yuan <alice.yuan@nxp.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
Chagnes in v4
- add Laurent Pinchart's review by tag
- fix $ref: /schemas/graph.yaml#/$defs/port-base, original is
$ref: /schemas/graph.yaml#/properties/port-base
Change in v3:
- use enum at compatible string
- add ref to video-interfaces.yaml#
- use cpi as node name in examples.
- replace csi (Camera Serial Interface) with CPI (Camera Parallel Interface)
in commit message.
Change in v2:
- use pcif surfix as Laurent Pinchart's suggest.
- put power-domains into required list
---
.../devicetree/bindings/media/fsl,imx93-pcif.yaml | 126 +++++++++++++++++++++
MAINTAINERS | 1 +
2 files changed, 127 insertions(+)
diff --git a/Documentation/devicetree/bindings/media/fsl,imx93-pcif.yaml b/Documentation/devicetree/bindings/media/fsl,imx93-pcif.yaml
new file mode 100644
index 0000000000000..9dd0331f6ef75
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/fsl,imx93-pcif.yaml
@@ -0,0 +1,126 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/fsl,imx93-pcif.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: i.MX8/9 Parallel Camera Interface
+
+maintainers:
+ - Frank Li <Frank.Li@nxp.com>
+
+description: |
+ This is device node for the Parallel Camera Interface which enables the
+ chip to connect directly to external Parallel CMOS image sensors.
+ Supports up to 80MHz input clock from sensor.
+ Supports the following input data formats
+ - 8-bit/10-bit Camera Sensor Interface (CSI)
+ - 8-bit data port for RGB, YCbCr, and YUV data input
+ - 8-bit/10-bit data ports for Bayer data input
+ Parallel Camera Interface is hooked to the Imaging subsystem via the
+ Pixel Link.
+
+properties:
+ compatible:
+ oneOf:
+ - enum:
+ - fsl,imx8qxp-pcif
+ - fsl,imx93-pcif
+ - items:
+ - enum:
+ - fsl,imx91-pcif
+ - const: fsl,imx93-pcif
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ maxItems: 2
+
+ clock-names:
+ items:
+ - const: pixel
+ - const: ipg
+
+ power-domains:
+ maxItems: 1
+
+ ports:
+ $ref: /schemas/graph.yaml#/properties/ports
+
+ properties:
+ port@0:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ unevaluatedProperties: false
+ description: Input port node.
+
+ properties:
+ endpoint:
+ $ref: video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ properties:
+ bus-type:
+ const: 5
+
+ port@1:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ unevaluatedProperties: false
+ description: Output port node.
+
+ properties:
+ endpoint:
+ $ref: video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ properties:
+ bus-type:
+ const: 5
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - power-domains
+ - ports
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/imx93-clock.h>
+ #include <dt-bindings/power/fsl,imx93-power.h>
+
+ cpi@4ac10070 {
+ compatible = "fsl,imx93-pcif";
+ reg = <0x4ac10070 0x10>;
+ clocks = <&clk IMX93_CLK_MIPI_CSI_GATE>,
+ <&clk IMX93_CLK_MEDIA_APB>;
+ clock-names = "pixel", "ipg";
+ assigned-clocks = <&clk IMX93_CLK_CAM_PIX>;
+ assigned-clock-parents = <&clk IMX93_CLK_VIDEO_PLL>;
+ assigned-clock-rates = <140000000>;
+ power-domains = <&media_blk_ctrl IMX93_MEDIABLK_PD_MIPI_CSI>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ endpoint {
+ remote-endpoint = <&mt9m114_ep>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+ endpoint {
+ remote-endpoint = <&isi_in>;
+ };
+ };
+ };
+ };
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index 825e7cd2d6739..deeec900ad071 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -16258,6 +16258,7 @@ L: linux-media@vger.kernel.org
S: Maintained
T: git git://linuxtv.org/media.git
F: Documentation/admin-guide/media/imx7.rst
+F: Documentation/devicetree/bindings/media/fsl,imx93-pcif.yaml
F: Documentation/devicetree/bindings/media/nxp,imx-mipi-csi2.yaml
F: Documentation/devicetree/bindings/media/nxp,imx7-csi.yaml
F: Documentation/devicetree/bindings/media/nxp,imx8mq-mipi-csi2.yaml
--
2.43.0
^ permalink raw reply related
* [PATCH v5 4/8] media: synopsys: Use V4L2 1-to-1 subdev helpers
From: Frank.Li @ 2026-06-17 19:50 UTC (permalink / raw)
To: Sakari Ailus, Mauro Carvalho Chehab, Michael Riesch,
Laurent Pinchart, Frank Li, Martin Kepplinger-Novakovic,
Rui Miguel Silva, Purism Kernel Team, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam
Cc: linux-media, linux-kernel, imx, devicetree, linux-arm-kernel
In-Reply-To: <20260617-imx8qxp_pcam-v5-0-7fa6c8e7fba7@nxp.com>
From: Frank Li <Frank.Li@nxp.com>
Use the V4L2 1-to-1 subdev infrastructure to simplify the driver.
Replace the local subdev registration and media pad setup code with
media_async_register_subdev_1to1() and struct v4l2_subdev_1to1. Reduce
boilerplate code and aligns the driver with the common pattern used by
simple subdevices that have a single sink and a single source pad.
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
change in v5
new patch
previous method:
https://lore.kernel.org/imx/20260226-v4l2_init_register-v2-2-902d7140f9fa@nxp.com/
---
drivers/media/platform/synopsys/Kconfig | 1 +
drivers/media/platform/synopsys/dw-mipi-csi2rx.c | 172 ++++-------------------
2 files changed, 28 insertions(+), 145 deletions(-)
diff --git a/drivers/media/platform/synopsys/Kconfig b/drivers/media/platform/synopsys/Kconfig
index b109de2c8111c..8d7aabf93af34 100644
--- a/drivers/media/platform/synopsys/Kconfig
+++ b/drivers/media/platform/synopsys/Kconfig
@@ -10,6 +10,7 @@ config VIDEO_DW_MIPI_CSI2RX
depends on PM && COMMON_CLK
select GENERIC_PHY_MIPI_DPHY
select MEDIA_CONTROLLER
+ select V4L2_1TO1
select V4L2_FWNODE
select VIDEO_V4L2_SUBDEV_API
help
diff --git a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
index f51367409ff46..b70e3783adcd3 100644
--- a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
+++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
@@ -22,6 +22,7 @@
#include <media/mipi-csi2.h>
#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device-1to1.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-mc.h>
#include <media/v4l2-subdev.h>
@@ -78,12 +79,6 @@ enum dw_mipi_csi2rx_regs_index {
DW_MIPI_CSI2RX_MAX,
};
-enum {
- DW_MIPI_CSI2RX_PAD_SINK,
- DW_MIPI_CSI2RX_PAD_SRC,
- DW_MIPI_CSI2RX_PAD_MAX,
-};
-
struct dw_mipi_csi2rx_device;
struct dw_mipi_csi2rx_drvdata {
@@ -112,12 +107,8 @@ struct dw_mipi_csi2rx_device {
const struct dw_mipi_csi2rx_format *formats;
unsigned int formats_num;
- struct media_pad pads[DW_MIPI_CSI2RX_PAD_MAX];
- struct v4l2_async_notifier notifier;
- struct v4l2_subdev sd;
+ struct v4l2_subdev_1to1 sd_1to1;
- enum v4l2_mbus_type bus_type;
- u32 lanes_num;
u64 enabled_streams;
const struct dw_mipi_csi2rx_drvdata *drvdata;
@@ -294,7 +285,7 @@ static const struct dw_mipi_csi2rx_format formats[] = {
static inline struct dw_mipi_csi2rx_device *to_csi2(struct v4l2_subdev *sd)
{
- return container_of(sd, struct dw_mipi_csi2rx_device, sd);
+ return container_of(sd, struct dw_mipi_csi2rx_device, sd_1to1.sd);
}
static bool dw_mipi_csi2rx_has_reg(struct dw_mipi_csi2rx_device *csi2,
@@ -360,9 +351,9 @@ dw_mipi_csi2rx_find_format(struct dw_mipi_csi2rx_device *csi2, u32 mbus_code)
static int dw_mipi_csi2rx_start(struct dw_mipi_csi2rx_device *csi2)
{
+ u32 lanes = csi2->sd_1to1.vep.bus.mipi_csi2.num_data_lanes;
struct media_pad *source_pad;
union phy_configure_opts opts;
- u32 lanes = csi2->lanes_num;
u32 control = 0;
s64 link_freq;
int ret;
@@ -371,7 +362,7 @@ static int dw_mipi_csi2rx_start(struct dw_mipi_csi2rx_device *csi2)
return -EINVAL;
source_pad = media_pad_remote_pad_unique(
- &csi2->pads[DW_MIPI_CSI2RX_PAD_SINK]);
+ &csi2->sd_1to1.pads[V4L2_SUBDEV_1TO1_PADS_SINK]);
if (IS_ERR(source_pad))
return PTR_ERR(source_pad);
@@ -380,7 +371,7 @@ static int dw_mipi_csi2rx_start(struct dw_mipi_csi2rx_device *csi2)
if (link_freq < 0)
return link_freq;
- switch (csi2->bus_type) {
+ switch (csi2->sd_1to1.vep.bus_type) {
case V4L2_MBUS_CSI2_DPHY:
ret = phy_mipi_dphy_get_default_config_for_hsclk(link_freq * 2,
lanes, &opts.mipi_dphy);
@@ -458,16 +449,16 @@ dw_mipi_csi2rx_enum_mbus_code(struct v4l2_subdev *sd,
struct dw_mipi_csi2rx_device *csi2 = to_csi2(sd);
switch (code->pad) {
- case DW_MIPI_CSI2RX_PAD_SRC:
+ case V4L2_SUBDEV_1TO1_PADS_SOURCE:
if (code->index)
return -EINVAL;
code->code =
v4l2_subdev_state_get_format(sd_state,
- DW_MIPI_CSI2RX_PAD_SINK)->code;
+ V4L2_SUBDEV_1TO1_PADS_SINK)->code;
return 0;
- case DW_MIPI_CSI2RX_PAD_SINK:
+ case V4L2_SUBDEV_1TO1_PADS_SINK:
if (code->index >= csi2->formats_num)
return -EINVAL;
@@ -487,7 +478,7 @@ static int dw_mipi_csi2rx_set_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *sink, *src;
/* the format on the source pad always matches the sink pad */
- if (format->pad == DW_MIPI_CSI2RX_PAD_SRC)
+ if (format->pad == V4L2_SUBDEV_1TO1_PADS_SOURCE)
return v4l2_subdev_get_fmt(sd, state, format);
sink = v4l2_subdev_state_get_format(state, format->pad, format->stream);
@@ -549,12 +540,12 @@ static int dw_mipi_csi2rx_enable_streams(struct v4l2_subdev *sd,
u64 mask;
int ret;
- sink_pad = &sd->entity.pads[DW_MIPI_CSI2RX_PAD_SINK];
+ sink_pad = &sd->entity.pads[V4L2_SUBDEV_1TO1_PADS_SINK];
remote_pad = media_pad_remote_pad_first(sink_pad);
remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
- mask = v4l2_subdev_state_xlate_streams(state, DW_MIPI_CSI2RX_PAD_SINK,
- DW_MIPI_CSI2RX_PAD_SRC,
+ mask = v4l2_subdev_state_xlate_streams(state, V4L2_SUBDEV_1TO1_PADS_SINK,
+ V4L2_SUBDEV_1TO1_PADS_SOURCE,
&streams_mask);
if (!csi2->enabled_streams) {
@@ -608,12 +599,12 @@ static int dw_mipi_csi2rx_disable_streams(struct v4l2_subdev *sd,
u64 mask;
int ret;
- sink_pad = &sd->entity.pads[DW_MIPI_CSI2RX_PAD_SINK];
+ sink_pad = &sd->entity.pads[V4L2_SUBDEV_1TO1_PADS_SINK];
remote_pad = media_pad_remote_pad_first(sink_pad);
remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
- mask = v4l2_subdev_state_xlate_streams(state, DW_MIPI_CSI2RX_PAD_SINK,
- DW_MIPI_CSI2RX_PAD_SRC,
+ mask = v4l2_subdev_state_xlate_streams(state, V4L2_SUBDEV_1TO1_PADS_SINK,
+ V4L2_SUBDEV_1TO1_PADS_SOURCE,
&streams_mask);
ret = v4l2_subdev_disable_streams(remote_sd, remote_pad->index, mask);
@@ -649,9 +640,9 @@ static int dw_mipi_csi2rx_init_state(struct v4l2_subdev *sd,
{
struct v4l2_subdev_route routes[] = {
{
- .sink_pad = DW_MIPI_CSI2RX_PAD_SINK,
+ .sink_pad = V4L2_SUBDEV_1TO1_PADS_SINK,
.sink_stream = 0,
- .source_pad = DW_MIPI_CSI2RX_PAD_SRC,
+ .source_pad = V4L2_SUBDEV_1TO1_PADS_SOURCE,
.source_stream = 0,
.flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
},
@@ -670,91 +661,11 @@ static const struct v4l2_subdev_internal_ops dw_mipi_csi2rx_internal_ops = {
.init_state = dw_mipi_csi2rx_init_state,
};
-static int dw_mipi_csi2rx_notifier_bound(struct v4l2_async_notifier *notifier,
- struct v4l2_subdev *sd,
- struct v4l2_async_connection *asd)
-{
- struct dw_mipi_csi2rx_device *csi2 =
- container_of(notifier, struct dw_mipi_csi2rx_device, notifier);
- struct media_pad *sink_pad = &csi2->pads[DW_MIPI_CSI2RX_PAD_SINK];
- int ret;
-
- ret = v4l2_create_fwnode_links_to_pad(sd, sink_pad,
- MEDIA_LNK_FL_ENABLED);
- if (ret) {
- dev_err(csi2->dev, "failed to link source pad of %s\n",
- sd->name);
- return ret;
- }
-
- return 0;
-}
-
-static const struct v4l2_async_notifier_operations dw_mipi_csi2rx_notifier_ops = {
- .bound = dw_mipi_csi2rx_notifier_bound,
-};
-
-static int dw_mipi_csi2rx_register_notifier(struct dw_mipi_csi2rx_device *csi2)
-{
- struct v4l2_async_connection *asd;
- struct v4l2_async_notifier *ntf = &csi2->notifier;
- struct v4l2_fwnode_endpoint vep;
- struct v4l2_subdev *sd = &csi2->sd;
- struct device *dev = csi2->dev;
- int ret;
-
- struct fwnode_handle *ep __free(fwnode_handle) =
- fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0);
- if (!ep)
- return dev_err_probe(dev, -ENODEV, "failed to get endpoint\n");
-
- vep.bus_type = V4L2_MBUS_UNKNOWN;
- ret = v4l2_fwnode_endpoint_parse(ep, &vep);
- if (ret)
- return dev_err_probe(dev, ret, "failed to parse endpoint\n");
-
- if (vep.bus_type != V4L2_MBUS_CSI2_DPHY &&
- vep.bus_type != V4L2_MBUS_CSI2_CPHY)
- return dev_err_probe(dev, -EINVAL,
- "invalid bus type of endpoint\n");
-
- csi2->bus_type = vep.bus_type;
- csi2->lanes_num = vep.bus.mipi_csi2.num_data_lanes;
-
- v4l2_async_subdev_nf_init(ntf, sd);
- ntf->ops = &dw_mipi_csi2rx_notifier_ops;
-
- asd = v4l2_async_nf_add_fwnode_remote(ntf, ep,
- struct v4l2_async_connection);
- if (IS_ERR(asd)) {
- ret = PTR_ERR(asd);
- goto err_nf_cleanup;
- }
-
- ret = v4l2_async_nf_register(ntf);
- if (ret) {
- ret = dev_err_probe(dev, ret, "failed to register notifier\n");
- goto err_nf_cleanup;
- }
-
- return 0;
-
-err_nf_cleanup:
- v4l2_async_nf_cleanup(ntf);
-
- return ret;
-}
-
static int dw_mipi_csi2rx_register(struct dw_mipi_csi2rx_device *csi2)
{
- struct media_pad *pads = csi2->pads;
- struct v4l2_subdev *sd = &csi2->sd;
+ struct v4l2_subdev *sd = &csi2->sd_1to1.sd;
int ret;
- ret = dw_mipi_csi2rx_register_notifier(csi2);
- if (ret)
- goto err;
-
v4l2_subdev_init(sd, &dw_mipi_csi2rx_ops);
sd->dev = csi2->dev;
sd->entity.ops = &dw_mipi_csi2rx_media_ops;
@@ -764,45 +675,15 @@ static int dw_mipi_csi2rx_register(struct dw_mipi_csi2rx_device *csi2)
snprintf(sd->name, sizeof(sd->name), "dw-mipi-csi2rx %s",
dev_name(csi2->dev));
- pads[DW_MIPI_CSI2RX_PAD_SINK].flags = MEDIA_PAD_FL_SINK |
- MEDIA_PAD_FL_MUST_CONNECT;
- pads[DW_MIPI_CSI2RX_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
- ret = media_entity_pads_init(&sd->entity, DW_MIPI_CSI2RX_PAD_MAX, pads);
- if (ret)
- goto err_notifier_unregister;
+ csi2->sd_1to1.remote_bustype_cap_mask = BIT(V4L2_MBUS_CSI2_DPHY) |
+ BIT(V4L2_MBUS_CSI2_CPHY);
- ret = v4l2_subdev_init_finalize(sd);
+ ret = media_async_register_subdev_1to1(&csi2->sd_1to1);
if (ret)
- goto err_entity_cleanup;
-
- ret = v4l2_async_register_subdev(sd);
- if (ret) {
- dev_err(sd->dev, "failed to register CSI-2 subdev\n");
- goto err_subdev_cleanup;
- }
+ return dev_err_probe(sd->dev, ret,
+ "failed to register CSI-2 subdev\n");
return 0;
-
-err_subdev_cleanup:
- v4l2_subdev_cleanup(sd);
-err_entity_cleanup:
- media_entity_cleanup(&sd->entity);
-err_notifier_unregister:
- v4l2_async_nf_unregister(&csi2->notifier);
- v4l2_async_nf_cleanup(&csi2->notifier);
-err:
- return ret;
-}
-
-static void dw_mipi_csi2rx_unregister(struct dw_mipi_csi2rx_device *csi2)
-{
- struct v4l2_subdev *sd = &csi2->sd;
-
- v4l2_async_unregister_subdev(sd);
- v4l2_subdev_cleanup(sd);
- media_entity_cleanup(&sd->entity);
- v4l2_async_nf_unregister(&csi2->notifier);
- v4l2_async_nf_cleanup(&csi2->notifier);
}
static void imx93_csi2rx_dphy_assert_reset(struct dw_mipi_csi2rx_device *csi2)
@@ -879,12 +760,13 @@ static void imx93_csi2rx_dphy_ipi_enable(struct dw_mipi_csi2rx_device *csi2)
static int imx93_csi2rx_wait_for_phy_stopstate(struct dw_mipi_csi2rx_device *csi2)
{
+ u32 num_lanes = csi2->sd_1to1.vep.bus.mipi_csi2.num_data_lanes;
struct device *dev = csi2->dev;
u32 stopstate_mask;
u32 val;
int ret;
- stopstate_mask = DPHY_STOPSTATE_CLK_LANE | GENMASK(csi2->lanes_num - 1, 0);
+ stopstate_mask = DPHY_STOPSTATE_CLK_LANE | GENMASK(num_lanes - 1, 0);
ret = read_poll_timeout(dw_mipi_csi2rx_read, val,
(val & stopstate_mask) == stopstate_mask,
@@ -993,7 +875,7 @@ static void dw_mipi_csi2rx_remove(struct platform_device *pdev)
{
struct dw_mipi_csi2rx_device *csi2 = platform_get_drvdata(pdev);
- dw_mipi_csi2rx_unregister(csi2);
+ media_async_subdev_1to1_cleanup(&csi2->sd_1to1);
phy_exit(csi2->phy);
}
--
2.43.0
^ permalink raw reply related
* [PATCH v5 3/8] media: synopsys: Use v4l2_subdev_get_frame_desc_passthrough()
From: Frank.Li @ 2026-06-17 19:50 UTC (permalink / raw)
To: Sakari Ailus, Mauro Carvalho Chehab, Michael Riesch,
Laurent Pinchart, Frank Li, Martin Kepplinger-Novakovic,
Rui Miguel Silva, Purism Kernel Team, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam
Cc: linux-media, linux-kernel, imx, devicetree, linux-arm-kernel
In-Reply-To: <20260617-imx8qxp_pcam-v5-0-7fa6c8e7fba7@nxp.com>
From: Frank Li <Frank.Li@nxp.com>
Replace the local frame descriptor callback implementation with
v4l2_subdev_get_frame_desc_passthrough().
This helper provides the same functionality while avoiding duplicate
code and simplifying the driver implementation.
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
change in v5
- new patch
---
drivers/media/platform/synopsys/dw-mipi-csi2rx.c | 22 +---------------------
1 file changed, 1 insertion(+), 21 deletions(-)
diff --git a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
index 41e48365167e5..f51367409ff46 100644
--- a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
+++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
@@ -630,31 +630,11 @@ static int dw_mipi_csi2rx_disable_streams(struct v4l2_subdev *sd,
return ret;
}
-static int
-dw_mipi_csi2rx_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
- struct v4l2_mbus_frame_desc *fd)
-{
- struct dw_mipi_csi2rx_device *csi2 = to_csi2(sd);
- struct v4l2_subdev *remote_sd;
- struct media_pad *remote_pad;
-
- remote_pad = media_pad_remote_pad_unique(&csi2->pads[DW_MIPI_CSI2RX_PAD_SINK]);
- if (IS_ERR(remote_pad)) {
- dev_err(csi2->dev, "can't get remote source pad\n");
- return PTR_ERR(remote_pad);
- }
-
- remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
-
- return v4l2_subdev_call(remote_sd, pad, get_frame_desc,
- remote_pad->index, fd);
-}
-
static const struct v4l2_subdev_pad_ops dw_mipi_csi2rx_pad_ops = {
.enum_mbus_code = dw_mipi_csi2rx_enum_mbus_code,
.get_fmt = v4l2_subdev_get_fmt,
.set_fmt = dw_mipi_csi2rx_set_fmt,
- .get_frame_desc = dw_mipi_csi2rx_get_frame_desc,
+ .get_frame_desc = v4l2_subdev_get_frame_desc_passthrough,
.set_routing = dw_mipi_csi2rx_set_routing,
.enable_streams = dw_mipi_csi2rx_enable_streams,
.disable_streams = dw_mipi_csi2rx_disable_streams,
--
2.43.0
^ permalink raw reply related
* [PATCH v5 2/8] media: v4l2-fwnode: Add common helper library for 1-to-1 subdev registration
From: Frank.Li @ 2026-06-17 19:50 UTC (permalink / raw)
To: Sakari Ailus, Mauro Carvalho Chehab, Michael Riesch,
Laurent Pinchart, Frank Li, Martin Kepplinger-Novakovic,
Rui Miguel Silva, Purism Kernel Team, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam
Cc: linux-media, linux-kernel, imx, devicetree, linux-arm-kernel
In-Reply-To: <20260617-imx8qxp_pcam-v5-0-7fa6c8e7fba7@nxp.com>
From: Frank Li <Frank.Li@nxp.com>
Many V4L2 subdev drivers implement the same registration and media pad
setup logic for simple pipelines consisting of a single sink pad and a
single source pad. As a result, the same boilerplate code is duplicated
across multiple drivers.
Introduce a common helper library for 1-to-1 subdevs to encapsulate the
registration, media entity initialization, and cleanup paths. Drivers
can embed a struct v4l2_subdev_1to1 instance and use the provided helper
APIs instead of open-coding the setup sequence.
This reduces code duplication and simplifies the implementation of
simple bridge and converter drivers.
In 1TO1 subdev driver:
struct your_device {
v4l2_subdev_1to1 sd_1to1; // instead of v4l2_subdev sd;
...
}
...
your_device_probe()
{
v4l2_subdev_init(&sd_1to1->sd, &dw_mipi_csi2rx_ops);
...
media_async_register_subdev_1to1(sd_1to1);
}
...
your_device_remove()
{
media_async_subdev_1to1_cleanup();
}
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
change in v5
- new patch
---
drivers/media/v4l2-core/Kconfig | 3 +
drivers/media/v4l2-core/Makefile | 1 +
drivers/media/v4l2-core/v4l2-1to1.c | 117 ++++++++++++++++++++++++++++++++++++
include/media/v4l2-device-1to1.h | 72 ++++++++++++++++++++++
4 files changed, 193 insertions(+)
diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
index d50ccac9733cc..532375cae7947 100644
--- a/drivers/media/v4l2-core/Kconfig
+++ b/drivers/media/v4l2-core/Kconfig
@@ -74,6 +74,9 @@ config V4L2_FWNODE
config V4L2_ASYNC
tristate
+config V4L2_1TO1
+ tristate
+
config V4L2_CCI
tristate
diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
index 329f0eadce994..55bf0e6bf2e33 100644
--- a/drivers/media/v4l2-core/Makefile
+++ b/drivers/media/v4l2-core/Makefile
@@ -24,6 +24,7 @@ videodev-$(CONFIG_VIDEO_V4L2_I2C) += v4l2-i2c.o
# Please keep it alphabetically sorted by Kconfig name
# (e. g. LC_ALL=C sort Makefile)
+obj-$(CONFIG_V4L2_1TO1) += v4l2-1to1.o
obj-$(CONFIG_V4L2_ASYNC) += v4l2-async.o
obj-$(CONFIG_V4L2_CCI) += v4l2-cci.o
obj-$(CONFIG_V4L2_FLASH_LED_CLASS) += v4l2-flash-led-class.o
diff --git a/drivers/media/v4l2-core/v4l2-1to1.c b/drivers/media/v4l2-core/v4l2-1to1.c
new file mode 100644
index 0000000000000..9f23dccece704
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-1to1.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/property.h>
+
+#include <media/v4l2-async.h>
+#include <media/v4l2-device-1to1.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+
+static int v4l2_1to1_notifier_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_connection *asd)
+{
+ struct v4l2_subdev_1to1 *sd_1to1 = v4l2_sd_to_1to1_device(notifier->sd);
+ struct media_pad *sink_pad = &sd_1to1->pads[V4L2_SUBDEV_1TO1_PADS_SINK];
+ int ret;
+
+ ret = v4l2_create_fwnode_links_to_pad(sd, sink_pad, MEDIA_LNK_FL_ENABLED);
+ if (ret) {
+ dev_err(sd_1to1->sd.dev, "failed to link source pad of %s\n", sd->name);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_async_notifier_operations v4l2_1to1_notifier_ops = {
+ .bound = v4l2_1to1_notifier_bound,
+};
+
+static int
+v4l2_async_nf_parse_fwnode_1to1(struct device *dev, struct v4l2_async_notifier *notifier)
+{
+ struct v4l2_subdev *sd = notifier->sd;
+ struct v4l2_subdev_1to1 *sd_1to1 = v4l2_sd_to_1to1_device(sd);
+ struct v4l2_fwnode_endpoint *vep = &sd_1to1->vep;
+ struct v4l2_async_connection *asd;
+ int ret;
+
+ struct fwnode_handle *ep __free(fwnode_handle) =
+ fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0);
+ if (!ep)
+ return dev_err_probe(dev, -ENODEV, "failed to get endpoint\n");
+
+ ret = v4l2_fwnode_endpoint_parse(ep, vep);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to parse endpoint\n");
+
+ if (!(BIT(vep->bus_type) & sd_1to1->remote_bustype_cap_mask))
+ return dev_err_probe(dev, -EINVAL,
+ "invalid bus type %d of endpoint\n",
+ vep->bus_type);
+
+ notifier->ops = &v4l2_1to1_notifier_ops;
+
+ asd = v4l2_async_nf_add_fwnode_remote(notifier, ep,
+ struct v4l2_async_connection);
+ if (IS_ERR(asd))
+ return dev_err_probe(dev, PTR_ERR(asd),
+ "failed to add notifier\n");
+
+ return 0;
+}
+
+void media_async_subdev_1to1_cleanup(struct v4l2_subdev_1to1 *sd_1to1)
+{
+ struct v4l2_subdev *sd = &sd_1to1->sd;
+
+ v4l2_async_unregister_subdev(sd);
+ v4l2_subdev_cleanup(sd);
+ media_entity_cleanup(&sd->entity);
+ v4l2_async_nf_unregister(sd->subdev_notifier);
+ v4l2_async_nf_cleanup(sd->subdev_notifier);
+
+ kfree(sd->subdev_notifier);
+}
+EXPORT_SYMBOL_GPL(media_async_subdev_1to1_cleanup);
+
+int __media_async_register_subdev_1to1(struct v4l2_subdev_1to1 *sd_1to1, struct module *module)
+{
+ struct media_pad *pads = sd_1to1->pads;
+ int ret;
+
+ pads[V4L2_SUBDEV_1TO1_PADS_SINK].flags = MEDIA_PAD_FL_SINK |
+ MEDIA_PAD_FL_MUST_CONNECT;
+ pads[V4L2_SUBDEV_1TO1_PADS_SOURCE].flags = MEDIA_PAD_FL_SOURCE |
+ MEDIA_PAD_FL_MUST_CONNECT;
+
+ ret = media_entity_pads_init(&sd_1to1->sd.entity, V4L2_SUBDEV_1TO1_PADS_TOTAL, pads);
+ if (ret)
+ return ret;
+
+ ret = v4l2_subdev_init_finalize(&sd_1to1->sd);
+ if (ret)
+ goto err_entity_cleanup;
+
+ ret = __v4l2_async_register_subdev_fwnode(&sd_1to1->sd,
+ v4l2_async_nf_parse_fwnode_1to1,
+ module);
+ if (ret)
+ goto err_subdev_cleanup;
+
+ return 0;
+
+err_subdev_cleanup:
+ v4l2_subdev_cleanup(&sd_1to1->sd);
+err_entity_cleanup:
+ media_entity_cleanup(&sd_1to1->sd.entity);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(__media_async_register_subdev_1to1);
+
+MODULE_DESCRIPTION("V4L2 subdev 1to1 helper library");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Frank.Li@kernel.org");
diff --git a/include/media/v4l2-device-1to1.h b/include/media/v4l2-device-1to1.h
new file mode 100644
index 0000000000000..a1256767b4d4c
--- /dev/null
+++ b/include/media/v4l2-device-1to1.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef __V4L2_SUBDEV_1TO1__
+#define __V4L2_SUBDEV_1TO1__
+
+#include <media/media-entity.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+enum v4l2_subdev_1to1_pads {
+ V4L2_SUBDEV_1TO1_PADS_SINK,
+ V4L2_SUBDEV_1TO1_PADS_SOURCE,
+ V4L2_SUBDEV_1TO1_PADS_TOTAL,
+};
+
+/**
+ * struct v4l2_subdev_1to1 - 1to1 sub-device
+ *
+ * @sd: sub-device that registered the notifier, NULL otherwise
+ * @pads: media pads(the first one is sink, the second one is source)
+ * @vep: The V4L2 fwnode data structure for remote node.
+ * @remote_bustype_cap_mask: Bit mask for required remote node v4l2_mbus_type.
+ */
+struct v4l2_subdev_1to1 {
+ struct v4l2_subdev sd;
+ struct media_pad pads[V4L2_SUBDEV_1TO1_PADS_TOTAL];
+ struct v4l2_fwnode_endpoint vep;
+ /* bit masks for enum v4l2_mbus_type*/
+ u32 remote_bustype_cap_mask;
+};
+
+static inline struct v4l2_subdev_1to1 *
+v4l2_sd_to_1to1_device(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct v4l2_subdev_1to1, sd);
+}
+
+/**
+ * media_async_register_subdev_1to1 - registers a 1to1 sub-device to the
+ * asynchronous sub-device framework and
+ * parse set up common 1to1 related
+ * devices
+ *
+ * @sd_1to1: pointer to struct &v4l2_subdev_1to1
+ *
+ * This function is just like v4l2_async_register_subdev() with the exception
+ * that calling it will also parse firmware interfaces for remote references
+ * using v4l2_async_nf_parse_fwnode_sensor() and registers the
+ * async sub-devices.
+ *
+ * This function also init media_pads.
+ *
+ * The sub-device is similarly unregistered and cleanup by
+ * media_async_subdev_1to1_cleanup()
+ *
+ * While registered, the subdev module is marked as in-use.
+ *
+ * An error is returned if the module is no longer loaded on any attempts
+ * to register it.
+ */
+#define media_async_register_subdev_1to1(sd_1to1) \
+ __media_async_register_subdev_1to1(sd_1to1, THIS_MODULE)
+
+int __media_async_register_subdev_1to1(struct v4l2_subdev_1to1 *sd_1to1, struct module *module);
+
+/**
+ * media_async_subdev_1to1_cleanup - unregistered and cleanup subdev and media
+ * pads
+ * @sd_1to1: pointer to struct &v4l2_subdev_1to1
+ */
+void media_async_subdev_1to1_cleanup(struct v4l2_subdev_1to1 *sd_1to1);
+
+#endif
--
2.43.0
^ permalink raw reply related
* [PATCH v5 1/8] media: v4l2-fwnode: Extract common helper __v4l2_async_register_subdev_fwnode()
From: Frank.Li @ 2026-06-17 19:50 UTC (permalink / raw)
To: Sakari Ailus, Mauro Carvalho Chehab, Michael Riesch,
Laurent Pinchart, Frank Li, Martin Kepplinger-Novakovic,
Rui Miguel Silva, Purism Kernel Team, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam
Cc: linux-media, linux-kernel, imx, devicetree, linux-arm-kernel
In-Reply-To: <20260617-imx8qxp_pcam-v5-0-7fa6c8e7fba7@nxp.com>
From: Frank Li <Frank.Li@nxp.com>
Extract __v4l2_async_register_subdev_fwnode() from
__media_async_register_subdev_1to1() and make the notifier parsing
function a parameter.
This prepares for future support of v4l2_async_register_subdev_1to1() by
allowing different fwnode parsing implementations to reuse the common
registration logic.
No functional change intended.
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
change in v5:
- new patch
---
drivers/media/v4l2-core/v4l2-fwnode.c | 17 ++++++++++++++---
include/media/v4l2-async.h | 6 ++++++
2 files changed, 20 insertions(+), 3 deletions(-)
diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
index 62a3a452f7884..f32dbf5bc8344 100644
--- a/drivers/media/v4l2-core/v4l2-fwnode.c
+++ b/drivers/media/v4l2-core/v4l2-fwnode.c
@@ -1256,7 +1256,11 @@ v4l2_async_nf_parse_fwnode_sensor(struct device *dev,
return 0;
}
-int __v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd, struct module *module)
+int
+__v4l2_async_register_subdev_fwnode(struct v4l2_subdev *sd,
+ int (*parse_fwnode)(struct device *dev,
+ struct v4l2_async_notifier *notifier),
+ struct module *module)
{
struct v4l2_async_notifier *notifier;
int ret;
@@ -1274,7 +1278,7 @@ int __v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd, struct module *m
if (ret < 0)
goto out_cleanup;
- ret = v4l2_async_nf_parse_fwnode_sensor(sd->dev, notifier);
+ ret = parse_fwnode(sd->dev, notifier);
if (ret < 0)
goto out_cleanup;
@@ -1300,8 +1304,15 @@ int __v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd, struct module *m
return ret;
}
-EXPORT_SYMBOL_GPL(__v4l2_async_register_subdev_sensor);
+EXPORT_SYMBOL_GPL(__v4l2_async_register_subdev_fwnode);
+int
+__v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd, struct module *module)
+{
+ return __v4l2_async_register_subdev_fwnode(sd, v4l2_async_nf_parse_fwnode_sensor,
+ module);
+}
+EXPORT_SYMBOL_GPL(__v4l2_async_register_subdev_sensor);
MODULE_DESCRIPTION("V4L2 fwnode binding parsing library");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>");
diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h
index 54a2d9620ed5b..a9345fb921b43 100644
--- a/include/media/v4l2-async.h
+++ b/include/media/v4l2-async.h
@@ -338,6 +338,12 @@ int __v4l2_async_register_subdev(struct v4l2_subdev *sd, struct module *module);
int __must_check
__v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd, struct module *module);
+int __must_check
+__v4l2_async_register_subdev_fwnode(struct v4l2_subdev *sd,
+ int (*parse_fwnode)(struct device *dev,
+ struct v4l2_async_notifier *notifier),
+ struct module *module);
+
/**
* v4l2_async_unregister_subdev - unregisters a sub-device to the asynchronous
* subdevice framework
--
2.43.0
^ permalink raw reply related
* [PATCH v5 0/8] media: add new API simple 1to1 subdev register and add imx parallel camera support
From: Frank.Li @ 2026-06-17 19:50 UTC (permalink / raw)
To: Sakari Ailus, Mauro Carvalho Chehab, Michael Riesch,
Laurent Pinchart, Frank Li, Martin Kepplinger-Novakovic,
Rui Miguel Silva, Purism Kernel Team, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam
Cc: linux-media, linux-kernel, imx, devicetree, linux-arm-kernel,
Alice Yuan, Robert Chiras, Zhipeng Wang, Krzysztof Kozlowski
This patches base on previous' thread "media: imx8qxp: add parallel camera
support".
Add new API media_async_register_subdev_1to1() to simple 1to1 subdev
register.
Many V4L2 subdev drivers implement the same registration and media pad
setup logic for simple pipelines consisting of a single sink pad and a
single source pad. As a result, the same boilerplate code is duplicated
across multiple drivers.
Introduce a common helper library for 1-to-1 subdevs to encapsulate the
registration, media entity initialization, and cleanup paths. Drivers
can embed a struct v4l2_subdev_1to1 instance and use the provided helper
APIs instead of open-coding the setup sequence.
This reduces code duplication and simplifies the implementation of
simple bridge and converter drivers.
In 1TO1 subdev driver:
struct your_device {
v4l2_subdev_1to1 sd_1to1; // instead of v4l2_subdev sd;
...
}
...
your_device_probe()
{
v4l2_subdev_init(&sd_1to1->sd, &dw_mipi_csi2rx_ops);
...
return media_async_register_subdev_1to1(sd_1to1);
}
...
your_device_remove()
{
media_async_subdev_1to1_cleanup();
}
This API help reduce over line duplcated code in synopsys/dw-mipi-csi2rx.c.
And use this API at imx8's parallel CPI driver, which over 90% code now
hardware related.
And also benefit on going pix format patch
https://lore.kernel.org/imx/20260525-csi_formatter-v8-0-6b646231224b@oss.nxp.com/
It will also reduce missed media_entity_cleanup() problem at some error path
https://lore.kernel.org/linux-media/20260614202835.11977-15-birenpandya@gmail.com/
Previous do partial simpilfy at
https://lore.kernel.org/imx/aaisdJSsFE5-PLx1@lizhi-Precision-Tower-5810/
To: Sakari Ailus <sakari.ailus@linux.intel.com>
To: Mauro Carvalho Chehab <mchehab@kernel.org>
To: Michael Riesch <michael.riesch@collabora.com>
To: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
To: Frank Li <Frank.Li@nxp.com>
To: Martin Kepplinger-Novakovic <martink@posteo.de>
To: Rui Miguel Silva <rmfrfs@gmail.com>
To: Purism Kernel Team <kernel@puri.sm>
To: Rob Herring <robh@kernel.org>
To: Krzysztof Kozlowski <krzk+dt@kernel.org>
To: Conor Dooley <conor+dt@kernel.org>
To: Sascha Hauer <s.hauer@pengutronix.de>
To: Pengutronix Kernel Team <kernel@pengutronix.de>
To: Fabio Estevam <festevam@gmail.com>
Cc: linux-media@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: imx@lists.linux.dev
Cc: devicetree@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
Changes in v5:
- Add media_async_register_subdev_1to1() to simple code.
- Link to v4: https://lore.kernel.org/r/20250729-imx8qxp_pcam-v4-0-4dfca4ed2f87@nxp.com
Changes in v4:
- remove imx93 driver support since have not camera sensor module to do test now.
Add it later
- Add new patch
media: v4l2-common: Add helper function v4l_get_required_align_by_bpp()
- See each patche's change log for detail.
- Link to v3: https://lore.kernel.org/r/20250708-imx8qxp_pcam-v3-0-c8533e405df1@nxp.com
Changes in v3:
- replace CSI with CPI.
- detail change see each patch's change logs
- Link to v2: https://lore.kernel.org/r/20250703-imx8qxp_pcam-v2-0-188be85f06f1@nxp.com
Changes in v2:
- remove patch media: nxp: isi: add support for UYVY8_2X8 and YUYV8_2X8 bus codes
because pcif controller convert 2x8 to 1x16 to match isi's input
- rename comaptible string to fsl,imx8qxp-pcif
- See each patches's change log for detail
- Link to v1: https://lore.kernel.org/r/20250630-imx8qxp_pcam-v1-0-eccd38d99201@nxp.com
---
Alice Yuan (2):
dt-bindings: media: add i.MX parallel CPI support
media: nxp: add V4L2 subdev driver for camera parallel interface (CPI)
Frank Li (6):
media: v4l2-fwnode: Extract common helper __v4l2_async_register_subdev_fwnode()
media: v4l2-fwnode: Add common helper library for 1-to-1 subdev registration
media: synopsys: Use v4l2_subdev_get_frame_desc_passthrough()
media: synopsys: Use V4L2 1-to-1 subdev helpers
arm64: dts: imx8: add camera parallel interface (CPI) node
arm64: dts: imx8qxp-mek: add parallel ov5640 camera support
.../devicetree/bindings/media/fsl,imx93-pcif.yaml | 126 +++++
MAINTAINERS | 2 +
arch/arm64/boot/dts/freescale/Makefile | 3 +
arch/arm64/boot/dts/freescale/imx8-ss-img.dtsi | 13 +
.../boot/dts/freescale/imx8qxp-mek-ov5640-cpi.dtso | 83 +++
arch/arm64/boot/dts/freescale/imx8qxp-ss-img.dtsi | 27 +
drivers/media/platform/nxp/Kconfig | 12 +
drivers/media/platform/nxp/Makefile | 1 +
drivers/media/platform/nxp/imx-parallel-cpi.c | 614 +++++++++++++++++++++
drivers/media/platform/synopsys/Kconfig | 1 +
drivers/media/platform/synopsys/dw-mipi-csi2rx.c | 194 +------
drivers/media/v4l2-core/Kconfig | 3 +
drivers/media/v4l2-core/Makefile | 1 +
drivers/media/v4l2-core/v4l2-1to1.c | 117 ++++
drivers/media/v4l2-core/v4l2-fwnode.c | 17 +-
include/media/v4l2-async.h | 6 +
include/media/v4l2-device-1to1.h | 72 +++
17 files changed, 1123 insertions(+), 169 deletions(-)
---
base-commit: 7193e493653c9c91b4be159cd919924ec6ad2392
change-id: 20250626-imx8qxp_pcam-d851238343c3
Best regards,
--
Frank Li <Frank.Li@nxp.com>
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox