On Tue Jul 28 14:58:57 2026 +0800, Guoniu Zhou wrote:
> The ISI crossbar needs to properly enable multiple streams from different
> virtual channels on the same input pad. Currently only the first stream
> gets enabled in hardware, subsequent streams are silently ignored.
> 
> The driver uses a single enable_count per input to track the input state.
> When enable_count is non-zero, the code assumes the input is already active
> and skips calling v4l2_subdev_enable_streams() for additional streams:
> 
>   Call 1: enable_streams(stream 0)
>     -> enable_count == 0, enable gasket and stream 0 in hardware
>     -> enable_count = 1
> 
>   Call 2: enable_streams(stream 1)
>     -> enable_count == 1, skip hardware enable
>     -> enable_count = 2
>     -> stream 1 never gets enabled
> 
> Similarly on disable, when enable_count reaches zero, ALL streams are
> disabled regardless of which streams are actually still active.
> 
> Implement per-stream state tracking by storing the input index and stream
> mask in the mxc_isi_pipe structure. On enable, record which input and
> stream the pipe is receiving. On disable, clear the pipe's record and
> check if any other pipe is still using the same input stream before
> actually disabling it.
> 
> Signed-off-by: Guoniu Zhou <[email protected]>
> Reviewed-by: Laurent Pinchart <[email protected]>
> Reviewed-by: Frank Li <[email protected]>
> Link: https://patch.msgid.link/[email protected]
> Signed-off-by: Laurent Pinchart <[email protected]>
> Signed-off-by: Hans Verkuil <[email protected]>

Patch committed.

Thanks,
Hans Verkuil

 .../media/platform/nxp/imx8-isi/imx8-isi-core.h    | 10 ++-
 .../platform/nxp/imx8-isi/imx8-isi-crossbar.c      | 90 ++++++++++++++++------
 .../media/platform/nxp/imx8-isi/imx8-isi-pipe.c    |  1 +
 3 files changed, 78 insertions(+), 23 deletions(-)

---

diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h 
b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h
index 9bb4d430d15e..58b47335afc7 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h
@@ -186,7 +186,7 @@ struct mxc_isi_dma_buffer {
 };
 
 struct mxc_isi_input {
-       unsigned int                    enable_count;
+       u64                             enabled_streams;
 };
 
 struct mxc_isi_crossbar {
@@ -259,6 +259,14 @@ struct mxc_isi_pipe {
        u8                              acquired_res;
        u8                              chained_res;
        bool                            chained;
+
+       unsigned int                    input;
+       /*
+        * Stream on the connected crossbar input, expressed as a bitmask. Zero
+        * when the pipeline is disabled, a single bit set when the pipeline is
+        * enabled (as each pipeline processes a single stream).
+        */
+       u64                             input_stream;
 };
 
 struct mxc_isi_m2m {
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c 
b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c
index 9db9c6e55c2c..7bb1335f2110 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c
@@ -330,11 +330,32 @@ static int mxc_isi_crossbar_set_routing(struct 
v4l2_subdev *sd,
        return __mxc_isi_crossbar_set_routing(sd, state, routing);
 }
 
+/*
+ * Check if a stream on a sink pad is in used by any of the ISI pipelines. The
+ * sink_streams argument is a bitmask that must have a single bit set (enforced
+ * by mxc_isi_crossbar_xlate_streams() translating the single stream mask of 
the
+ * pipeline to a single stream on the crossbar input side).
+ */
+static bool mxc_isi_crossbar_stream_in_use(const struct mxc_isi_crossbar *xbar,
+                                          unsigned int sink_pad, u64 
sink_streams)
+{
+       for (unsigned int i = 0; i < xbar->isi->pdata->num_channels; ++i) {
+               const struct mxc_isi_pipe *pipe = &xbar->isi->pipes[i];
+
+               if (pipe->input == sink_pad &&
+                   pipe->input_stream == sink_streams)
+                       return true;
+       }
+
+       return false;
+}
+
 static int mxc_isi_crossbar_enable_streams(struct v4l2_subdev *sd,
                                           struct v4l2_subdev_state *state,
                                           u32 pad, u64 streams_mask)
 {
        struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd);
+       struct mxc_isi_pipe *pipe = &xbar->isi->pipes[pad - xbar->num_sinks];
        struct v4l2_subdev *remote_sd;
        struct mxc_isi_input *input;
        u64 sink_streams;
@@ -351,29 +372,44 @@ static int mxc_isi_crossbar_enable_streams(struct 
v4l2_subdev *sd,
        input = &xbar->inputs[sink_pad];
 
        /*
-        * TODO: Track per-stream enable counts to support multiplexed
-        * streams.
+        * Check if any other pipe already receives the same input stream.
+        * If so, just record this pipe's usage and return.
         */
-       if (!input->enable_count) {
+       if (mxc_isi_crossbar_stream_in_use(xbar, sink_pad, sink_streams)) {
+               pipe->input = sink_pad;
+               pipe->input_stream = sink_streams;
+               return 0;
+       }
+
+       /* Enable the gasket when the first stream is enabled for this input. */
+       if (!input->enabled_streams) {
                ret = mxc_isi_crossbar_gasket_enable(xbar, state, remote_sd,
                                                     remote_pad, sink_pad);
                if (ret)
                        return ret;
+       }
 
-               ret = v4l2_subdev_enable_streams(remote_sd, remote_pad,
-                                                sink_streams);
-               if (ret) {
-                       dev_err(xbar->isi->dev,
-                               "failed to enable streams 0x%llx on '%s':%u: 
%d\n",
-                               sink_streams, remote_sd->name, remote_pad, ret);
-                       mxc_isi_crossbar_gasket_disable(xbar, sink_pad);
-                       return ret;
-               }
+       ret = v4l2_subdev_enable_streams(remote_sd, remote_pad, sink_streams);
+       if (ret) {
+               dev_err(xbar->isi->dev,
+                       "failed to enable streams 0x%llx on '%s':%u: %d\n",
+                       sink_streams, remote_sd->name, remote_pad, ret);
+               goto err_gasket_disable;
        }
 
-       input->enable_count++;
+       input->enabled_streams |= sink_streams;
+
+       /* Record the input and stream for this pipe. */
+       pipe->input = sink_pad;
+       pipe->input_stream = sink_streams;
 
        return 0;
+
+err_gasket_disable:
+       if (!input->enabled_streams)
+               mxc_isi_crossbar_gasket_disable(xbar, sink_pad);
+
+       return ret;
 }
 
 static int mxc_isi_crossbar_disable_streams(struct v4l2_subdev *sd,
@@ -381,6 +417,7 @@ static int mxc_isi_crossbar_disable_streams(struct 
v4l2_subdev *sd,
                                            u32 pad, u64 streams_mask)
 {
        struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd);
+       struct mxc_isi_pipe *pipe = &xbar->isi->pipes[pad - xbar->num_sinks];
        struct v4l2_subdev *remote_sd;
        struct mxc_isi_input *input;
        u64 sink_streams;
@@ -396,18 +433,27 @@ static int mxc_isi_crossbar_disable_streams(struct 
v4l2_subdev *sd,
 
        input = &xbar->inputs[sink_pad];
 
-       input->enable_count--;
+       /* Clear the input and stream for this pipe. */
+       pipe->input = UINT_MAX;
+       pipe->input_stream = 0;
 
-       if (!input->enable_count) {
-               ret = v4l2_subdev_disable_streams(remote_sd, remote_pad,
-                                                 sink_streams);
-               if (ret)
-                       dev_err(xbar->isi->dev,
-                               "failed to disable streams 0x%llx on '%s':%u: 
%d\n",
-                               sink_streams, remote_sd->name, remote_pad, ret);
+       /*
+        * Check if any other pipe receives the same input stream. If so we
+        * can't disable it yet, so return immediately.
+        */
+       if (mxc_isi_crossbar_stream_in_use(xbar, sink_pad, sink_streams))
+               return 0;
 
+       ret = v4l2_subdev_disable_streams(remote_sd, remote_pad, sink_streams);
+       if (ret)
+               dev_err(xbar->isi->dev,
+                       "failed to disable streams 0x%llx on '%s':%u: %d\n",
+                       sink_streams, remote_sd->name, remote_pad, ret);
+
+       input->enabled_streams &= ~sink_streams;
+
+       if (!input->enabled_streams)
                mxc_isi_crossbar_gasket_disable(xbar, sink_pad);
-       }
 
        return ret;
 }
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c 
b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c
index 16085f23bc0b..c0ec59856374 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c
@@ -816,6 +816,7 @@ int mxc_isi_pipe_init(struct mxc_isi_dev *isi, unsigned int 
id)
        pipe->acquired_res = 0;
        pipe->chained_res = 0;
        pipe->chained = false;
+       pipe->input = UINT_MAX;
 
        sd = &pipe->sd;
        v4l2_subdev_init(sd, &mxc_isi_pipe_subdev_ops);
_______________________________________________
linuxtv-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to