Linux Media Controller development
 help / color / mirror / Atom feed
* Re: [PATCH v2 1/4] device property: Introduce fwnode_graph_for_each_endpoint_scoped()
From: Andy Shevchenko @ 2026-06-25  6:33 UTC (permalink / raw)
  To: Frank.Li
  Cc: Daniel Scally, Heikki Krogerus, Sakari Ailus, Greg Kroah-Hartman,
	Rafael J. Wysocki, Danilo Krummrich, Mauro Carvalho Chehab,
	Dafna Hirschfeld, Laurent Pinchart, Heiko Stuebner,
	Bryan O'Donoghue, Vladimir Zapolskiy, Loic Poulain,
	driver-core, linux-acpi, linux-kernel, linux-media,
	linux-rockchip, linux-arm-kernel, linux-arm-msm, imx, Guoniu Zhou,
	Frank Li, Guoniu Zhou
In-Reply-To: <20260624-fw_scoped-v2-1-0a8db472af4a@nxp.com>

On Wed, Jun 24, 2026 at 01:00:09PM -0400, Frank.Li@oss.nxp.com wrote:

> Similar to recently propose for_each_child_of_node_scoped() this new
> version of the loop macro instantiates a new local struct fwnode_handle *
> that uses the __free(fwnode_handle) auto cleanup handling so that if a
> reference to a node is held on early exit from the loop the reference will
> be released. If the loop runs to completion, the child pointer will be NULL
> and no action will be taken.
> 
> The reason this is useful is that it removes the need for
> fwnode_handle_put() on early loop exits. If there is a need to retain the
> reference, then return_ptr(child) or no_free_ptr(child) may be used to
> safely disable the auto cleanup.

...

> +#define fwnode_graph_for_each_endpoint_scoped(fwnode, child)			\
> +	for (struct fwnode_handle *child __free(fwnode_handle) =		\
> +		fwnode_graph_get_next_endpoint(fwnode, NULL);		\

Now there is a misindentation of the \, id est an additional tab is missing.

> +	     child; child = fwnode_graph_get_next_endpoint(fwnode, child))

Collect more tags and send a v3 :-)

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply

* Re: [PATCH v2 0/4] media: add and use fwnode_graph_for_each_endpoint_scoped()
From: Andy Shevchenko @ 2026-06-25  6:31 UTC (permalink / raw)
  To: Frank Li
  Cc: Laurent Pinchart, Daniel Scally, Heikki Krogerus, Sakari Ailus,
	Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
	Mauro Carvalho Chehab, Dafna Hirschfeld, Heiko Stuebner,
	Bryan O'Donoghue, Vladimir Zapolskiy, Loic Poulain,
	driver-core, linux-acpi, linux-kernel, linux-media,
	linux-rockchip, linux-arm-kernel, linux-arm-msm, imx, Guoniu Zhou,
	Frank Li, Guoniu Zhou
In-Reply-To: <ajxCOE3avXXLlrfT@SMW015318>

On Wed, Jun 24, 2026 at 03:46:48PM -0500, Frank Li wrote:
> On Wed, Jun 24, 2026 at 11:02:37PM +0300, Laurent Pinchart wrote:
> > On Wed, Jun 24, 2026 at 02:35:14PM -0500, Frank Li wrote:
> > > On Wed, Jun 24, 2026 at 10:19:35PM +0300, Laurent Pinchart wrote:
> > > > On Wed, Jun 24, 2026 at 01:00:08PM -0400, Frank.Li@oss.nxp.com wrote:
> > > > > Add new helper macro fwnode_graph_for_each_endpoint_scoped() and use it
> > > > > simplify media code.
> > > > >
> > > > > Typical example should qualcomm's driver (camss.c), the v4l2_mc.c and
> > > > > rkisp1-dev.c only silience improvement.
> > > > >
> > > > > Anyways, *_for_each_*_scoped() already use widely and make code clean.
> > > > >
> > > > > Build test only.
> > > > >
> > > > > Sakari Ailus:
> > > > > 	when I try to improve the patch
> > > > > "Add common helper library for 1-to-1 subdev registration", I found need
> > > > > camss.c pattern, so I create this small improvement firstly.
> > > >
> > > > Those are nice cleanups, thank you.
> > > >
> > > > After applying this series, the only left users of the
> > > > fwnode_graph_for_each_endpoint() macro are in drivers/base/property.c.
> > >
> > > I already checked previously, two place use it.
> > >
> > > fwnode_graph_get_endpoint_count(), it will go though all endpoints, last
> > > ep is NULL, which totally equial to scoped() version.
> > >
> > > another one fwnode_graph_get_endpoint_by_id(), which return ep, expect
> > > caller to call put().
> > >
> > > if use scoped() version, need use no_free_ptr() at return, which make think
> > > a little bit complex.
> >
> > It would introduce a tiny bit of extra complexity there, but the
> > advantage (in my opinion) is that we'll be able to remove the less safe
> > fwnode_graph_for_each_endpoint() macro.
> >
> > Now one may argue that the risk of
> > fwnode_graph_for_each_endpoint_scoped() is returning the iterator
> > without using no_free_ptr(). I wonder if that would be easier to catch
> > in static analysis tools than the current pattern that leaks a reference
> > when exiting the loop early.
> 
> It's not big deal, if everyone prefer drop fwnode_graph_for_each_endpoint(),
> I can do it.

I slightly tend to the safest option (see below), but as a compromise I can
suggest to inline the fwnode_graph_for_each_endpoint() into that single user
that doesn't need a put. However, this may uglify the code and rise a question
of the consistency. So, consider that suggestion with grain of salt and apply
only if we have wider agreement with it.

> > > It'd better leave these as it.

TL;DR:
This is the safest option, of course. And as mentioned above I slightly
prefer this way. Another argument is that in some cases we might want to
have it in the future and since we have an existing user, let it live.

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply

* Re: [PATCH v6 2/9] dt-bindings: media: nxp: Add Wave6 video codec device
From: Krzysztof Kozlowski @ 2026-06-25  6:28 UTC (permalink / raw)
  To: Nas Chung
  Cc: Conor Dooley, mchehab@kernel.org, hverkuil@xs4all.nl,
	robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
	shawnguo@kernel.org, s.hauer@pengutronix.de,
	linux-media@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-imx@nxp.com,
	linux-arm-kernel@lists.infradead.org, jackson.lee, lafley.kim,
	marek.vasut@mailbox.org
In-Reply-To: <SL2P216MB2441BB9DC91CCBE494F2B45BFBEC2@SL2P216MB2441.KORP216.PROD.OUTLOOK.COM>

On Thu, Jun 25, 2026 at 01:43:33AM +0000, Nas Chung wrote:
> >> +  sram:
> >> +    $ref: /schemas/types.yaml#/definitions/phandle
> >> +    description:
> >> +      phandle to the SRAM node used to store reference data, reducing DMA
> >> +      memory bandwidth.
> >> +
> >> +  iommus:
> >> +    maxItems: 1
> >> +
> >> +  "#cooling-cells":
> >> +    const: 2
> >> +
> >> +  "#address-cells":
> >> +    const: 2
> >> +
> >> +  "#size-cells":
> >> +    const: 2
> >> +
> >> +  ranges: true
> >> +
> >> +patternProperties:
> >> +  "^interface@[0-9a-f]+$":
> >
> >I have to wonder if this interface business is required at all.
> >Why can this not go into the parent, with each region fetchable via
> >reg-names, interrupt-names and iommu-names?
> 
> Thanks for your feedback.
> 
> I did try the flat model, but the blocker is the IOMMU.
> 
> The control region and four interface regions are independent DMA requesters
> with distinct stream IDs, and each interface can be assigned to a different VM,
> driving the video core with its own isolated memory.
> 
> If all stream IDs are listed under the parent's iommus, they bind to a
> single device and share one domain, so the isolation is lost.
> This is the main reason I added the interface nodes.

Feels similar to issue Qualcomm has. I rejected such subnodes and
Qualcomm came with a solution in DMA IOMMU code, but that solution was
rejected by DMA folks:
https://lore.kernel.org/all/c7b956a9-d3e8-4e18-b780-5d08f5cd2ca1@kernel.org/

I don't have proper arguments to convince DMA folks, thus I agree for
Qualcomm for the subnodes. It should be fine here as well, in such case.

Best regards,
Krzysztof


^ permalink raw reply

* [RFC] media: dib0090: stale STANDARD_* guards appear to disable delivery-system paths
From: Pengpeng Hou @ 2026-06-25  6:10 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: Kees Cook, linux-media, linux-kernel, Pengpeng Hou

Hi,

while auditing non-Kconfig macro worlds in drivers/media/dvb-frontends/dib0090.c,
I noticed that the file still has conditional paths using older local
CONFIG_STANDARD_* and STANDARD_* names, while nearby source uses current
CONFIG_SYS_* / SYS_* style delivery-system names.

This looks like a conditional data/control-path legality issue: if the old
STANDARD_* world is no longer reachable, register setup and calibration logic
for a delivery system can be silently compiled out even though related source
paths remain.

I am not sending a patch yet because the correct DVB policy is unclear.  The
possible repairs appear to be:

1. map the old STANDARD_* world to the current SYS_* names;
2. add an explicit current local gate if the path is still supported; or
3. delete stale support if the path is intentionally dead.

Could you advise whether those STANDARD_* branches are still intended support,
or whether they should be converted or removed?

This is static source/macro analysis only.  I have not tested tuner hardware.

Thanks,
Pengpeng


^ permalink raw reply

* [PATCH] udmabuf: serialize the sg_table cache under the reservation lock
From: Bryam Vargas via B4 Relay @ 2026-06-25  6:10 UTC (permalink / raw)
  To: Christian König, Gerd Hoffmann, Vivek Kasireddy,
	Sumit Semwal
  Cc: Gurchetan Singh, linux-media, linux-kernel, linaro-mm-sig,
	dri-devel

From: Bryam Vargas <hexlabsecurity@proton.me>

begin_cpu_udmabuf() builds and caches ubuf->sg with an unserialised
check-then-set, and end_cpu_udmabuf() reads the same field unlocked. The
core invokes both cpu-access hooks without holding the reservation lock and
DMA_BUF_IOCTL_SYNC is unlocked, so concurrent SYNC ioctls on a shared
udmabuf fd race on ubuf->sg: two begins can both observe NULL and both call
get_sg_table(), and the later store orphans the earlier table and its DMA
mapping, which release_udmabuf() never frees. Each won race permanently
leaks an sg_table and an unbalanced DMA mapping.

Serialize both hooks under the buffer's reservation lock, as panfrost and
panthor do. dma_buf_begin/end_cpu_access() already annotate might_lock() on
that lock, so taking it here matches the documented contract.
Single-threaded callers are unaffected.

Fixes: 284562e1f348 ("udmabuf: implement begin_cpu_access/end_cpu_access hooks")
Cc: stable@vger.kernel.org
Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
---
Same leak-with-dangling-pointer class as CVE-2024-56712 (export_udmabuf()
error path) -- a distinct site the 2024 fix does not cover.

udmabuf is the only exporter that lazily builds its sg_table cache inside the
cpu-access hook without serialising the check-then-set. The exporters that do
comparable in-hook cache work all take a lock first: panfrost and panthor
dma_resv_lock() (both hooks), omapdrm omap_obj->lock around its lazy page-get,
the dma-heaps buffer->lock, and the TTM/GEM exporters (amdgpu, i915, xe) their
object's reservation lock. tegra and videobuf2 take no lock here because they
only sync an sg_table built earlier, so there is nothing to serialise.

Confirmed with an out-of-tree A/B exercising the begin/begin race: this driver
built as a module with get_sg_table()/put_sg_table() counting allocations
against frees, driven by a userspace racer that creates 3000 udmabufs and fires
DMA_BUF_IOCTL_SYNC(SYNC_START) from N threads on each shared fd.

  arm                            leaked sg_tables (of 3000 buffers)
  vulnerable, 4 threads          4761
  control, 1 thread              0
  patched (resv lock), 4 threads 0

One sg_table and its DMA mapping leak per won race; the single-thread control
does not leak, isolating the race; with the lock the lazy-init runs once per
buffer (3000 allocations, zero leaked). end_cpu_udmabuf() is locked for the
same field too: an unlocked end could otherwise observe the transient IS_ERR
store begin makes before resetting ubuf->sg to NULL, and dereference it. In a
tighter 5000-iteration loop the unpatched leak runs around 15-20 MB/s of slab.
---
 drivers/dma-buf/udmabuf.c | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c
index bced421c0d65..702ae13b97d1 100644
--- a/drivers/dma-buf/udmabuf.c
+++ b/drivers/dma-buf/udmabuf.c
@@ -226,6 +226,8 @@ static int begin_cpu_udmabuf(struct dma_buf *buf,
 	struct device *dev = ubuf->device->this_device;
 	int ret = 0;
 
+	dma_resv_lock(buf->resv, NULL);
+
 	if (!ubuf->sg) {
 		ubuf->sg = get_sg_table(dev, buf, direction);
 		if (IS_ERR(ubuf->sg)) {
@@ -238,6 +240,8 @@ static int begin_cpu_udmabuf(struct dma_buf *buf,
 		dma_sync_sgtable_for_cpu(dev, ubuf->sg, direction);
 	}
 
+	dma_resv_unlock(buf->resv);
+
 	return ret;
 }
 
@@ -246,12 +250,18 @@ static int end_cpu_udmabuf(struct dma_buf *buf,
 {
 	struct udmabuf *ubuf = buf->priv;
 	struct device *dev = ubuf->device->this_device;
+	int ret = 0;
+
+	dma_resv_lock(buf->resv, NULL);
 
 	if (!ubuf->sg)
-		return -EINVAL;
+		ret = -EINVAL;
+	else
+		dma_sync_sgtable_for_device(dev, ubuf->sg, direction);
 
-	dma_sync_sgtable_for_device(dev, ubuf->sg, direction);
-	return 0;
+	dma_resv_unlock(buf->resv);
+
+	return ret;
 }
 
 static const struct dma_buf_ops udmabuf_ops = {

---
base-commit: 7eed1fb17959e721031555e5b5654083fe6a7d02
change-id: 20260625-b4-disp-67d1f3db-0082918fdcb5

Best regards,
-- 
Bryam Vargas <hexlabsecurity@proton.me>



^ permalink raw reply related

* [RFC PATCH] media: dib0090: enable L/S-band tuning table rows
From: Pengpeng Hou @ 2026-06-25  6:08 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Michael Krufky
  Cc: Kees Cook, Bradford Love, Hans Verkuil, linux-media, linux-kernel,
	Pengpeng Hou

The DiB0090 tuner source enables C-band, VHF and UHF table rows through
local CONFIG_BAND_* macros, but leaves CONFIG_BAND_LBAND and
CONFIG_BAND_SBAND undefined.

This compiles the L-band and S-band PLL/tuning rows out even though current
frontends still advertise BAND_LBAND and BAND_SBAND in band_caps.  The
runtime band classifier can also route requested frequencies above UHF into
BAND_LBAND or BAND_SBAND before the driver walks the tuning and PLL tables.

Enable the existing L-band and S-band rows so the compiled provider tables
match the currently advertised band capabilities.

This is an RFC/RFT patch draft because the issue was found by static source
and macro-world auditing, not by hardware reproduction.

Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
---
 drivers/media/dvb-frontends/dib0090.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/media/dvb-frontends/dib0090.c b/drivers/media/dvb-frontends/dib0090.c
index e2a48059e854..7221aefcdfe1 100644
--- a/drivers/media/dvb-frontends/dib0090.c
+++ b/drivers/media/dvb-frontends/dib0090.c
@@ -35,6 +35,8 @@ MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
 #define CONFIG_BAND_CBAND
 #define CONFIG_BAND_VHF
 #define CONFIG_BAND_UHF
+#define CONFIG_BAND_LBAND
+#define CONFIG_BAND_SBAND
 #define CONFIG_DIB0090_USE_PWM_AGC
 
 #define EN_LNA0      0x8000
-- 
2.39.5


^ permalink raw reply related

* uvcvideo: device resets on stream start (SET_INTERFACE) — Azurewave 13d3:56bd, iso-only
From: Sid @ 2026-06-25  5:53 UTC (permalink / raw)
  To: linux-media


[-- Attachment #1.1: Type: text/plain, Size: 2362 bytes --]

Hi,

I'm reporting a UVC webcam that enumerates correctly but resets on every
stream start, with no working quirk in current mainline.

*Hardware:* Azurewave / IMC Networks USB Camera, VID:PID 13d3:56bd,
bcdDevice 20.01. Internal webcam, Razer Blade 15 Advanced (2021).
High-Speed (480 Mbps), bus-powered, UVC 1.00.

*Kernel:* 6.18.36 (NixOS 26.05), uvcvideo 1.1.1.

*Summary:*

The camera enumerates correctly and exposes valid MJPEG and YUYV formats,
but every stream start causes the device to disconnect and re-enumerate on
the USB bus. Streaming never begins; userspace returns "VIDIOC_STREAMON:
Protocol error" across all tools (ffmpeg, v4l2-ctl).

*Reproduction:*

```
v4l2-ctl --device=/dev/vio0 \
  --set-fmt-video=width=160,height=120,pixelformat=MJPG \
  --stream-mmap --stream-count=1 --stream-to=/tmp/t.jpg --verbose

```

All ioctls through QBUF succeed; STREAMON returns -1.

*Observations:*

   1. Enumeration is clean on every boot; full descriptor read, correct
   strings.
   2. Buffer setup succeeds: REQBUFS, CREATE_BUFS, QUERYBUF, QBUF all
   return OK.
   3. uvcvideo trace (trace=0xffff) shows successful format/PROBE
   negotiation, then the device disconnects at stream start. No "Setting
   alternate setting" or COMMIT completion is logged before the "USB
   disconnect" event — the reset occurs at or immediately before the
   SET_INTERFACE to a non-zero streaming altsetting, before any isochronous
   data flows.
   4. Failure is independent of resolution — 160x120 (smallest altsetting,
   128 B/packet) fails identically to 1280x720. Not a bandwidth ceiling.
   5. USB autosuspend state has no effect (power/control=on, unchanged
   behavior).

*Descriptor note:*

Interface 1 (VideoStreaming) exposes alternate settings 0–7, all
isochronous (128 B up to 3×1024 B per microframe); alt 0 is the
zero-bandwidth idle. There is no bulk streaming endpoint.
UVC_QUIRK_FORCE_BULK is therefore structurally inapplicable. Quirks tested,
confirmed active in dmesg, both ineffective:

   - 0x80 (FORCE_BULK)
   - 0x83 (FORCE_BULK | FIX_BANDWIDTH | FIX_HD)

The failure is in the isochronous stream-start / SET_INTERFACE path.

Full lsusb -v descriptor dump and uvcvideo trace log are attached. Happy to
capture a usbmon trace or test patches on request.

Thanks,

Sid

[-- Attachment #1.2: Type: text/html, Size: 5794 bytes --]

[-- Attachment #2: uvc-13d3-56bd-report.txt --]
[-- Type: text/plain, Size: 2233 bytes --]

To: linux-media@vger.kernel.org
Subject: uvcvideo: device resets on stream start (SET_INTERFACE) - Azurewave 13d3:56bd, iso-only

Hardware: Azurewave / IMC Networks USB Camera, VID:PID 13d3:56bd, bcdDevice 20.01.
Internal webcam, Razer Blade 15 Advanced (2021). High-Speed (480 Mbps), bus-powered,
UVC 1.00.

Kernel: 6.18.36 (NixOS 26.05), uvcvideo 1.1.1.

Summary:

The camera enumerates correctly and exposes valid MJPEG and YUYV formats, but every
stream start causes the device to disconnect and re-enumerate on the USB bus. Streaming
never begins; userspace returns "VIDIOC_STREAMON: Protocol error" across all tools
(ffmpeg, v4l2-ctl).

Reproduction:

    v4l2-ctl --device=/dev/video0 \
      --set-fmt-video=width=160,height=120,pixelformat=MJPG \
      --stream-mmap --stream-count=1 --stream-to=/tmp/t.jpg --verbose

All ioctls through QBUF succeed; STREAMON returns -1.

Observations:

1. Enumeration is clean on every boot; full descriptor read, correct strings.

2. Buffer setup succeeds: REQBUFS, CREATE_BUFS, QUERYBUF, QBUF all return OK.

3. uvcvideo trace (trace=0xffff) shows successful format/PROBE negotiation, then the
   device disconnects at stream start. No "Setting alternate setting" or COMMIT
   completion is logged before the "USB disconnect" event - the reset occurs at or
   immediately before the SET_INTERFACE to a non-zero streaming altsetting, before any
   isochronous data flows.

4. Failure is independent of resolution - 160x120 (smallest altsetting, 128 B/packet)
   fails identically to 1280x720. Not a bandwidth ceiling.

5. USB autosuspend state has no effect (power/control=on, unchanged behavior).

Descriptor note:

Interface 1 (VideoStreaming) exposes alternate settings 0-7, all isochronous (128 B up
to 3x1024 B per microframe); alt 0 is the zero-bandwidth idle. There is no bulk
streaming endpoint. UVC_QUIRK_FORCE_BULK is therefore structurally inapplicable. Quirks
tested, confirmed active in dmesg, both ineffective:

  - 0x80 (FORCE_BULK)
  - 0x83 (FORCE_BULK | FIX_BANDWIDTH | FIX_HD)

The failure is in the isochronous stream-start / SET_INTERFACE path.

Attached: full lsusb -v descriptor dump; uvcvideo trace log.

Happy to capture a usbmon trace or test patches on request.

[-- Attachment #3: uvc-streamon-trace.txt --]
[-- Type: text/plain, Size: 5541 bytes --]

# uvcvideo trace (trace=0xffff), kernel 6.18.36, device 13d3:56bd
# Trace captured during: ffmpeg -f v4l2 -input_format mjpeg -video_size 160x120 -i /dev/video0 -frames:v 1 /tmp/t.jpg
# Note: trace shows full format/PROBE negotiation, then uvc_v4l2_release, then the device
# is suspended/disconnects at stream start. No "Setting alternate setting" or COMMIT
# completion is logged before the USB disconnect.

[  591.354468] uvcvideo 1-7:1.0: Using default frame interval 33333.3 us (30.0 fps)
[  591.355844] uvcvideo 1-7:1.0: Trying format 0x47504a4d (MJPG): 176x144
[  591.355845] uvcvideo 1-7:1.0: Using default frame interval 33333.3 us (30.0 fps)
[  591.357078] uvcvideo 1-7:1.0: Trying format 0x47504a4d (MJPG): 320x240
[  591.357080] uvcvideo 1-7:1.0: Using default frame interval 33333.3 us (30.0 fps)
[  591.358315] uvcvideo 1-7:1.0: Trying format 0x47504a4d (MJPG): 352x288
[  591.358316] uvcvideo 1-7:1.0: Using default frame interval 33333.3 us (30.0 fps)
[  591.359609] uvcvideo 1-7:1.0: Trying format 0x47504a4d (MJPG): 640x480
[  591.359610] uvcvideo 1-7:1.0: Using default frame interval 33333.3 us (30.0 fps)
[  591.360848] uvcvideo 1-7:1.0: Trying format 0x47504a4d (MJPG): 1280x720
[  591.360850] uvcvideo 1-7:1.0: Using default frame interval 33333.3 us (30.0 fps)
[  591.362145] uvcvideo 1-7:1.0: Trying format 0x56595559 (YUYV): 1280x720
[  591.362147] uvcvideo 1-7:1.0: Using default frame interval 100000.0 us (10.0 fps)
[  591.363488] uvcvideo 1-7:1.0: Trying format 0x56595559 (YUYV): 160x120
[  591.363490] uvcvideo 1-7:1.0: Using default frame interval 33333.3 us (30.0 fps)
[  591.364794] uvcvideo 1-7:1.0: Trying format 0x56595559 (YUYV): 176x144
[  591.364795] uvcvideo 1-7:1.0: Using default frame interval 33333.3 us (30.0 fps)
[  591.366037] uvcvideo 1-7:1.0: Trying format 0x56595559 (YUYV): 320x240
[  591.366038] uvcvideo 1-7:1.0: Using default frame interval 33333.3 us (30.0 fps)
[  591.367265] uvcvideo 1-7:1.0: Trying format 0x56595559 (YUYV): 352x288
[  591.367266] uvcvideo 1-7:1.0: Using default frame interval 33333.3 us (30.0 fps)
[  591.368554] uvcvideo 1-7:1.0: Trying format 0x56595559 (YUYV): 640x480
[  591.368555] uvcvideo 1-7:1.0: Using default frame interval 33333.3 us (30.0 fps)
[  591.369861] uvcvideo 1-7:1.0: Trying format 0x56595559 (YUYV): 1280x720
[  591.369863] uvcvideo 1-7:1.0: Using default frame interval 100000.0 us (10.0 fps)
[  591.371282] uvcvideo 1-7:1.0: uvc_v4l2_release
[  591.371656] uvcvideo 1-7:1.0: uvc_v4l2_open
[  591.373231] uvcvideo 1-7:1.0: uvc_v4l2_release
[  591.373239] uvcvideo 1-7:1.0: uvc_v4l2_open
[  591.373249] uvcvideo 1-7:1.0: Trying format 0x47504a4d (MJPG): 1280x720
[  591.373250] uvcvideo 1-7:1.0: Using default frame interval 33333.3 us (30.0 fps)
[  591.374550] uvcvideo 1-7:1.0: Trying format 0x47504a4d (MJPG): 160x120
[  591.374552] uvcvideo 1-7:1.0: Using default frame interval 33333.3 us (30.0 fps)
[  591.375798] uvcvideo 1-7:1.0: Trying format 0x47504a4d (MJPG): 176x144
[  591.375800] uvcvideo 1-7:1.0: Using default frame interval 33333.3 us (30.0 fps)
[  591.377079] uvcvideo 1-7:1.0: Trying format 0x47504a4d (MJPG): 320x240
[  591.377081] uvcvideo 1-7:1.0: Using default frame interval 33333.3 us (30.0 fps)
[  591.378348] uvcvideo 1-7:1.0: Trying format 0x47504a4d (MJPG): 352x288
[  591.378349] uvcvideo 1-7:1.0: Using default frame interval 33333.3 us (30.0 fps)
[  591.379631] uvcvideo 1-7:1.0: Trying format 0x47504a4d (MJPG): 640x480
[  591.379633] uvcvideo 1-7:1.0: Using default frame interval 33333.3 us (30.0 fps)
[  591.380911] uvcvideo 1-7:1.0: Trying format 0x47504a4d (MJPG): 1280x720
[  591.380912] uvcvideo 1-7:1.0: Using default frame interval 33333.3 us (30.0 fps)
[  591.382154] uvcvideo 1-7:1.0: Trying format 0x56595559 (YUYV): 1280x720
[  591.382155] uvcvideo 1-7:1.0: Using default frame interval 100000.0 us (10.0 fps)
[  591.383505] uvcvideo 1-7:1.0: Trying format 0x56595559 (YUYV): 160x120
[  591.383506] uvcvideo 1-7:1.0: Using default frame interval 33333.3 us (30.0 fps)
[  591.384859] uvcvideo 1-7:1.0: Trying format 0x56595559 (YUYV): 176x144
[  591.384861] uvcvideo 1-7:1.0: Using default frame interval 33333.3 us (30.0 fps)
[  591.386199] uvcvideo 1-7:1.0: Trying format 0x56595559 (YUYV): 320x240
[  591.386200] uvcvideo 1-7:1.0: Using default frame interval 33333.3 us (30.0 fps)
[  591.387504] uvcvideo 1-7:1.0: Trying format 0x56595559 (YUYV): 352x288
[  591.387506] uvcvideo 1-7:1.0: Using default frame interval 33333.3 us (30.0 fps)
[  591.388745] uvcvideo 1-7:1.0: Trying format 0x56595559 (YUYV): 640x480
[  591.388746] uvcvideo 1-7:1.0: Using default frame interval 33333.3 us (30.0 fps)
[  591.389984] uvcvideo 1-7:1.0: Trying format 0x56595559 (YUYV): 1280x720
[  591.389986] uvcvideo 1-7:1.0: Using default frame interval 100000.0 us (10.0 fps)
[  591.391306] uvcvideo 1-7:1.0: uvc_v4l2_release
[  593.858017] uvcvideo 1-7:1.0: Suspending interface 1
[  593.858030] uvcvideo 1-7:1.0: Suspending interface 0

# Disconnect/re-enumerate sequence (separate cleared-buffer capture, grep-filtered):
[  803.462154] usb 1-7: USB disconnect, device number 7
[  803.467658] usb 1-7:1.0: uvc_v4l2_release
[  803.789746] usb 1-7: new high-speed USB device number 8 using xhci_hcd
[  803.941790] usb 1-7: New USB device found, idVendor=13d3, idProduct=56bd, bcdDevice=20.01
[  803.941805] usb 1-7: New USB device strings: Mfr=3, Product=1, SerialNumber=2
[  803.941810] usb 1-7: Product: USB Camera
[  803.941814] usb 1-7: Manufacturer: Azurewave
[  803.941818] usb 1-7: SerialNumber: 0x0001

[-- Attachment #4: lsusb-13d3-56bd.txt --]
[-- Type: text/plain, Size: 10670 bytes --]

Bus 001 Device 006: ID 13d3:56bd IMC Networks USB Camera
Negotiated speed: High Speed (480Mbps)
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.01
  bDeviceClass          239 Miscellaneous Device
  bDeviceSubClass         2 [unknown]
  bDeviceProtocol         1 Interface Association
  bMaxPacketSize0        64
  idVendor           0x13d3 IMC Networks
  idProduct          0x56bd USB Camera
  bcdDevice           20.01
  iManufacturer           3 Azurewave
  iProduct                1 USB Camera
  iSerial                 2 0x0001
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength       0x02ef
    bNumInterfaces          2
    bConfigurationValue     1
    iConfiguration          4 USB Camera
    bmAttributes         0x80
      (Bus Powered)
    MaxPower              500mA
    Interface Association:
      bLength                 8
      bDescriptorType        11
      bFirstInterface         0
      bInterfaceCount         2
      bFunctionClass         14 Video
      bFunctionSubClass       3 Video Interface Collection
      bFunctionProtocol       0
      iFunction               5 USB Camera
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass        14 Video
      bInterfaceSubClass      1 Video Control
      bInterfaceProtocol      0
      iInterface              5 USB Camera
      VideoControl Interface Descriptor:
        bLength                13
        bDescriptorType        36
        bDescriptorSubtype      1 (HEADER)
        bcdUVC               1.00
        wTotalLength       0x006b
        dwClockFrequency       15.000000MHz
        bInCollection           1
        baInterfaceNr( 0)       1
      VideoControl Interface Descriptor:
        bLength                18
        bDescriptorType        36
        bDescriptorSubtype      2 (INPUT_TERMINAL)
        bTerminalID             1
        wTerminalType      0x0201 Camera Sensor
        bAssocTerminal          0
        iTerminal               0
        wObjectiveFocalLengthMin      0
        wObjectiveFocalLengthMax      0
        wOcularFocalLength            0
        bControlSize                  3
        bmControls           0x00000004
          Auto-Exposure Priority
      VideoControl Interface Descriptor:
        bLength                11
        bDescriptorType        36
        bDescriptorSubtype      5 (PROCESSING_UNIT)
      Warning: Descriptor too short
        bUnitID                 2
        bSourceID               1
        wMaxMultiplier          0
        bControlSize            2
        bmControls     0x0000177f
          Brightness
          Contrast
          Hue
          Saturation
          Sharpness
          Gamma
          White Balance Temperature
          Backlight Compensation
          Gain
          Power Line Frequency
          White Balance Temperature, Auto
        iProcessing             0
        bmVideoStandards     0x09
          None
          SECAM - 625/50
      VideoControl Interface Descriptor:
        bLength                 9
        bDescriptorType        36
        bDescriptorSubtype      3 (OUTPUT_TERMINAL)
        bTerminalID             3
        wTerminalType      0x0101 USB Streaming
        bAssocTerminal          0
        bSourceID               6
        iTerminal               0
      VideoControl Interface Descriptor:
        bLength                27
        bDescriptorType        36
        bDescriptorSubtype      6 (EXTENSION_UNIT)
        bUnitID                 4
        guidExtensionCode         {1229a78c-47b4-4094-b0ce-db07386fb938}
        bNumControls            2
        bNrInPins               1
        baSourceID( 0)          2
        bControlSize            2
        bmControls( 0)       0x00
        bmControls( 1)       0x06
        iExtension              0
      VideoControl Interface Descriptor:
        bLength                29
        bDescriptorType        36
        bDescriptorSubtype      6 (EXTENSION_UNIT)
        bUnitID                 6
        guidExtensionCode         {26b8105a-0713-4870-979d-da79444bb68e}
        bNumControls            1
        bNrInPins               1
        baSourceID( 0)          4
        bControlSize            4
        bmControls( 0)       0x04
        bmControls( 1)       0x00
        bmControls( 2)       0x00
        bmControls( 3)       0x00
        iExtension              7 (error)
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x83  EP 3 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0010  1x 16 bytes
        bInterval               6
        VideoControl Endpoint Descriptor:
          bLength                 5
          bDescriptorType        37
          bDescriptorSubtype      3 (EP_INTERRUPT)
          wMaxTransferSize       16
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       0
      bNumEndpoints           0
      bInterfaceClass        14 Video
      bInterfaceSubClass      2 Video Streaming
      bInterfaceProtocol      0
      iInterface              0
      VideoStreaming Interface Descriptor:
        bLength                            15
        bDescriptorType                    36
        bDescriptorSubtype                  1 (INPUT_HEADER)
        bNumFormats                         2
        wTotalLength                   0x01e5
        bEndpointAddress                 0x81  EP 1 IN
        bmInfo                              0
        bTerminalLink                       3
        bStillCaptureMethod                 1
        bTriggerSupport                     1
        bTriggerUsage                       0
        bControlSize                        1
        bmaControls( 0)                     0
        bmaControls( 1)                     0
      VideoStreaming Interface Descriptor:
        bLength                            11
        bDescriptorType                    36
        bDescriptorSubtype                  6 (FORMAT_MJPEG)
        bFormatIndex                        1
        bNumFrameDescriptors                7
        bFlags                              1
          Fixed-size samples: Yes
        bDefaultFrameIndex                  1
        bAspectRatioX                       0
        bAspectRatioY                       0
        bmInterlaceFlags                 0x00
          Interlaced stream or variable: No
          Fields per frame: 1 fields
          Field 1 first: No
          Field pattern: Field 1 only
        bCopyProtect                        0
      [MJPEG FRAME descriptors, bFrameIndex 1-7:]
        1: 1280x720   minBitRate 442368000   maxFrameBuffer 1843200   interval 333333 (30fps)
        2:  160x120   minBitRate   9216000   maxFrameBuffer   38400   interval 333333 (30fps)
        3:  176x144   minBitRate  12165120   maxFrameBuffer   50688   interval 333333 (30fps)
        4:  320x240   minBitRate  36864000   maxFrameBuffer  153600   interval 333333 (30fps)
        5:  352x288   minBitRate  48660480   maxFrameBuffer  202752   interval 333333 (30fps)
        6:  640x480   minBitRate 147456000   maxFrameBuffer  614400   interval 333333 (30fps)
        7: 1280x720   minBitRate 442368000   maxFrameBuffer 1843200   interval 333333 (30fps)
      VideoStreaming Interface Descriptor:
        bDescriptorSubtype                 13 (COLORFORMAT)
        bColorPrimaries                     1 (BT.709,sRGB)
        bTransferCharacteristics            1 (BT.709)
        bMatrixCoefficients                 4 (SMPTE 170M (BT.601))
      VideoStreaming Interface Descriptor:
        bLength                            27
        bDescriptorType                    36
        bDescriptorSubtype                  4 (FORMAT_UNCOMPRESSED)
        bFormatIndex                        2
        bNumFrameDescriptors                7
        guidFormat                            {32595559-0000-0010-8000-00aa00389b71} (YUY2)
        bBitsPerPixel                      16
        bDefaultFrameIndex                  1
        bAspectRatioX                       0
        bAspectRatioY                       0
        bmInterlaceFlags                 0x00
        bCopyProtect                        0
      [YUYV/UNCOMPRESSED FRAME descriptors, bFrameIndex 1-7:]
        1: 1280x720   minBitRate 147456000   maxFrameBuffer 1843200   interval 1000000 (10fps)
        2:  160x120   minBitRate   9216000   maxFrameBuffer   38400   interval  333333 (30fps)
        3:  176x144   minBitRate  12165120   maxFrameBuffer   50688   interval  333333 (30fps)
        4:  320x240   minBitRate  36864000   maxFrameBuffer  153600   interval  333333 (30fps)
        5:  352x288   minBitRate  48660480   maxFrameBuffer  202752   interval  333333 (30fps)
        6:  640x480   minBitRate 147456000   maxFrameBuffer  614400   interval  333333 (30fps)
        7: 1280x720   minBitRate 147456000   maxFrameBuffer 1843200   interval 1000000 (10fps)
      VideoStreaming Interface Descriptor:
        bDescriptorSubtype                 13 (COLORFORMAT)
        bColorPrimaries                     1 (BT.709,sRGB)
        bTransferCharacteristics            1 (BT.709)
        bMatrixCoefficients                 4 (SMPTE 170M (BT.601))

    --- Interface 1, streaming alternate settings (ALL ISOCHRONOUS, EP 0x81 IN, bInterval 1) ---
    AltSetting 0: bNumEndpoints 0  (zero-bandwidth idle, no endpoint)
    AltSetting 1: wMaxPacketSize 0x0080  1x  128 bytes
    AltSetting 2: wMaxPacketSize 0x0200  1x  512 bytes
    AltSetting 3: wMaxPacketSize 0x0400  1x 1024 bytes
    AltSetting 4: wMaxPacketSize 0x0b00  2x  768 bytes
    AltSetting 5: wMaxPacketSize 0x0c00  2x 1024 bytes
    AltSetting 6: wMaxPacketSize 0x1380  3x  896 bytes
    AltSetting 7: wMaxPacketSize 0x1400  3x 1024 bytes
    (No bulk streaming endpoint exists on any alternate setting.)

Binary Object Store Descriptor:
  bLength                 5
  bDescriptorType        15
  wTotalLength       0x0021
  bNumDeviceCaps          1
  Platform Device Capability:
    bLength                28
    bDescriptorType        16
    bDevCapabilityType      5
    PlatformCapabilityUUID    {d8dd60df-4589-4cc7-9cd2-659d9e648a9f}
Device Status:     0x0000
  (Bus Powered)

^ permalink raw reply

* Re: [PATCH v5 3/3] media: i2c: imx471: Add Sony IMX471 image sensor driver
From: Kate Hsuan @ 2026-06-25  5:46 UTC (permalink / raw)
  To: Tarang Raval
  Cc: Kieran Bingham, Hans Verkuil, Hans de Goede,
	Mauro Carvalho Chehab, Sakari Ailus, Serin Yeh,
	linux-media@vger.kernel.org, linux-kernel@vger.kernel.org,
	Damjan Georgievski
In-Reply-To: <PN3P287MB18292427E6E41029373E9B1D8BED2@PN3P287MB1829.INDP287.PROD.OUTLOOK.COM>

Hi Kieran and Tarang,

Thank you for reviewing.

On Wed, Jun 24, 2026 at 10:09 AM Tarang Raval
<tarang.raval@siliconsignals.io> wrote:
>
> Hi Kieran,
>
> > Quoting Kate Hsuan (2026-06-24 04:35:08)
> > > Add a new driver for Sony imx471 camera sensor. It is based on
> > > Jimmy Su <jimmy.su@intel.com> implementation and the driver can be found
> > > in the following URL.
> > > https://github.com/intel/ipu6-drivers/commits/master/drivers/media/i2c/imx471.c
> > >
> > > This sensor can be found on Lenovo X1 Carbon G14, X9-14 and X9-15 laptops
> > > and it is a part of IPU7 solution. The driver was tested on Lenovo X1
> > > Carbon G14, X9-14 and X9-15 laptops.
> > >
> > > Signed-off-by: Kate Hsuan <hpa@redhat.com>
> > > ---
> >
> > ... <snip>
> >
> > > +#define IMX471_REG_EXCK_FREQ                   CCI_REG16(0x0136)
> > > +#define IMX471_EXCK_FREQ(n)                    ((n) * 256)     /* n in MHz */
> > >
> >
> > <snip>
> >
> > > +
> > > +static const struct cci_reg_sequence imx471_global_regs[] = {
> > > +       { IMX471_REG_EXCK_FREQ, IMX471_EXCK_FREQ(19.2) },
> >
> >
> > Does this work? Is this a compile time constant multiplying floating
> > point with the compiler, or something happening in the kernel?
> >
> > I'll be happy if it does, but it catches my attention as something I
> > thought we couldn't do.
>
> I tested this with GCC. It evaluates the expression at compile time and
> folds it into the integer constant 4915. The generated assembly/object
> file contains .quad 4915 in the initialized data and no floating-point
> instructions are emitted, so the kernel only ever sees the integer value.

The compiler didn't complain anything and covert it to a integer but
putting a floating point here is not a good way.
I focued on finding a register value without noticing that it is a
floating point
I'll set a value IMX471_REG_EXCK_FREQ directly and  drop IMX471_EXCK_FREQ.


>
> Best Regards,
> Tarang
>


-- 
BR,
Kate


^ permalink raw reply

* Re: [PATCH v5 3/3] media: i2c: imx471: Add Sony IMX471 image sensor driver
From: Kate Hsuan @ 2026-06-25  5:36 UTC (permalink / raw)
  To: Vladimir Zapolskiy
  Cc: Mauro Carvalho Chehab, Hans de Goede, Hans Verkuil, Sakari Ailus,
	Serin Yeh, Tarang Raval, Damjan Georgievski, linux-media,
	linux-kernel
In-Reply-To: <e74e992a-9358-40c5-a685-833a8f135dc0@linaro.org>

Hi Vladimir,

Thank you for reviewing.

On Wed, Jun 24, 2026 at 6:12 AM Vladimir Zapolskiy
<vladimir.zapolskiy@linaro.org> wrote:
>
> On 6/24/26 06:35, Kate Hsuan wrote:
> > Add a new driver for Sony imx471 camera sensor. It is based on
> > Jimmy Su <jimmy.su@intel.com> implementation and the driver can be found
> > in the following URL.
> > https://github.com/intel/ipu6-drivers/commits/master/drivers/media/i2c/imx471.c
> >
> > This sensor can be found on Lenovo X1 Carbon G14, X9-14 and X9-15 laptops
> > and it is a part of IPU7 solution. The driver was tested on Lenovo X1
> > Carbon G14, X9-14 and X9-15 laptops.
> >
> > Signed-off-by: Kate Hsuan <hpa@redhat.com>
>
> Please find a few minor nitpicks below.
>
> > ---
> >   MAINTAINERS                |   6 +
> >   drivers/media/i2c/Kconfig  |  10 +
> >   drivers/media/i2c/Makefile |   1 +
> >   drivers/media/i2c/imx471.c | 971 +++++++++++++++++++++++++++++++++++++
> >   4 files changed, 988 insertions(+)
> >   create mode 100644 drivers/media/i2c/imx471.c
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 6b4560681b51..586958b1816d 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -25219,6 +25219,12 @@ T:   git git://linuxtv.org/media.git
> >   F:  Documentation/devicetree/bindings/media/i2c/sony,imx415.yaml
> >   F:  drivers/media/i2c/imx415.c
> >
> > +SONY IMX471 SENSOR DRIVER
> > +M:   Kate Hsuan <hpa@redhat.com>
> > +L:   linux-media@vger.kernel.org
> > +S:   Maintained
> > +F:   drivers/media/i2c/imx471.c
> > +
> >   SONY MEMORYSTICK SUBSYSTEM
> >   M:  Maxim Levitsky <maximlevitsky@gmail.com>
> >   M:  Alex Dubov <oakad@yahoo.com>
> > diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> > index 5d173e0ecf42..b7199f9f5a0c 100644
> > --- a/drivers/media/i2c/Kconfig
> > +++ b/drivers/media/i2c/Kconfig
> > @@ -287,6 +287,16 @@ config VIDEO_IMX415
> >         To compile this driver as a module, choose M here: the
> >         module will be called imx415.
> >
> > +config VIDEO_IMX471
> > +     tristate "Sony IMX471 sensor support"
> > +     select V4L2_CCI_I2C
> > +     help
> > +       This is a Video4Linux2 sensor driver for the Sony
> > +       IMX471 camera.
> > +
> > +       To compile this driver as a module, choose M here: the
> > +       module will be called imx471.
> > +
> >   config VIDEO_MAX9271_LIB
> >       tristate
> >
> > diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> > index e45359efe0e4..acbd321fc12e 100644
> > --- a/drivers/media/i2c/Makefile
> > +++ b/drivers/media/i2c/Makefile
> > @@ -61,6 +61,7 @@ obj-$(CONFIG_VIDEO_IMX335) += imx335.o
> >   obj-$(CONFIG_VIDEO_IMX355) += imx355.o
> >   obj-$(CONFIG_VIDEO_IMX412) += imx412.o
> >   obj-$(CONFIG_VIDEO_IMX415) += imx415.o
> > +obj-$(CONFIG_VIDEO_IMX471) += imx471.o
> >   obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o
> >   obj-$(CONFIG_VIDEO_ISL7998X) += isl7998x.o
> >   obj-$(CONFIG_VIDEO_KS0127) += ks0127.o
> > diff --git a/drivers/media/i2c/imx471.c b/drivers/media/i2c/imx471.c
> > new file mode 100644
> > index 000000000000..1e1bff69ea3d
> > --- /dev/null
> > +++ b/drivers/media/i2c/imx471.c
> > @@ -0,0 +1,971 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * imx471.c - imx471 sensor driver
> > + *
> > + * Copyright (C) 2025 Intel Corporation
> > + * Copyright (C) 2026 Kate Hsuan <hpa@redhat.com>
> > + */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/delay.h>
> > +#include <linux/i2c.h>
> > +#include <linux/module.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/regulator/consumer.h>
> > +#include <linux/unaligned.h>
>
> No declarations coming from linux/unaligned.h header are used in the driver.
>
> > +#include <media/v4l2-cci.h>
> > +#include <media/v4l2-ctrls.h>
> > +#include <media/v4l2-device.h>
>
> No declarations coming from media/v4l-event.h header are used in the driver,
> likely it's intended to include media/v4l2-subdev.h instead.
>
> > +#include <media/v4l2-event.h>
>
> No declarations coming from media/v4l-event.h header are used in the driver.

I'll drop them.

>
> > +#include <media/v4l2-fwnode.h>
> > +
> > +#define IMX471_REG_MODE_SELECT                       CCI_REG8(0x0100)
> > +#define IMX471_MODE_STANDBY                  0x00
> > +#define IMX471_MODE_STREAMING                        0x01
> > +
> > +/* Chip ID */
> > +#define IMX471_REG_CHIP_ID                   CCI_REG16(0x0016)
> > +#define IMX471_CHIP_ID                               0x0471
> > +
> > +/* V_TIMING internal */
> > +#define IMX471_REG_FLL                               CCI_REG16(0x0340)
> > +#define IMX471_FLL_MAX                               0xffff
> > +
> > +/* Exposure control */
> > +#define IMX471_REG_EXPOSURE                  CCI_REG16(0x0202)
> > +#define IMX471_EXPOSURE_MIN                  1
> > +#define IMX471_EXPOSURE_STEP                 1
> > +#define IMX471_EXPOSURE_DEFAULT                      1270
> > +
> > +/* Default exposure margin */
> > +#define IMX471_EXPOSURE_MARGIN                       18
> > +
> > +/* Analog gain control */
> > +#define IMX471_REG_ANALOG_GAIN                       CCI_REG16(0x0204)
> > +#define IMX471_ANA_GAIN_MIN                  0
> > +#define IMX471_ANA_GAIN_MAX                  800
> > +#define IMX471_ANA_GAIN_STEP                 1
> > +#define IMX471_ANA_GAIN_DEFAULT                      0
> > +
> > +/* Digital gain control */
> > +#define IMX471_REG_DPGA_USE_GLOBAL_GAIN              CCI_REG16(0x3ff9)
> > +#define IMX471_REG_DIG_GAIN_GLOBAL           CCI_REG16(0x020e)
> > +#define IMX471_DGTL_GAIN_MIN                 256
> > +#define IMX471_DGTL_GAIN_MAX                 4095
> > +#define IMX471_DGTL_GAIN_STEP                        1
> > +#define IMX471_DGTL_GAIN_DEFAULT             256
> > +
> > +/* HFLIP and VFLIP control */
> > +#define IMX471_REG_ORIENTATION                       CCI_REG8(0x0101)
> > +#define IMX471_HFLIP_BIT                     BIT(0)
> > +#define IMX471_VFLIP_BIT                     BIT(1)
>
> IMX471_HFLIP_BIT and IMX471_VFLIP_BIT macro do not have any users in the driver.
I'll drop them.

>
> > +
> > +/* Test Pattern Control */
> > +#define IMX471_REG_TEST_PATTERN                      CCI_REG8(0x0600)
> > +#define IMX471_TEST_PATTERN_DISABLED         0
> > +#define IMX471_TEST_PATTERN_SOLID_COLOR              1
> > +#define IMX471_TEST_PATTERN_COLOR_BARS               2
> > +#define IMX471_TEST_PATTERN_GRAY_COLOR_BARS  3
> > +#define IMX471_TEST_PATTERN_PN9                      4
>
> IMX471_TEST_PATTERN_* macro do not have any users in the driver.
I'll drop them.

>
> > +
> > +/* default link frequency and external clock */
> > +#define IMX471_LINK_FREQ_DEFAULT             200000000LL
> > +#define IMX471_EXT_CLK                               19200000
> > +#define IMX471_LINK_FREQ_INDEX                       0
>
> Please drop IMX471_LINK_FREQ_INDEX macro along with .link_freq_index from
> struct imx471_mode.
Okay

>
> --
> Best wishes,
> Vladimir
>


-- 
BR,
Kate


^ permalink raw reply

* Re: [PATCH v5 3/3] media: i2c: imx471: Add Sony IMX471 image sensor driver
From: Kate Hsuan @ 2026-06-25  3:55 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: Tarang Raval, Serin Yeh, Damjan Georgievski, Hans Verkuil,
	Hans de Goede, linux-media@vger.kernel.org,
	linux-kernel@vger.kernel.org, Mauro Carvalho Chehab
In-Reply-To: <ajuq_pDnirv1NvKg@kekkonen.localdomain>

Hi Sakari and Tarang,


On Wed, Jun 24, 2026 at 6:01 AM Sakari Ailus
<sakari.ailus@linux.intel.com> wrote:
>
> On Wed, Jun 24, 2026 at 09:58:11AM +0000, Tarang Raval wrote:
> > > So, avdd is necessary.
> >
> > Could you please try adding a mapping for SONY471A in
> > int3472_gpio_map[] in drivers/platform/x86/intel/int3472/discrete.c?
> >
> > Not fully sure, I have to check, but hopefully it will work.
>
> That's the intent, yes.
>
> --
> Sakari Ailus
>

I'll propose a change for it.

-- 
BR,
Kate


^ permalink raw reply

* RE: [PATCH v6 9/9] arm64: dts: freescale: imx95: Add video codec node
From: Nas Chung @ 2026-06-25  2:11 UTC (permalink / raw)
  To: Francesco Dolcini
  Cc: mchehab@kernel.org, hverkuil@xs4all.nl, robh@kernel.org,
	krzk+dt@kernel.org, conor+dt@kernel.org, shawnguo@kernel.org,
	s.hauer@pengutronix.de, linux-media@vger.kernel.org,
	devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-imx@nxp.com, linux-arm-kernel@lists.infradead.org,
	jackson.lee, lafley.kim, marek.vasut@mailbox.org
In-Reply-To: <20260624115050.GA38214@francesco-nb>

Hi, Francesco.

>-----Original Message-----
>From: Francesco Dolcini <francesco@dolcini.it>
>Sent: Wednesday, June 24, 2026 8:51 PM
>To: Nas Chung <nas.chung@chipsnmedia.com>
>Cc: mchehab@kernel.org; hverkuil@xs4all.nl; robh@kernel.org;
>krzk+dt@kernel.org; conor+dt@kernel.org; shawnguo@kernel.org;
>s.hauer@pengutronix.de; linux-media@vger.kernel.org;
>devicetree@vger.kernel.org; linux-kernel@vger.kernel.org; linux-imx@nxp.com;
>linux-arm-kernel@lists.infradead.org; jackson.lee
><jackson.lee@chipsnmedia.com>; lafley.kim <lafley.kim@chipsnmedia.com>;
>marek.vasut@mailbox.org
>Subject: Re: [PATCH v6 9/9] arm64: dts: freescale: imx95: Add video codec
>node
>
>On Wed, Jun 24, 2026 at 04:20:43PM +0900, Nas Chung wrote:
>> Add the Chips and Media wave633 video codec node on IMX95 SoCs.
>>
>> Signed-off-by: Nas Chung <nas.chung@chipsnmedia.com>
>> ---
>>  .../boot/dts/freescale/imx95-19x19-evk.dts    | 11 ++++++
>>  arch/arm64/boot/dts/freescale/imx95.dtsi      | 36 +++++++++++++++++++
>>  2 files changed, 47 insertions(+)
>>
>> diff --git a/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts
>b/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts
>> index 041fd838fabb..7edd1c69966a 100644
>> --- a/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts
>> +++ b/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts
>> @@ -76,6 +76,11 @@ linux_cma: linux,cma {
>>  			linux,cma-default;
>>  			reusable;
>>  		};
>> +
>> +		vpu_boot: memory@a0000000 {
>> +			reg = <0 0xa0000000 0 0x100000>;
>> +			no-map;
>> +		};
>>  	};
>>
>>  	flexcan1_phy: can-phy0 {
>> @@ -1142,3 +1147,9 @@ &tpm6 {
>>  	pinctrl-0 = <&pinctrl_tpm6>;
>>  	status = "okay";
>>  };
>> +
>> +&vpu {
>> +	memory-region = <&vpu_boot>;
>> +	sram = <&sram1>;
>
>Can the `sram` node moved to imx95.dtsi or not?

Thanks for your feedback.

Agreed, I'll move the sram property to imx95.dtsi in v7.

Thanks.
Nas.

>
>Francesco

^ permalink raw reply

* RE: [PATCH v6 2/9] dt-bindings: media: nxp: Add Wave6 video codec device
From: Nas Chung @ 2026-06-25  1:43 UTC (permalink / raw)
  To: Conor Dooley
  Cc: mchehab@kernel.org, hverkuil@xs4all.nl, robh@kernel.org,
	krzk+dt@kernel.org, conor+dt@kernel.org, shawnguo@kernel.org,
	s.hauer@pengutronix.de, linux-media@vger.kernel.org,
	devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-imx@nxp.com, linux-arm-kernel@lists.infradead.org,
	jackson.lee, lafley.kim, marek.vasut@mailbox.org
In-Reply-To: <20260624-junkyard-sensuous-fcd43189b593@spud>

Hi, Conor.

>-----Original Message-----
>From: Conor Dooley <conor@kernel.org>
>Sent: Thursday, June 25, 2026 1:42 AM
>To: Nas Chung <nas.chung@chipsnmedia.com>
>Cc: mchehab@kernel.org; hverkuil@xs4all.nl; robh@kernel.org;
>krzk+dt@kernel.org; conor+dt@kernel.org; shawnguo@kernel.org;
>s.hauer@pengutronix.de; linux-media@vger.kernel.org;
>devicetree@vger.kernel.org; linux-kernel@vger.kernel.org; linux-imx@nxp.com;
>linux-arm-kernel@lists.infradead.org; jackson.lee
><jackson.lee@chipsnmedia.com>; lafley.kim <lafley.kim@chipsnmedia.com>;
>marek.vasut@mailbox.org
>Subject: Re: [PATCH v6 2/9] dt-bindings: media: nxp: Add Wave6 video codec
>device
>
>On Wed, Jun 24, 2026 at 04:20:36PM +0900, Nas Chung wrote:
>> Add documentation for the Chips&Media Wave6 video codec on NXP i.MX SoCs.
>>
>> The hardware contains one control register region and four interface
>> register regions for a shared video processing engine. The control region
>> manages shared resources such as firmware memory, while each interface
>> region has its own MMIO range and interrupt.
>>
>> The control region and each interface region are distinct DMA requesters
>> and can be associated with separate IOMMU stream IDs. Represent the
>> control region as the parent node and the interface register regions as
>> child nodes to describe these resources.
>>
>> Signed-off-by: Nas Chung <nas.chung@chipsnmedia.com>
>> ---
>>  .../bindings/media/nxp,imx95-vpu.yaml         | 163 ++++++++++++++++++
>>  MAINTAINERS                                   |   7 +
>>  2 files changed, 170 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/media/nxp,imx95-
>vpu.yaml
>>
>> diff --git a/Documentation/devicetree/bindings/media/nxp,imx95-vpu.yaml
>b/Documentation/devicetree/bindings/media/nxp,imx95-vpu.yaml
>> new file mode 100644
>> index 000000000000..9a5ca53e15a3
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/media/nxp,imx95-vpu.yaml
>> @@ -0,0 +1,163 @@
>> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/media/nxp,imx95-vpu.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Chips&Media Wave6 Series multi-standard codec IP on NXP i.MX SoCs
>> +
>> +maintainers:
>> +  - Nas Chung <nas.chung@chipsnmedia.com>
>> +  - Jackson Lee <jackson.lee@chipsnmedia.com>
>> +
>> +description:
>> +  The Chips&Media Wave6 codec IP is a multi-standard video
>encoder/decoder.
>> +  On NXP i.MX SoCs, the Wave6 codec IP exposes one control register
>region and
>> +  four interface register regions for a shared video processing engine.
>> +  The parent node describes the control region, which has its own MMIO
>range and
>> +  manages shared resources such as firmware memory. The child nodes
>describe the
>> +  interface register regions. Each interface region has its own MMIO
>range and
>> +  interrupt.
>> +  The control region and the interface regions are distinct DMA
>requesters.
>> +  The control region and each interface region can be associated with
>separate
>> +  IOMMU stream IDs, allowing DMA isolation between them.
>> +
>> +properties:
>> +  compatible:
>> +    enum:
>> +      - nxp,imx95-vpu
>> +
>> +  reg:
>> +    maxItems: 1
>> +
>> +  clocks:
>> +    items:
>> +      - description: VPU core clock
>> +      - description: VPU associated block clock
>> +
>> +  clock-names:
>> +    items:
>> +      - const: core
>> +      - const: vpublk
>> +
>> +  power-domains:
>> +    items:
>> +      - description: Main VPU power domain
>> +      - description: Performance power domain
>> +
>> +  power-domain-names:
>> +    items:
>> +      - const: vpu
>> +      - const: perf
>> +
>> +  memory-region:
>> +    maxItems: 1
>> +
>> +  sram:
>> +    $ref: /schemas/types.yaml#/definitions/phandle
>> +    description:
>> +      phandle to the SRAM node used to store reference data, reducing DMA
>> +      memory bandwidth.
>> +
>> +  iommus:
>> +    maxItems: 1
>> +
>> +  "#cooling-cells":
>> +    const: 2
>> +
>> +  "#address-cells":
>> +    const: 2
>> +
>> +  "#size-cells":
>> +    const: 2
>> +
>> +  ranges: true
>> +
>> +patternProperties:
>> +  "^interface@[0-9a-f]+$":
>
>I have to wonder if this interface business is required at all.
>Why can this not go into the parent, with each region fetchable via
>reg-names, interrupt-names and iommu-names?

Thanks for your feedback.

I did try the flat model, but the blocker is the IOMMU.

The control region and four interface regions are independent DMA requesters
with distinct stream IDs, and each interface can be assigned to a different VM,
driving the video core with its own isolated memory.

If all stream IDs are listed under the parent's iommus, they bind to a
single device and share one domain, so the isolation is lost.
This is the main reason I added the interface nodes.

Thanks.
Nas.

>
>Cheers,
>Conor.
>
>> +    type: object
>> +    description:
>> +      An interface register region within the Chips&Media Wave6 codec IP.
>> +      Each region has its own MMIO range and interrupt and can be
>associated
>> +      with a separate IOMMU stream ID for DMA isolation.
>> +    additionalProperties: false
>> +
>> +    properties:
>> +      reg:
>> +        maxItems: 1
>> +
>> +      interrupts:
>> +        maxItems: 1
>> +
>> +      iommus:
>> +        maxItems: 1
>> +
>> +    required:
>> +      - reg
>> +      - interrupts
>> +
>> +required:
>> +  - compatible
>> +  - reg
>> +  - clocks
>> +  - clock-names
>> +  - power-domains
>> +  - power-domain-names
>> +  - memory-region
>> +  - "#address-cells"
>> +  - "#size-cells"
>> +  - ranges
>> +
>> +additionalProperties: false
>> +
>> +examples:
>> +  - |
>> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
>> +    #include <dt-bindings/clock/nxp,imx95-clock.h>
>> +
>> +    soc {
>> +      #address-cells = <2>;
>> +      #size-cells = <2>;
>> +
>> +      video-codec@4c4c0000 {
>> +        compatible = "nxp,imx95-vpu";
>> +        reg = <0x0 0x4c4c0000 0x0 0x10000>;
>> +        clocks = <&scmi_clk 115>,
>> +                 <&vpu_blk_ctrl IMX95_CLK_VPUBLK_WAVE>;
>> +        clock-names = "core", "vpublk";
>> +        power-domains = <&scmi_devpd 21>,
>> +                        <&scmi_perf 10>;
>> +        power-domain-names = "vpu", "perf";
>> +        memory-region = <&vpu_boot>;
>> +        sram = <&sram1>;
>> +        iommus = <&smmu 0x32>;
>> +        #cooling-cells = <2>;
>> +        #address-cells = <2>;
>> +        #size-cells = <2>;
>> +        ranges;
>> +
>> +        interface@4c480000 {
>> +          reg = <0x0 0x4c480000 0x0 0x10000>;
>> +          interrupts = <GIC_SPI 299 IRQ_TYPE_LEVEL_HIGH>;
>> +          iommus = <&smmu 0x33>;
>> +        };
>> +
>> +        interface@4c490000 {
>> +          reg = <0x0 0x4c490000 0x0 0x10000>;
>> +          interrupts = <GIC_SPI 300 IRQ_TYPE_LEVEL_HIGH>;
>> +          iommus = <&smmu 0x34>;
>> +        };
>> +
>> +        interface@4c4a0000 {
>> +          reg = <0x0 0x4c4a0000 0x0 0x10000>;
>> +          interrupts = <GIC_SPI 301 IRQ_TYPE_LEVEL_HIGH>;
>> +          iommus = <&smmu 0x35>;
>> +        };
>> +
>> +        interface@4c4b0000 {
>> +          reg = <0x0 0x4c4b0000 0x0 0x10000>;
>> +          interrupts = <GIC_SPI 302 IRQ_TYPE_LEVEL_HIGH>;
>> +          iommus = <&smmu 0x36>;
>> +        };
>> +      };
>> +    };
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index efbf808063e5..77ea3a1a966b 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -28688,6 +28688,13 @@ S:	Maintained
>>  F:	Documentation/devicetree/bindings/media/cnm,wave521c.yaml
>>  F:	drivers/media/platform/chips-media/wave5/
>>
>> +WAVE6 VPU CODEC DRIVER
>> +M:	Nas Chung <nas.chung@chipsnmedia.com>
>> +M:	Jackson Lee <jackson.lee@chipsnmedia.com>
>> +L:	linux-media@vger.kernel.org
>> +S:	Maintained
>> +F:	Documentation/devicetree/bindings/media/nxp,imx95-vpu.yaml
>> +
>>  WHISKEYCOVE PMIC GPIO DRIVER
>>  M:	Kuppuswamy Sathyanarayanan
><sathyanarayanan.kuppuswamy@linux.intel.com>
>>  L:	linux-gpio@vger.kernel.org
>> --
>> 2.31.1
>>

^ permalink raw reply

* Re: [PATCH 06/10] drm/xe: stop using dma_fence_is_signaled_locked
From: Matthew Brost @ 2026-06-25  0:44 UTC (permalink / raw)
  To: christian.koenig
  Cc: phasta, simona, sumit.semwal, tvrtko.ursulin, dakr, dri-devel,
	linux-media, linaro-mm-sig
In-Reply-To: <20260624122917.2483-7-christian.koenig@amd.com>

On Wed, Jun 24, 2026 at 01:13:30PM +0200, Christian König wrote:
> This use case is a bit more complicated since the irq worker is
> actually the one signaling the fence.
> 
> The patch should not indroduce any functional change, but the code can
> probably be cleaned up quite a bit after the full patch set lands.
> 

Most likely - this is pretty old code largely untouched from the
original Xe implementation - will look at this after this series lands.

> Signed-off-by: Christian König <christian.koenig@amd.com>
> ---
>  drivers/gpu/drm/xe/xe_hw_fence.c | 28 +++++++++++++++-------------
>  1 file changed, 15 insertions(+), 13 deletions(-)
> 
> diff --git a/drivers/gpu/drm/xe/xe_hw_fence.c b/drivers/gpu/drm/xe/xe_hw_fence.c
> index 14720623ad00..a4e0278559b8 100644
> --- a/drivers/gpu/drm/xe/xe_hw_fence.c
> +++ b/drivers/gpu/drm/xe/xe_hw_fence.c
> @@ -16,6 +16,8 @@
>  
>  static struct kmem_cache *xe_hw_fence_slab;
>  
> +static struct xe_hw_fence *to_xe_hw_fence(struct dma_fence *fence);
> +
>  int __init xe_hw_fence_module_init(void)
>  {
>  	xe_hw_fence_slab = kmem_cache_create("xe_hw_fence",
> @@ -47,6 +49,16 @@ static void fence_free(struct rcu_head *rcu)
>  		kmem_cache_free(xe_hw_fence_slab, fence);
>  }
>  
> +static bool xe_hw_fence_signaled(struct dma_fence *dma_fence)

Maybe just forward declare this function to keep git blame intact?

Matt

> +{
> +	struct xe_hw_fence *fence = to_xe_hw_fence(dma_fence);
> +	struct xe_device *xe = fence->xe;
> +	u32 seqno = xe_map_rd(xe, &fence->seqno_map, 0, u32);
> +
> +	return dma_fence->error ||
> +		!__dma_fence_is_later(dma_fence, dma_fence->seqno, seqno);
> +}
> +
>  static void hw_fence_irq_run_cb(struct irq_work *work)
>  {
>  	struct xe_hw_fence_irq *irq = container_of(work, typeof(*irq), work);
> @@ -60,7 +72,9 @@ static void hw_fence_irq_run_cb(struct irq_work *work)
>  			struct dma_fence *dma_fence = &fence->dma;
>  
>  			trace_xe_hw_fence_try_signal(fence);
> -			if (dma_fence_is_signaled_locked(dma_fence)) {
> +			if (dma_fence_test_signaled_flag(dma_fence) ||
> +			    xe_hw_fence_signaled(dma_fence)) {
> +				dma_fence_signal_locked(dma_fence);
>  				trace_xe_hw_fence_signal(fence);
>  				list_del_init(&fence->irq_link);
>  				dma_fence_put(dma_fence);
> @@ -120,8 +134,6 @@ void xe_hw_fence_ctx_finish(struct xe_hw_fence_ctx *ctx)
>  {
>  }
>  
> -static struct xe_hw_fence *to_xe_hw_fence(struct dma_fence *fence);
> -
>  static struct xe_hw_fence_irq *xe_hw_fence_irq(struct xe_hw_fence *fence)
>  {
>  	return container_of(fence->dma.extern_lock, struct xe_hw_fence_irq,
> @@ -142,16 +154,6 @@ static const char *xe_hw_fence_get_timeline_name(struct dma_fence *dma_fence)
>  	return fence->name;
>  }
>  
> -static bool xe_hw_fence_signaled(struct dma_fence *dma_fence)
> -{
> -	struct xe_hw_fence *fence = to_xe_hw_fence(dma_fence);
> -	struct xe_device *xe = fence->xe;
> -	u32 seqno = xe_map_rd(xe, &fence->seqno_map, 0, u32);
> -
> -	return dma_fence->error ||
> -		!__dma_fence_is_later(dma_fence, dma_fence->seqno, seqno);
> -}
> -
>  static bool xe_hw_fence_enable_signaling(struct dma_fence *dma_fence)
>  {
>  	struct xe_hw_fence *fence = to_xe_hw_fence(dma_fence);
> -- 
> 2.43.0
> 

^ permalink raw reply

* Re: [PATCH 01/10] dma-buf: rename dma_fence_enable_sw_signaling
From: Matthew Brost @ 2026-06-25  0:39 UTC (permalink / raw)
  To: christian.koenig
  Cc: phasta, simona, sumit.semwal, tvrtko.ursulin, dakr, dri-devel,
	linux-media, linaro-mm-sig
In-Reply-To: <20260624122917.2483-2-christian.koenig@amd.com>

On Wed, Jun 24, 2026 at 01:13:25PM +0200, Christian König wrote:
> Dropping the _sw_ part from the names was proposed multiple times now and
> IIRC people generally agreed with the idea already.

I certainly agree.

Reviewed-by: Matthew Brost <matthew.brost@intel.com>

> 
> The function requests a fence to signal and triggers some sort of HW
> interaction on most backends.
> 
> So this is not really software related at all and the callback is already
> just named enable_signaling as well.
> 
> Just streamline that and use a consistent name everywhere.
> 
> Assisted-by: Claude Sonet 4
> Signed-off-by: Christian König <christian.koenig@amd.com>
> ---
>  drivers/dma-buf/dma-fence.c                   |  8 ++--
>  drivers/dma-buf/st-dma-fence-chain.c          |  4 +-
>  drivers/dma-buf/st-dma-fence-unwrap.c         | 42 +++++++++----------
>  drivers/dma-buf/st-dma-fence.c                | 16 +++----
>  drivers/dma-buf/st-dma-resv.c                 | 10 ++---
>  drivers/gpu/drm/i915/i915_active.c            |  2 +-
>  .../gpu/drm/ttm/tests/ttm_bo_validate_test.c  |  2 +-
>  drivers/gpu/drm/ttm/ttm_bo.c                  |  2 +-
>  drivers/gpu/drm/xe/xe_bo.c                    |  2 +-
>  drivers/gpu/drm/xe/xe_sched_job.c             |  2 +-
>  drivers/gpu/drm/xe/xe_svm.c                   |  2 +-
>  drivers/gpu/drm/xe/xe_userptr.c               |  2 +-
>  drivers/gpu/drm/xe/xe_vm.c                    |  4 +-
>  include/linux/dma-fence.h                     |  4 +-
>  14 files changed, 51 insertions(+), 51 deletions(-)
> 
> diff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c
> index c7ea1e75d38a..0ec81a568bbd 100644
> --- a/drivers/dma-buf/dma-fence.c
> +++ b/drivers/dma-buf/dma-fence.c
> @@ -534,7 +534,7 @@ dma_fence_wait_timeout(struct dma_fence *fence, bool intr, signed long timeout)
>  
>  	__dma_fence_might_wait();
>  
> -	dma_fence_enable_sw_signaling(fence);
> +	dma_fence_enable_signaling(fence);
>  
>  	rcu_read_lock();
>  	ops = rcu_dereference(fence->ops);
> @@ -656,14 +656,14 @@ static bool __dma_fence_enable_signaling(struct dma_fence *fence)
>  }
>  
>  /**
> - * dma_fence_enable_sw_signaling - enable signaling on fence
> + * dma_fence_enable_signaling - enable signaling on fence
>   * @fence: the fence to enable
>   *
>   * This will request for sw signaling to be enabled, to make the fence
>   * complete as soon as possible. This calls &dma_fence_ops.enable_signaling
>   * internally.
>   */
> -void dma_fence_enable_sw_signaling(struct dma_fence *fence)
> +void dma_fence_enable_signaling(struct dma_fence *fence)
>  {
>  	unsigned long flags;
>  
> @@ -671,7 +671,7 @@ void dma_fence_enable_sw_signaling(struct dma_fence *fence)
>  	__dma_fence_enable_signaling(fence);
>  	dma_fence_unlock_irqrestore(fence, flags);
>  }
> -EXPORT_SYMBOL(dma_fence_enable_sw_signaling);
> +EXPORT_SYMBOL(dma_fence_enable_signaling);
>  
>  /**
>   * dma_fence_add_callback - add a callback to be called when the fence
> diff --git a/drivers/dma-buf/st-dma-fence-chain.c b/drivers/dma-buf/st-dma-fence-chain.c
> index a3023d3fedc9..e0d9b69bfa76 100644
> --- a/drivers/dma-buf/st-dma-fence-chain.c
> +++ b/drivers/dma-buf/st-dma-fence-chain.c
> @@ -82,7 +82,7 @@ static void test_sanitycheck(struct kunit *test)
>  
>  	chain = mock_chain(NULL, f, 1);
>  	if (chain)
> -		dma_fence_enable_sw_signaling(chain);
> +		dma_fence_enable_signaling(chain);
>  	else
>  		KUNIT_FAIL(test, "Failed to create chain");
>  
> @@ -139,7 +139,7 @@ static int fence_chains_init(struct fence_chains *fc, unsigned int count,
>  
>  		fc->tail = fc->chains[i];
>  
> -		dma_fence_enable_sw_signaling(fc->chains[i]);
> +		dma_fence_enable_signaling(fc->chains[i]);
>  	}
>  
>  	fc->chain_length = i;
> diff --git a/drivers/dma-buf/st-dma-fence-unwrap.c b/drivers/dma-buf/st-dma-fence-unwrap.c
> index 4e7ee25372ba..4d9d313b460c 100644
> --- a/drivers/dma-buf/st-dma-fence-unwrap.c
> +++ b/drivers/dma-buf/st-dma-fence-unwrap.c
> @@ -103,7 +103,7 @@ static void test_sanitycheck(struct kunit *test)
>  	f = mock_fence();
>  	KUNIT_ASSERT_NOT_NULL(test, f);
>  
> -	dma_fence_enable_sw_signaling(f);
> +	dma_fence_enable_signaling(f);
>  
>  	array = mock_array(1, f);
>  	KUNIT_ASSERT_NOT_NULL(test, array);
> @@ -122,7 +122,7 @@ static void test_unwrap_array(struct kunit *test)
>  	f1 = mock_fence();
>  	KUNIT_ASSERT_NOT_NULL(test, f1);
>  
> -	dma_fence_enable_sw_signaling(f1);
> +	dma_fence_enable_signaling(f1);
>  
>  	f2 = mock_fence();
>  	if (!f2) {
> @@ -131,7 +131,7 @@ static void test_unwrap_array(struct kunit *test)
>  		return;
>  	}
>  
> -	dma_fence_enable_sw_signaling(f2);
> +	dma_fence_enable_signaling(f2);
>  
>  	array = mock_array(2, f1, f2);
>  	KUNIT_ASSERT_NOT_NULL(test, array);
> @@ -160,7 +160,7 @@ static void test_unwrap_chain(struct kunit *test)
>  	f1 = mock_fence();
>  	KUNIT_ASSERT_NOT_NULL(test, f1);
>  
> -	dma_fence_enable_sw_signaling(f1);
> +	dma_fence_enable_signaling(f1);
>  
>  	f2 = mock_fence();
>  	if (!f2) {
> @@ -169,7 +169,7 @@ static void test_unwrap_chain(struct kunit *test)
>  		return;
>  	}
>  
> -	dma_fence_enable_sw_signaling(f2);
> +	dma_fence_enable_signaling(f2);
>  
>  	chain = mock_chain(f1, f2);
>  	KUNIT_ASSERT_NOT_NULL(test, chain);
> @@ -198,7 +198,7 @@ static void test_unwrap_chain_array(struct kunit *test)
>  	f1 = mock_fence();
>  	KUNIT_ASSERT_NOT_NULL(test, f1);
>  
> -	dma_fence_enable_sw_signaling(f1);
> +	dma_fence_enable_signaling(f1);
>  
>  	f2 = mock_fence();
>  	if (!f2) {
> @@ -207,7 +207,7 @@ static void test_unwrap_chain_array(struct kunit *test)
>  		return;
>  	}
>  
> -	dma_fence_enable_sw_signaling(f2);
> +	dma_fence_enable_signaling(f2);
>  
>  	array = mock_array(2, f1, f2);
>  	KUNIT_ASSERT_NOT_NULL(test, array);
> @@ -239,7 +239,7 @@ static void test_unwrap_merge(struct kunit *test)
>  	f1 = mock_fence();
>  	KUNIT_ASSERT_NOT_NULL(test, f1);
>  
> -	dma_fence_enable_sw_signaling(f1);
> +	dma_fence_enable_signaling(f1);
>  
>  	f2 = mock_fence();
>  	if (!f2) {
> @@ -247,7 +247,7 @@ static void test_unwrap_merge(struct kunit *test)
>  		goto error_put_f1;
>  	}
>  
> -	dma_fence_enable_sw_signaling(f2);
> +	dma_fence_enable_signaling(f2);
>  
>  	f3 = dma_fence_unwrap_merge(f1, f2);
>  	if (!f3) {
> @@ -285,7 +285,7 @@ static void test_unwrap_merge_duplicate(struct kunit *test)
>  	f1 = mock_fence();
>  	KUNIT_ASSERT_NOT_NULL(test, f1);
>  
> -	dma_fence_enable_sw_signaling(f1);
> +	dma_fence_enable_signaling(f1);
>  
>  	f2 = dma_fence_unwrap_merge(f1, f1);
>  	if (!f2) {
> @@ -322,7 +322,7 @@ static void test_unwrap_merge_seqno(struct kunit *test)
>  	f1 = __mock_fence(ctx[1], 1);
>  	KUNIT_ASSERT_NOT_NULL(test, f1);
>  
> -	dma_fence_enable_sw_signaling(f1);
> +	dma_fence_enable_signaling(f1);
>  
>  	f2 = __mock_fence(ctx[1], 2);
>  	if (!f2) {
> @@ -330,7 +330,7 @@ static void test_unwrap_merge_seqno(struct kunit *test)
>  		goto error_put_f1;
>  	}
>  
> -	dma_fence_enable_sw_signaling(f2);
> +	dma_fence_enable_signaling(f2);
>  
>  	f3 = __mock_fence(ctx[0], 1);
>  	if (!f3) {
> @@ -338,7 +338,7 @@ static void test_unwrap_merge_seqno(struct kunit *test)
>  		goto error_put_f2;
>  	}
>  
> -	dma_fence_enable_sw_signaling(f3);
> +	dma_fence_enable_signaling(f3);
>  
>  	f4 = dma_fence_unwrap_merge(f1, f2, f3);
>  	if (!f4) {
> @@ -378,7 +378,7 @@ static void test_unwrap_merge_order(struct kunit *test)
>  	f1 = mock_fence();
>  	KUNIT_ASSERT_NOT_NULL(test, f1);
>  
> -	dma_fence_enable_sw_signaling(f1);
> +	dma_fence_enable_signaling(f1);
>  
>  	f2 = mock_fence();
>  	if (!f2) {
> @@ -387,7 +387,7 @@ static void test_unwrap_merge_order(struct kunit *test)
>  		return;
>  	}
>  
> -	dma_fence_enable_sw_signaling(f2);
> +	dma_fence_enable_signaling(f2);
>  
>  	a1 = mock_array(2, f1, f2);
>  	KUNIT_ASSERT_NOT_NULL(test, a1);
> @@ -442,7 +442,7 @@ static void test_unwrap_merge_complex(struct kunit *test)
>  	f1 = mock_fence();
>  	KUNIT_ASSERT_NOT_NULL(test, f1);
>  
> -	dma_fence_enable_sw_signaling(f1);
> +	dma_fence_enable_signaling(f1);
>  
>  	f2 = mock_fence();
>  	if (!f2) {
> @@ -450,7 +450,7 @@ static void test_unwrap_merge_complex(struct kunit *test)
>  		goto error_put_f1;
>  	}
>  
> -	dma_fence_enable_sw_signaling(f2);
> +	dma_fence_enable_signaling(f2);
>  
>  	f3 = dma_fence_unwrap_merge(f1, f2);
>  	if (!f3) {
> @@ -510,7 +510,7 @@ static void test_unwrap_merge_complex_seqno(struct kunit *test)
>  	f1 = __mock_fence(ctx[0], 2);
>  	KUNIT_ASSERT_NOT_NULL(test, f1);
>  
> -	dma_fence_enable_sw_signaling(f1);
> +	dma_fence_enable_signaling(f1);
>  
>  	f2 = __mock_fence(ctx[1], 1);
>  	if (!f2) {
> @@ -518,7 +518,7 @@ static void test_unwrap_merge_complex_seqno(struct kunit *test)
>  		goto error_put_f1;
>  	}
>  
> -	dma_fence_enable_sw_signaling(f2);
> +	dma_fence_enable_signaling(f2);
>  
>  	f3 = __mock_fence(ctx[0], 1);
>  	if (!f3) {
> @@ -526,7 +526,7 @@ static void test_unwrap_merge_complex_seqno(struct kunit *test)
>  		goto error_put_f2;
>  	}
>  
> -	dma_fence_enable_sw_signaling(f3);
> +	dma_fence_enable_signaling(f3);
>  
>  	f4 = __mock_fence(ctx[1], 2);
>  	if (!f4) {
> @@ -534,7 +534,7 @@ static void test_unwrap_merge_complex_seqno(struct kunit *test)
>  		goto error_put_f3;
>  	}
>  
> -	dma_fence_enable_sw_signaling(f4);
> +	dma_fence_enable_signaling(f4);
>  
>  	f5 = mock_array(2, dma_fence_get(f1), dma_fence_get(f2));
>  	if (!f5) {
> diff --git a/drivers/dma-buf/st-dma-fence.c b/drivers/dma-buf/st-dma-fence.c
> index 499272229696..856d0d302a5d 100644
> --- a/drivers/dma-buf/st-dma-fence.c
> +++ b/drivers/dma-buf/st-dma-fence.c
> @@ -42,7 +42,7 @@ static void test_sanitycheck(struct kunit *test)
>  	f = mock_fence();
>  	KUNIT_ASSERT_NOT_NULL(test, f);
>  
> -	dma_fence_enable_sw_signaling(f);
> +	dma_fence_enable_signaling(f);
>  
>  	dma_fence_signal(f);
>  	dma_fence_put(f);
> @@ -55,7 +55,7 @@ static void test_signaling(struct kunit *test)
>  	f = mock_fence();
>  	KUNIT_ASSERT_NOT_NULL(test, f);
>  
> -	dma_fence_enable_sw_signaling(f);
> +	dma_fence_enable_signaling(f);
>  
>  	if (dma_fence_is_signaled(f)) {
>  		KUNIT_FAIL(test, "Fence unexpectedly signaled on creation");
> @@ -127,7 +127,7 @@ static void test_late_add_callback(struct kunit *test)
>  	f = mock_fence();
>  	KUNIT_ASSERT_NOT_NULL(test, f);
>  
> -	dma_fence_enable_sw_signaling(f);
> +	dma_fence_enable_signaling(f);
>  
>  	dma_fence_signal(f);
>  
> @@ -209,7 +209,7 @@ static void test_status(struct kunit *test)
>  	f = mock_fence();
>  	KUNIT_ASSERT_NOT_NULL(test, f);
>  
> -	dma_fence_enable_sw_signaling(f);
> +	dma_fence_enable_signaling(f);
>  
>  	if (dma_fence_get_status(f)) {
>  		KUNIT_FAIL(test, "Fence unexpectedly has signaled status on creation");
> @@ -233,7 +233,7 @@ static void test_error(struct kunit *test)
>  	f = mock_fence();
>  	KUNIT_ASSERT_NOT_NULL(test, f);
>  
> -	dma_fence_enable_sw_signaling(f);
> +	dma_fence_enable_signaling(f);
>  
>  	dma_fence_set_error(f, -EIO);
>  
> @@ -260,7 +260,7 @@ static void test_wait(struct kunit *test)
>  	f = mock_fence();
>  	KUNIT_ASSERT_NOT_NULL(test, f);
>  
> -	dma_fence_enable_sw_signaling(f);
> +	dma_fence_enable_signaling(f);
>  
>  	if (dma_fence_wait_timeout(f, false, 0) != 0) {
>  		KUNIT_FAIL(test, "Wait reported complete before being signaled");
> @@ -300,7 +300,7 @@ static void test_wait_timeout(struct kunit *test)
>  	wt.f = mock_fence();
>  	KUNIT_ASSERT_NOT_NULL(test, wt.f);
>  
> -	dma_fence_enable_sw_signaling(wt.f);
> +	dma_fence_enable_signaling(wt.f);
>  
>  	if (dma_fence_wait_timeout(wt.f, false, 1) != 0) {
>  		KUNIT_FAIL(test, "Wait reported complete before being signaled");
> @@ -379,7 +379,7 @@ static int thread_signal_callback(void *arg)
>  			break;
>  		}
>  
> -		dma_fence_enable_sw_signaling(f1);
> +		dma_fence_enable_signaling(f1);
>  
>  		rcu_assign_pointer(t->fences[t->id], f1);
>  		smp_wmb();
> diff --git a/drivers/dma-buf/st-dma-resv.c b/drivers/dma-buf/st-dma-resv.c
> index 95a4becdb892..0b96136bbd54 100644
> --- a/drivers/dma-buf/st-dma-resv.c
> +++ b/drivers/dma-buf/st-dma-resv.c
> @@ -48,7 +48,7 @@ static void test_sanitycheck(struct kunit *test)
>  	f = alloc_fence();
>  	KUNIT_ASSERT_NOT_NULL(test, f);
>  
> -	dma_fence_enable_sw_signaling(f);
> +	dma_fence_enable_signaling(f);
>  
>  	dma_fence_signal(f);
>  	dma_fence_put(f);
> @@ -73,7 +73,7 @@ static void test_signaling(struct kunit *test)
>  	f = alloc_fence();
>  	KUNIT_ASSERT_NOT_NULL(test, f);
>  
> -	dma_fence_enable_sw_signaling(f);
> +	dma_fence_enable_signaling(f);
>  
>  	dma_resv_init(&resv);
>  	r = dma_resv_lock(&resv, NULL);
> @@ -117,7 +117,7 @@ static void test_for_each(struct kunit *test)
>  	f = alloc_fence();
>  	KUNIT_ASSERT_NOT_NULL(test, f);
>  
> -	dma_fence_enable_sw_signaling(f);
> +	dma_fence_enable_signaling(f);
>  
>  	dma_resv_init(&resv);
>  	r = dma_resv_lock(&resv, NULL);
> @@ -176,7 +176,7 @@ static void test_for_each_unlocked(struct kunit *test)
>  	f = alloc_fence();
>  	KUNIT_ASSERT_NOT_NULL(test, f);
>  
> -	dma_fence_enable_sw_signaling(f);
> +	dma_fence_enable_signaling(f);
>  
>  	dma_resv_init(&resv);
>  	r = dma_resv_lock(&resv, NULL);
> @@ -246,7 +246,7 @@ static void test_get_fences(struct kunit *test)
>  	f = alloc_fence();
>  	KUNIT_ASSERT_NOT_NULL(test, f);
>  
> -	dma_fence_enable_sw_signaling(f);
> +	dma_fence_enable_signaling(f);
>  
>  	dma_resv_init(&resv);
>  	r = dma_resv_lock(&resv, NULL);
> diff --git a/drivers/gpu/drm/i915/i915_active.c b/drivers/gpu/drm/i915/i915_active.c
> index 5cb7a72774a0..e7632c1ff4be 100644
> --- a/drivers/gpu/drm/i915/i915_active.c
> +++ b/drivers/gpu/drm/i915/i915_active.c
> @@ -543,7 +543,7 @@ static void enable_signaling(struct i915_active_fence *active)
>  	if (!fence)
>  		return;
>  
> -	dma_fence_enable_sw_signaling(fence);
> +	dma_fence_enable_signaling(fence);
>  	dma_fence_put(fence);
>  }
>  
> diff --git a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> index 2db221f6fc3a..56ad8ef32584 100644
> --- a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> +++ b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
> @@ -69,7 +69,7 @@ static void dma_resv_kunit_active_fence_init(struct kunit *test,
>  	struct dma_fence *fence;
>  
>  	fence = alloc_mock_fence(test);
> -	dma_fence_enable_sw_signaling(fence);
> +	dma_fence_enable_signaling(fence);
>  
>  	dma_resv_lock(resv, NULL);
>  	dma_resv_reserve_fences(resv, 1);
> diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
> index bcd76f6bb7f0..3980f376e3ba 100644
> --- a/drivers/gpu/drm/ttm/ttm_bo.c
> +++ b/drivers/gpu/drm/ttm/ttm_bo.c
> @@ -224,7 +224,7 @@ static void ttm_bo_flush_all_fences(struct ttm_buffer_object *bo)
>  
>  	dma_resv_iter_begin(&cursor, resv, DMA_RESV_USAGE_BOOKKEEP);
>  	dma_resv_for_each_fence_unlocked(&cursor, fence)
> -		dma_fence_enable_sw_signaling(fence);
> +		dma_fence_enable_signaling(fence);
>  	dma_resv_iter_end(&cursor);
>  }
>  
> diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c
> index 4c80bac67622..85e6d9a0f575 100644
> --- a/drivers/gpu/drm/xe/xe_bo.c
> +++ b/drivers/gpu/drm/xe/xe_bo.c
> @@ -670,7 +670,7 @@ static int xe_bo_trigger_rebind(struct xe_device *xe, struct xe_bo *bo,
>  		dma_resv_iter_begin(&cursor, bo->ttm.base.resv,
>  				    DMA_RESV_USAGE_BOOKKEEP);
>  		dma_resv_for_each_fence_unlocked(&cursor, fence)
> -			dma_fence_enable_sw_signaling(fence);
> +			dma_fence_enable_signaling(fence);
>  		dma_resv_iter_end(&cursor);
>  	}
>  
> diff --git a/drivers/gpu/drm/xe/xe_sched_job.c b/drivers/gpu/drm/xe/xe_sched_job.c
> index ae5b38b2a884..a4fa00632a30 100644
> --- a/drivers/gpu/drm/xe/xe_sched_job.c
> +++ b/drivers/gpu/drm/xe/xe_sched_job.c
> @@ -214,7 +214,7 @@ void xe_sched_job_set_error(struct xe_sched_job *job, int error)
>  
>  	trace_xe_sched_job_set_error(job);
>  
> -	dma_fence_enable_sw_signaling(job->fence);
> +	dma_fence_enable_signaling(job->fence);
>  	xe_hw_fence_irq_run(job->q->fence_irq);
>  }
>  
> diff --git a/drivers/gpu/drm/xe/xe_svm.c b/drivers/gpu/drm/xe/xe_svm.c
> index e1651e70c8f0..dba73786d82a 100644
> --- a/drivers/gpu/drm/xe/xe_svm.c
> +++ b/drivers/gpu/drm/xe/xe_svm.c
> @@ -1090,7 +1090,7 @@ static int xe_drm_pagemap_populate_mm(struct drm_pagemap *dpagemap,
>  				dma_resv_wait_timeout(bo->ttm.base.resv, DMA_RESV_USAGE_KERNEL,
>  						      false, MAX_SCHEDULE_TIMEOUT);
>  			else if (pre_migrate_fence)
> -				dma_fence_enable_sw_signaling(pre_migrate_fence);
> +				dma_fence_enable_signaling(pre_migrate_fence);
>  		}
>  
>  		drm_pagemap_devmem_init(&bo->devmem_allocation, dev, mm,
> diff --git a/drivers/gpu/drm/xe/xe_userptr.c b/drivers/gpu/drm/xe/xe_userptr.c
> index 6761005c0b90..2e45e42c648f 100644
> --- a/drivers/gpu/drm/xe/xe_userptr.c
> +++ b/drivers/gpu/drm/xe/xe_userptr.c
> @@ -180,7 +180,7 @@ xe_vma_userptr_invalidate_pass1(struct xe_vm *vm, struct xe_userptr_vma *uvma)
>  	dma_resv_iter_begin(&cursor, xe_vm_resv(vm),
>  			    DMA_RESV_USAGE_BOOKKEEP);
>  	dma_resv_for_each_fence_unlocked(&cursor, fence) {
> -		dma_fence_enable_sw_signaling(fence);
> +		dma_fence_enable_signaling(fence);
>  		if (signaled && !dma_fence_is_signaled(fence))
>  			signaled = false;
>  	}
> diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
> index 080c2fff0e95..73ac031ffb04 100644
> --- a/drivers/gpu/drm/xe/xe_vm.c
> +++ b/drivers/gpu/drm/xe/xe_vm.c
> @@ -256,7 +256,7 @@ int xe_vm_add_compute_exec_queue(struct xe_vm *vm, struct xe_exec_queue *q)
>  	 */
>  	wait = __xe_vm_userptr_needs_repin(vm) || preempt_fences_waiting(vm);
>  	if (wait)
> -		dma_fence_enable_sw_signaling(pfence);
> +		dma_fence_enable_signaling(pfence);
>  
>  	xe_svm_notifier_unlock(vm);
>  
> @@ -287,7 +287,7 @@ void xe_vm_remove_compute_exec_queue(struct xe_vm *vm, struct xe_exec_queue *q)
>  		--vm->preempt.num_exec_queues;
>  	}
>  	if (q->lr.pfence) {
> -		dma_fence_enable_sw_signaling(q->lr.pfence);
> +		dma_fence_enable_signaling(q->lr.pfence);
>  		dma_fence_put(q->lr.pfence);
>  		q->lr.pfence = NULL;
>  	}
> diff --git a/include/linux/dma-fence.h b/include/linux/dma-fence.h
> index b52ab692b22e..158cd609f103 100644
> --- a/include/linux/dma-fence.h
> +++ b/include/linux/dma-fence.h
> @@ -448,7 +448,7 @@ int dma_fence_add_callback(struct dma_fence *fence,
>  			   dma_fence_func_t func);
>  bool dma_fence_remove_callback(struct dma_fence *fence,
>  			       struct dma_fence_cb *cb);
> -void dma_fence_enable_sw_signaling(struct dma_fence *fence);
> +void dma_fence_enable_signaling(struct dma_fence *fence);
>  
>  /**
>   * DOC: Safe external access to driver provided object members
> @@ -534,7 +534,7 @@ dma_fence_is_signaled_locked(struct dma_fence *fence)
>   * Returns true if the fence was already signaled, false if not. Since this
>   * function doesn't enable signaling, it is not guaranteed to ever return
>   * true if dma_fence_add_callback(), dma_fence_wait() or
> - * dma_fence_enable_sw_signaling() haven't been called before.
> + * dma_fence_enable_signaling() haven't been called before.
>   *
>   * It's recommended for seqno fences to call dma_fence_signal when the
>   * operation is complete, it makes it possible to prevent issues from
> -- 
> 2.43.0
> 

^ permalink raw reply

* [PATCH] media: mediatek: jpeg: retry HW selection after successful wait
From: Pengpeng Hou @ 2026-06-25  0:31 UTC (permalink / raw)
  To: Bin Liu
  Cc: pengpeng, Mauro Carvalho Chehab, Matthias Brugger,
	AngeloGioacchino Del Regno, linux-media, linux-kernel,
	linux-arm-kernel, linux-mediatek

wait_event_interruptible_timeout() returns a positive value when the
condition becomes true before the timeout expires.

mtk_jpegdec_worker() treats every non-zero return value as a failure and
finishes the mem2mem job with an error.  A normal wakeup that indicates
that a decode engine is ready can therefore be handled as if all hardware
remained busy.

Only fail immediately on interrupted waits.  Keep the existing retry limit
for real timeouts, and retry hardware selection after a successful wait.

Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
---
 drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
index d147ec483..391db77a6 100644
--- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
+++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
@@ -1697,7 +1697,7 @@ static void mtk_jpegdec_worker(struct work_struct *work)
 		ret = wait_event_interruptible_timeout(jpeg->hw_wq,
 						       atomic_read(&jpeg->hw_rdy) > 0,
 						       MTK_JPEG_HW_TIMEOUT_MSEC);
-		if (ret != 0 || (i++ > MTK_JPEG_MAX_RETRY_TIME)) {
+		if (ret < 0 || (!ret && i++ > MTK_JPEG_MAX_RETRY_TIME)) {
 			dev_err(jpeg->dev, "%s : %d, all HW are busy\n",
 				__func__, __LINE__);
 			v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx);
-- 
2.50.1 (Apple Git-155)


^ permalink raw reply related

* Re: [PATCH v2 0/4] media: add and use fwnode_graph_for_each_endpoint_scoped()
From: Laurent Pinchart @ 2026-06-24 22:20 UTC (permalink / raw)
  To: Frank Li
  Cc: Andy Shevchenko, Daniel Scally, Heikki Krogerus, Sakari Ailus,
	Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
	Mauro Carvalho Chehab, Dafna Hirschfeld, Heiko Stuebner,
	Bryan O'Donoghue, Vladimir Zapolskiy, Loic Poulain,
	driver-core, linux-acpi, linux-kernel, linux-media,
	linux-rockchip, linux-arm-kernel, linux-arm-msm, imx, Guoniu Zhou,
	Frank Li, Guoniu Zhou
In-Reply-To: <ajxCOE3avXXLlrfT@SMW015318>

On Wed, Jun 24, 2026 at 03:46:48PM -0500, Frank Li wrote:
> On Wed, Jun 24, 2026 at 11:02:37PM +0300, Laurent Pinchart wrote:
> > On Wed, Jun 24, 2026 at 02:35:14PM -0500, Frank Li wrote:
> > > On Wed, Jun 24, 2026 at 10:19:35PM +0300, Laurent Pinchart wrote:
> > > > On Wed, Jun 24, 2026 at 01:00:08PM -0400, Frank.Li@oss.nxp.com wrote:
> > > > > Add new helper macro fwnode_graph_for_each_endpoint_scoped() and use it
> > > > > simplify media code.
> > > > >
> > > > > Typical example should qualcomm's driver (camss.c), the v4l2_mc.c and
> > > > > rkisp1-dev.c only silience improvement.
> > > > >
> > > > > Anyways, *_for_each_*_scoped() already use widely and make code clean.
> > > > >
> > > > > Build test only.
> > > > >
> > > > > Sakari Ailus:
> > > > > 	when I try to improve the patch
> > > > > "Add common helper library for 1-to-1 subdev registration", I found need
> > > > > camss.c pattern, so I create this small improvement firstly.
> > > >
> > > > Those are nice cleanups, thank you.
> > > >
> > > > After applying this series, the only left users of the
> > > > fwnode_graph_for_each_endpoint() macro are in drivers/base/property.c.
> > >
> > > I already checked previously, two place use it.
> > >
> > > fwnode_graph_get_endpoint_count(), it will go though all endpoints, last
> > > ep is NULL, which totally equial to scoped() version.
> > >
> > > another one fwnode_graph_get_endpoint_by_id(), which return ep, expect
> > > caller to call put().
> > >
> > > if use scoped() version, need use no_free_ptr() at return, which make think
> > > a little bit complex.
> >
> > It would introduce a tiny bit of extra complexity there, but the
> > advantage (in my opinion) is that we'll be able to remove the less safe
> > fwnode_graph_for_each_endpoint() macro.
> >
> > Now one may argue that the risk of
> > fwnode_graph_for_each_endpoint_scoped() is returning the iterator
> > without using no_free_ptr(). I wonder if that would be easier to catch
> > in static analysis tools than the current pattern that leaks a reference
> > when exiting the loop early.
> 
> It's not big deal, if everyone prefer drop fwnode_graph_for_each_endpoint(),
> I can do it.

Let's see what others think. If people prefer keeping both versions,
I'll be OK with that.

> > > It'd better leave these as it.

-- 
Regards,

Laurent Pinchart

^ permalink raw reply

* [PATCH v2] dma-buf: udmabuf: avoid list copy size overflow
From: Yousef Alhouseen @ 2026-06-24 21:54 UTC (permalink / raw)
  To: Gerd Hoffmann, Vivek Kasireddy, Sumit Semwal,
	Christian König
  Cc: dri-devel, linux-media, linaro-mm-sig, linux-kernel,
	Yousef Alhouseen

UDMABUF_CREATE_LIST copies an array whose element count comes from
userspace. The count is bounded by the list_limit module parameter, but
that parameter does not need negative values.

Make list_limit unsigned so its type matches the u32 count field. Also
use memdup_array_user() for the list copy so the element-count
multiplication is checked before allocation and copying.

Suggested-by: Christian König <christian.koenig@amd.com>
Signed-off-by: Yousef Alhouseen <alhouseenyousef@gmail.com>
---
Changes in v2:
- Make list_limit unsigned as suggested by Christian.
- Keep the checked array copy and drop the local u32 byte-count temporary.

 drivers/dma-buf/udmabuf.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c
index bced421c0..e34a3b135 100644
--- a/drivers/dma-buf/udmabuf.c
+++ b/drivers/dma-buf/udmabuf.c
@@ -21,8 +21,8 @@
 #include <linux/udmabuf.h>
 #include <linux/vmalloc.h>

-static int list_limit = 1024;
-module_param(list_limit, int, 0644);
+static unsigned int list_limit = 1024;
+module_param(list_limit, uint, 0644);
 MODULE_PARM_DESC(list_limit, "udmabuf_create_list->count limit.
Default is 1024.");
 static int size_limit_mb = 64;
 module_param(size_limit_mb, int, 0644);
@@ -471,12 +471,11 @@ static long udmabuf_ioctl_create_list(struct
file *filp, unsigned long arg)
 	struct udmabuf_create_list head;
 	struct udmabuf_create_item *list;
 	int ret = -EINVAL;
-	u32 lsize;

 	if (copy_from_user(&head, (void __user *)arg, sizeof(head)))
 		return -EFAULT;
 	if (head.count > list_limit)
 		return -EINVAL;
-	lsize = sizeof(struct udmabuf_create_item) * head.count;
-	list = memdup_user((void __user *)(arg + sizeof(head)), lsize);
+	list = memdup_array_user((void __user *)(arg + sizeof(head)),
+				      head.count, sizeof(*list));
 	if (IS_ERR(list))
 		return PTR_ERR(list);

-- 
2.54.0

^ permalink raw reply related

* Re: [PATCH v2 0/4] media: add and use fwnode_graph_for_each_endpoint_scoped()
From: Frank Li @ 2026-06-24 20:46 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Andy Shevchenko, Daniel Scally, Heikki Krogerus, Sakari Ailus,
	Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
	Mauro Carvalho Chehab, Dafna Hirschfeld, Heiko Stuebner,
	Bryan O'Donoghue, Vladimir Zapolskiy, Loic Poulain,
	driver-core, linux-acpi, linux-kernel, linux-media,
	linux-rockchip, linux-arm-kernel, linux-arm-msm, imx, Guoniu Zhou,
	Frank Li, Guoniu Zhou
In-Reply-To: <20260624200237.GJ851255@killaraus.ideasonboard.com>

On Wed, Jun 24, 2026 at 11:02:37PM +0300, Laurent Pinchart wrote:
> On Wed, Jun 24, 2026 at 02:35:14PM -0500, Frank Li wrote:
> > On Wed, Jun 24, 2026 at 10:19:35PM +0300, Laurent Pinchart wrote:
> > > On Wed, Jun 24, 2026 at 01:00:08PM -0400, Frank.Li@oss.nxp.com wrote:
> > > > Add new helper macro fwnode_graph_for_each_endpoint_scoped() and use it
> > > > simplify media code.
> > > >
> > > > Typical example should qualcomm's driver (camss.c), the v4l2_mc.c and
> > > > rkisp1-dev.c only silience improvement.
> > > >
> > > > Anyways, *_for_each_*_scoped() already use widely and make code clean.
> > > >
> > > > Build test only.
> > > >
> > > > Sakari Ailus:
> > > > 	when I try to improve the patch
> > > > "Add common helper library for 1-to-1 subdev registration", I found need
> > > > camss.c pattern, so I create this small improvement firstly.
> > >
> > > Those are nice cleanups, thank you.
> > >
> > > After applying this series, the only left users of the
> > > fwnode_graph_for_each_endpoint() macro are in drivers/base/property.c.
> >
> > I already checked previously, two place use it.
> >
> > fwnode_graph_get_endpoint_count(), it will go though all endpoints, last
> > ep is NULL, which totally equial to scoped() version.
> >
> > another one fwnode_graph_get_endpoint_by_id(), which return ep, expect
> > caller to call put().
> >
> > if use scoped() version, need use no_free_ptr() at return, which make think
> > a little bit complex.
>
> It would introduce a tiny bit of extra complexity there, but the
> advantage (in my opinion) is that we'll be able to remove the less safe
> fwnode_graph_for_each_endpoint() macro.
>
> Now one may argue that the risk of
> fwnode_graph_for_each_endpoint_scoped() is returning the iterator
> without using no_free_ptr(). I wonder if that would be easier to catch
> in static analysis tools than the current pattern that leaks a reference
> when exiting the loop early.

It's not big deal, if everyone prefer drop fwnode_graph_for_each_endpoint(),
I can do it.

Frank

>
> > It'd better leave these as it.
>
> --
> Regards,
>
> Laurent Pinchart

^ permalink raw reply

* [PATCH v6 9/9] arm64: dts: imx8qxp-mek: add parallel ov5640 camera support
From: Frank.Li @ 2026-06-24 20:37 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, Guoniu Zhou, devicetree,
	linux-arm-kernel
In-Reply-To: <20260624-imx8qxp_pcam-v6-0-4b3f45920d2f@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.

Reviewed-by: Guoniu Zhou <guoniu.zhou@nxp.com>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
changes in v6
- add Guoniu's reviewed-by tags

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 = <&reg_2v8>;
+		DOVDD-supply = <&reg_1v8>;
+		DVDD-supply = <&reg_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 = <&parallel_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";
+};
+
+&parallel_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 v6 8/9] arm64: dts: imx8: add camera parallel interface (CPI) node
From: Frank.Li @ 2026-06-24 20:37 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, Guoniu Zhou, devicetree,
	linux-arm-kernel
In-Reply-To: <20260624-imx8qxp_pcam-v6-0-4b3f45920d2f@nxp.com>

From: Frank Li <Frank.Li@nxp.com>

Add camera parallel interface (CPI) node.

Reviewed-by: Guoniu Zhou <guoniu.zhou@nxp.com>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
changes in v6
- add Guoniu Zhou's review by

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 = <&parallel_cpi_out>;
+			};
+		};
 	};
 };
 
@@ -95,3 +103,22 @@ &jpegenc {
 &mipi_csi_1 {
 	status = "disabled";
 };
+
+&parallel_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 v6 7/9] media: nxp: add V4L2 subdev driver for camera parallel interface (CPI)
From: Frank.Li @ 2026-06-24 20:37 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, Guoniu Zhou, devicetree,
	linux-arm-kernel, Alice Yuan, Robert Chiras, Zhipeng Wang
In-Reply-To: <20260624-imx8qxp_pcam-v6-0-4b3f45920d2f@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 v6
- Don't use subdev_1to1
- use new api media_async_register_subdev()
- remove used switch -case

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 | 629 ++++++++++++++++++++++++++
 4 files changed, 643 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 51d5c62e3fdea..045a06d0bb216 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..0f74b51608715
--- /dev/null
+++ b/drivers/media/platform/nxp/imx-parallel-cpi.c
@@ -0,0 +1,629 @@
+// 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-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 sd;
+
+	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)
+{
+	u32 flags = pcpidev->sd.entity.pads[V4L2_SUBDEV_1TO1_PADS_SINK].vep.bus.parallel.flags;
+	const struct imx_cpi_plat_data *pdata = pcpidev->pdata;
+	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;
+
+	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);
+}
+
+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 int
+imx_cpi_set_pad_by_ep(struct v4l2_subdev *sd, struct media_pad *pad)
+{
+	struct v4l2_fwnode_endpoint *vep = &pad->vep;
+
+	if (vep->base.port == V4L2_SUBDEV_1TO1_PADS_SINK) {
+		/* Sink port */
+
+		if (vep->bus_type != V4L2_MBUS_PARALLEL)
+			return -EINVAL;
+
+		pad->flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
+
+		return 0;
+	}
+
+	if (vep->base.port == V4L2_SUBDEV_1TO1_PADS_SOURCE) {
+		/* Source port */
+		pad->flags = MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+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,
+	.set_pad_by_ep = imx_cpi_set_pad_by_ep,
+};
+
+/* ----------------------------------------------------------------------
+ * 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_cleanup(&pcpidev->sd);
+}
+
+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;
+
+	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;
+
+	pm_runtime_use_autosuspend(dev);
+	ret = devm_pm_runtime_enable(dev);
+	if (ret)
+		return ret;
+
+	return media_async_register_subdev(sd);
+}
+
+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 v6 6/9] dt-bindings: media: add i.MX parallel CPI support
From: Frank.Li @ 2026-06-24 20:37 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, Guoniu Zhou, devicetree,
	linux-arm-kernel, Alice Yuan, Krzysztof Kozlowski
In-Reply-To: <20260624-imx8qxp_pcam-v6-0-4b3f45920d2f@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 895a87b571c35..51d5c62e3fdea 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 v6 5/9] media: synopsys: Use media_async_register_subdev() to simplify code
From: Frank.Li @ 2026-06-24 20:37 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, Guoniu Zhou, devicetree,
	linux-arm-kernel
In-Reply-To: <20260624-imx8qxp_pcam-v6-0-4b3f45920d2f@nxp.com>

From: Frank Li <Frank.Li@nxp.com>

Use the media_async_register_subdev() to simplify the driver.

Replace the local subdev registration and media pad setup code with
media_async_register_subdev(). Reduce boilerplate code and aligns the
driver with the common pattern used by simple subdevices that each media
pad has one endpoint in fwnode.

Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
change in v6
- use media_async_register_subdev()
- remove sd_1to1.

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/dw-mipi-csi2rx.c | 178 +++++------------------
 1 file changed, 40 insertions(+), 138 deletions(-)

diff --git a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
index f51367409ff46..0fabc89a49b80 100644
--- a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
+++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
@@ -78,12 +78,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 +106,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;
 
-	enum v4l2_mbus_type bus_type;
-	u32 lanes_num;
 	u64 enabled_streams;
 
 	const struct dw_mipi_csi2rx_drvdata *drvdata;
@@ -360,9 +350,10 @@ 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)
 {
+	struct media_pad *sink_pad = &csi2->sd.entity.pads[V4L2_SUBDEV_1TO1_PADS_SINK];
+	u32 lanes = sink_pad->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;
@@ -370,8 +361,7 @@ static int dw_mipi_csi2rx_start(struct dw_mipi_csi2rx_device *csi2)
 	if (lanes < 1 || lanes > 4)
 		return -EINVAL;
 
-	source_pad = media_pad_remote_pad_unique(
-		&csi2->pads[DW_MIPI_CSI2RX_PAD_SINK]);
+	source_pad = media_pad_remote_pad_unique(sink_pad);
 	if (IS_ERR(source_pad))
 		return PTR_ERR(source_pad);
 
@@ -380,7 +370,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 (sink_pad->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 +448,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 +477,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 +539,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 +598,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 +639,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,
 		},
@@ -666,95 +656,38 @@ static int dw_mipi_csi2rx_init_state(struct v4l2_subdev *sd,
 						&default_format);
 }
 
-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)
+static int dw_mipi_set_pad_by_ep(struct v4l2_subdev *sd, struct media_pad *pad)
 {
-	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;
-}
+	struct v4l2_fwnode_endpoint *vep = &pad->vep;
 
-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;
+	if (vep->base.port == V4L2_SUBDEV_1TO1_PADS_SINK) {
+		if (vep->bus_type != V4L2_MBUS_CSI2_DPHY &&
+		    vep->bus_type != V4L2_MBUS_CSI2_CPHY)
+			return -EINVAL;
 
-	v4l2_async_subdev_nf_init(ntf, sd);
-	ntf->ops = &dw_mipi_csi2rx_notifier_ops;
+		pad->flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
 
-	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;
+		return 0;
 	}
 
-	ret = v4l2_async_nf_register(ntf);
-	if (ret) {
-		ret = dev_err_probe(dev, ret, "failed to register notifier\n");
-		goto err_nf_cleanup;
+	if (vep->base.port == V4L2_SUBDEV_1TO1_PADS_SOURCE) {
+		pad->flags = MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT;
+		return 0;
 	}
 
-	return 0;
-
-err_nf_cleanup:
-	v4l2_async_nf_cleanup(ntf);
-
-	return ret;
+	return -EINVAL;
 }
 
+static const struct v4l2_subdev_internal_ops dw_mipi_csi2rx_internal_ops = {
+	.init_state = dw_mipi_csi2rx_init_state,
+	.set_pad_by_ep = dw_mipi_set_pad_by_ep,
+};
+
 static int dw_mipi_csi2rx_register(struct dw_mipi_csi2rx_device *csi2)
 {
-	struct media_pad *pads = csi2->pads;
 	struct v4l2_subdev *sd = &csi2->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 +697,12 @@ 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);
+	ret = media_async_register_subdev(&csi2->sd);
 	if (ret)
-		goto err_notifier_unregister;
-
-	ret = v4l2_subdev_init_finalize(sd);
-	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 +779,14 @@ 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)
 {
+	struct media_pad *sink_pad = &csi2->sd.entity.pads[V4L2_SUBDEV_1TO1_PADS_SINK];
+	u32 num_lanes = sink_pad->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 +895,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_cleanup(&csi2->sd);
 	phy_exit(csi2->phy);
 }
 

-- 
2.43.0


^ permalink raw reply related

* [PATCH v6 4/9] media: synopsys: Use v4l2_subdev_get_frame_desc_passthrough()
From: Frank.Li @ 2026-06-24 20:37 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, Guoniu Zhou, devicetree,
	linux-arm-kernel
In-Reply-To: <20260624-imx8qxp_pcam-v6-0-4b3f45920d2f@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.

Reviewed-by: Guoniu Zhou <guoniu.zhou@nxp.com>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
change in v6
- collect gouniu's review by

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 v6 3/9] media: subdev: Add media_async_register_subdev() helper
From: Frank.Li @ 2026-06-24 20:37 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, Guoniu Zhou, devicetree,
	linux-arm-kernel
In-Reply-To: <20260624-imx8qxp_pcam-v6-0-4b3f45920d2f@nxp.com>

From: Frank Li <Frank.Li@nxp.com>

Add media_async_register_subdev(), a helper to register a V4L2 sub-device
with the asynchronous sub-device framework.

The helper assumes a 1:1 mapping between firmware endpoints and media pads.
During registration it parses the firmware graph, creates media pads for
all endpoints, and registers common asynchronous notifiers for sink
endpoints. These notifiers automatically create media links when the
corresponding remote source devices become available.

The set_pad_by_ep() callback allows drivers to determine the media pad
associated with a firmware endpoint and identify whether the endpoint
represents a sink pad.

By centralizing firmware graph parsing, media pad creation, notifier
registration, and link creation, this helper reduces duplicated code and
simplifies error handling in V4L2 sub-device drivers.

Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
change in v6
- new patch
---
 drivers/media/v4l2-core/v4l2-fwnode.c | 155 ++++++++++++++++++++++++++++++++++
 include/media/v4l2-async.h            |  39 +++++++++
 2 files changed, 194 insertions(+)

diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
index 62a3a452f7884..169059654478f 100644
--- a/drivers/media/v4l2-core/v4l2-fwnode.c
+++ b/drivers/media/v4l2-core/v4l2-fwnode.c
@@ -26,6 +26,7 @@
 
 #include <media/v4l2-async.h>
 #include <media/v4l2-fwnode.h>
+#include <media/v4l2-mc.h>
 #include <media/v4l2-subdev.h>
 
 #include "v4l2-subdev-priv.h"
@@ -1302,6 +1303,160 @@ int __v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd, struct module *m
 }
 EXPORT_SYMBOL_GPL(__v4l2_async_register_subdev_sensor);
 
+static int v4l2_common_notifier_bound(struct v4l2_async_notifier *notifier,
+				      struct v4l2_subdev *sd,
+				      struct v4l2_async_connection *asd)
+{
+	struct media_pad *pad = NULL;
+	int ret;
+
+	if (asd->match.type != V4L2_ASYNC_MATCH_TYPE_FWNODE)
+		return -EINVAL;
+
+	if (!asd->match.fwnode)
+		return -EINVAL;
+
+	struct fwnode_handle *remote __free(fwnode_handle) =
+		fwnode_graph_get_remote_endpoint(asd->match.fwnode);
+
+	for (int i = 0; i < notifier->sd->entity.num_pads; i++) {
+		if (notifier->sd->entity.pads[i].vep.base.local_fwnode == remote) {
+			pad = &notifier->sd->entity.pads[i];
+			break;
+		}
+	}
+
+	if (!pad) {
+		dev_err(notifier->sd->dev, "failed to find sink pad\n");
+		return -EINVAL;
+	}
+
+	ret = v4l2_create_fwnode_links_to_pad(sd, pad, MEDIA_LNK_FL_ENABLED);
+	if (ret) {
+		dev_err(sd->dev, "failed to link source pad\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct v4l2_async_notifier_operations v4l2_common_notifier_ops = {
+	.bound = v4l2_common_notifier_bound,
+};
+
+static int
+v4l2_async_nf_parse_fwnode(struct device *dev, struct media_pad *pads,
+			   struct v4l2_async_notifier *notifier)
+{
+	struct v4l2_subdev *sd = notifier->sd;
+	struct v4l2_async_connection *asd;
+	struct media_pad *pad;
+	int ret;
+
+	if (!sd->internal_ops->set_pad_by_ep)
+		return dev_err_probe(dev, -EINVAL,
+				     "Missed valiate_endpoint() callback\n");
+	pad = pads;
+
+	fwnode_graph_for_each_endpoint_scoped(dev_fwnode(dev), ep) {
+		u32 flags;
+
+		ret = v4l2_fwnode_endpoint_parse(ep, &pad->vep);
+		if (ret)
+			return dev_err_probe(dev, ret, "failed to parse endpoint\n");
+
+		ret = sd->internal_ops->set_pad_by_ep(sd, pad);
+		if (ret < 0)
+			return dev_err_probe(dev, ret, "Can support endponit\n");
+
+		flags = pad->flags;
+
+		pad++;
+
+		if (flags & MEDIA_PAD_FL_SOURCE)
+			continue; /* Bypass source port */
+
+		notifier->ops = &v4l2_common_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_cleanup(struct v4l2_subdev *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->entity.pads);
+}
+EXPORT_SYMBOL_GPL(media_async_subdev_cleanup);
+
+int __media_async_register_subdev(struct v4l2_subdev *sd, struct module *module)
+{
+	struct device *dev = sd->dev;
+	u32 ep_count;
+	int ret;
+
+	if (WARN_ON(!sd->dev))
+		return -ENODEV;
+
+	struct v4l2_async_notifier *notifier __free(kfree) = kzalloc_obj(*notifier);
+	if (!notifier)
+		return -ENOMEM;
+
+	v4l2_async_subdev_nf_init(notifier, sd);
+
+	ep_count = fwnode_graph_get_endpoint_count(dev_fwnode(dev), 0);
+	if (!ep_count)
+		return dev_err_probe(dev, -EINVAL, "No connected endpoints\n");
+
+	struct media_pad *pads __free(kfree) = kzalloc_objs(struct media_pad, ep_count);
+	if (!pads)
+		return -ENOMEM;
+
+	ret = v4l2_async_nf_parse_fwnode(dev, pads, notifier);
+	if (ret < 0)
+		return ret;
+
+	ret = media_entity_pads_init(&sd->entity, ep_count, pads);
+	if (ret)
+		goto out_cleanup;
+
+	ret = v4l2_async_nf_register(notifier);
+	if (ret < 0)
+		goto out_cleanup;
+
+	ret = v4l2_subdev_init_finalize(sd);
+	if (ret)
+		goto out_unregister;
+
+	ret = __v4l2_async_register_subdev(sd, module);
+	if (ret < 0)
+		goto out_unregister;
+
+	sd->subdev_notifier = no_free_ptr(notifier);
+	retain_and_null_ptr(pads);
+
+	return 0;
+
+out_unregister:
+	v4l2_async_nf_unregister(notifier);
+
+out_cleanup:
+	v4l2_async_nf_cleanup(notifier);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(__media_async_register_subdev);
+
 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..ca41820f776c5 100644
--- a/include/media/v4l2-async.h
+++ b/include/media/v4l2-async.h
@@ -345,4 +345,43 @@ __v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd, struct module *modul
  * @sd: pointer to &struct v4l2_subdev
  */
 void v4l2_async_unregister_subdev(struct v4l2_subdev *sd);
+
+enum v4l2_subdev_1to1_pads {
+	V4L2_SUBDEV_1TO1_PADS_SINK,
+	V4L2_SUBDEV_1TO1_PADS_SOURCE,
+	V4L2_SUBDEV_1TO1_PADS_TOTAL,
+};
+
+/**
+ * media_async_register_subdev - registers a sub-device to the asynchronous
+ *				 sub-device framework and parse set up common
+ *				 related devices
+ *
+ * @sd: pointer to struct &v4l2_subdev
+ *
+ * Register a V4L2 sub-device with the asynchronous sub-device framework.
+ * In addition to v4l2_async_register_subdev(), this function parses the
+ * firmware graph, creates media pads for the endpoints, and registers common
+ * notifiers to create media links between connected devices.
+ *
+ * This function also init media_pads.
+ *
+ * The sub-device is unregistered and cleanup by media_async_subdev_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(sd_1to1) \
+	 __media_async_register_subdev(sd_1to1, THIS_MODULE)
+
+int __media_async_register_subdev(struct v4l2_subdev *sd_1to1, struct module *module);
+
+/**
+ * media_async_subdev_cleanup - unregistered and cleanup subdev and media pads
+ * @sd_1to1: pointer to struct &v4l2_subdev_1to1
+ */
+void media_async_subdev_cleanup(struct v4l2_subdev *sd_1to1);
+
 #endif

-- 
2.43.0


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox