On 27/06/2024 16:10, Jai Luthra wrote:
> Each CSI2 stream can be multiplexed into 4 independent streams, each
> identified by its virtual channel number. To capture this multiplexed
> stream, the application needs to tell the driver how it wants to route
> the data. It needs to specify which context should process which stream.
> This is done via the new routing APIs.
>
> Add ioctls to accept routing information from the application and save
> that in the driver. This can be used when starting streaming on a
> context to determine which route and consequently which virtual channel
> it should process.
>
> Support the new enable_stream()/disable_stream() APIs in the subdev
> instead of s_stream() hook. We wait for the userspace to start capturing
> on all video nodes that have active routes, and once all video nodes are
> STREAMON, we request the source to enable_stream() for all the sink
> streams.
>
> Co-developed-by: Pratyush Yadav <p.yadav@ti.com>
> Signed-off-by: Pratyush Yadav <p.yadav@ti.com>
> Signed-off-by: Jai Luthra <j-luthra@ti.com>
> ---
> .../media/platform/ti/j721e-csi2rx/j721e-csi2rx.c | 231 +++++++++++++++++----
> 1 file changed, 185 insertions(+), 46 deletions(-)
>
> diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
> index c0916ca1a6f8..84b972c251e8 100644
> --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
> +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
> @@ -124,6 +124,7 @@ struct ti_csi2rx_dev {
> struct v4l2_subdev *source;
> struct v4l2_subdev subdev;
> struct ti_csi2rx_ctx ctx[TI_CSI2RX_MAX_CTX];
> + u64 streams_mask; /* Enabled sink streams */
> /* Buffer to drain stale data from PSI-L endpoint */
> struct {
> void *vaddr;
> @@ -535,10 +536,6 @@ static void ti_csi2rx_setup_shim(struct ti_csi2rx_ctx *ctx)
>
> fmt = find_format_by_fourcc(ctx->v_fmt.fmt.pix.pixelformat);
>
> - /* De-assert the pixel interface reset. */
> - reg = SHIM_CNTL_PIX_RST;
> - writel(reg, csi->shim + SHIM_CNTL);
> -
> reg = SHIM_DMACNTX_EN;
> reg |= FIELD_PREP(SHIM_DMACNTX_FMT, fmt->csi_dt);
>
> @@ -881,8 +878,12 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count)
> struct ti_csi2rx_dev *csi = ctx->csi;
> struct ti_csi2rx_dma *dma = &ctx->dma;
> struct ti_csi2rx_buffer *buf;
> + struct v4l2_subdev_krouting *routing;
> + struct v4l2_subdev_route *route = NULL;
> + struct media_pad *remote_pad;
> unsigned long flags;
> - int ret = 0;
> + int ret = 0, i;
> + struct v4l2_subdev_state *state;
>
> spin_lock_irqsave(&dma->lock, flags);
> if (list_empty(&dma->queue))
> @@ -895,6 +896,40 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count)
> if (ret)
> goto err;
>
> + remote_pad = media_entity_remote_source_pad_unique(ctx->pad.entity);
> + if (!remote_pad) {
> + ret = -ENODEV;
> + goto err;
> + }
> +
> + state = v4l2_subdev_lock_and_get_active_state(&csi->subdev);
> +
> + routing = &state->routing;
> +
> + /* Find the stream to process. */
> + for (i = 0; i < routing->num_routes; i++) {
for_each_active_route()
> + struct v4l2_subdev_route *r = &routing->routes[i];
> +
> + if (!(r->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE))
> + continue;
> +
> + if (r->source_pad != remote_pad->index)
> + continue;
> +
> + route = r;
> + break;
> + }
> +
> + if (!route) {
> + ret = -ENODEV;
> + v4l2_subdev_unlock_state(state);
> + goto err;
> + }
> +
> + ctx->stream = route->sink_stream;
> +
> + v4l2_subdev_unlock_state(state);
> +
> ret = ti_csi2rx_get_vc(ctx);
> if (ret == -ENOIOCTLCMD)
> ctx->vc = 0;
> @@ -921,7 +956,10 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count)
> dma->state = TI_CSI2RX_DMA_ACTIVE;
> spin_unlock_irqrestore(&dma->lock, flags);
>
> - ret = v4l2_subdev_call(&csi->subdev, video, s_stream, 1);
> + /* Start stream 0, we don't allow multiple streams on the source pad */
> + ret = v4l2_subdev_enable_streams(&csi->subdev,
> + TI_CSI2RX_PAD_FIRST_SOURCE + ctx->idx,
> + BIT(0));
> if (ret)
> goto err_dma;
>
> @@ -944,12 +982,16 @@ static void ti_csi2rx_stop_streaming(struct vb2_queue *vq)
> struct ti_csi2rx_dev *csi = ctx->csi;
> int ret;
>
> - video_device_pipeline_stop(&ctx->vdev);
> -
> + /* assert pixel reset to prevent stale data */
> writel(0, csi->shim + SHIM_CNTL);
> +
> + video_device_pipeline_stop(&ctx->vdev);
> writel(0, csi->shim + SHIM_DMACNTX(ctx->idx));
>
> - ret = v4l2_subdev_call(&csi->subdev, video, s_stream, 0);
> + ret = v4l2_subdev_disable_streams(&csi->subdev,
> + TI_CSI2RX_PAD_FIRST_SOURCE + ctx->idx,
> + BIT(0));
> +
> if (ret)
> dev_err(csi->dev, "Failed to stop subdev stream\n");
>
> @@ -995,8 +1037,8 @@ static int ti_csi2rx_sd_set_fmt(struct v4l2_subdev *sd,
> }
> *fmt = format->format;
>
> - fmt = v4l2_subdev_state_get_format(state, TI_CSI2RX_PAD_FIRST_SOURCE,
> - format->stream);
> + fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad,
> + format->stream);
> if (!fmt) {
> ret = -EINVAL;
> goto out;
> @@ -1007,72 +1049,169 @@ static int ti_csi2rx_sd_set_fmt(struct v4l2_subdev *sd,
> return ret;
> }
>
> +static int _ti_csi2rx_sd_set_routing(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *state,
> + struct v4l2_subdev_krouting *routing)
> +{
> + int ret;
> +
> + const struct v4l2_mbus_framefmt format = {
static const
Tomi
@@ -124,6 +124,7 @@ struct ti_csi2rx_dev {
struct v4l2_subdev *source;
struct v4l2_subdev subdev;
struct ti_csi2rx_ctx ctx[TI_CSI2RX_MAX_CTX];
+ u64 streams_mask; /* Enabled sink streams */
/* Buffer to drain stale data from PSI-L endpoint */
struct {
void *vaddr;
@@ -535,10 +536,6 @@ static void ti_csi2rx_setup_shim(struct ti_csi2rx_ctx *ctx)
fmt = find_format_by_fourcc(ctx->v_fmt.fmt.pix.pixelformat);
- /* De-assert the pixel interface reset. */
- reg = SHIM_CNTL_PIX_RST;
- writel(reg, csi->shim + SHIM_CNTL);
-
reg = SHIM_DMACNTX_EN;
reg |= FIELD_PREP(SHIM_DMACNTX_FMT, fmt->csi_dt);
@@ -881,8 +878,12 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count)
struct ti_csi2rx_dev *csi = ctx->csi;
struct ti_csi2rx_dma *dma = &ctx->dma;
struct ti_csi2rx_buffer *buf;
+ struct v4l2_subdev_krouting *routing;
+ struct v4l2_subdev_route *route = NULL;
+ struct media_pad *remote_pad;
unsigned long flags;
- int ret = 0;
+ int ret = 0, i;
+ struct v4l2_subdev_state *state;
spin_lock_irqsave(&dma->lock, flags);
if (list_empty(&dma->queue))
@@ -895,6 +896,40 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count)
if (ret)
goto err;
+ remote_pad = media_entity_remote_source_pad_unique(ctx->pad.entity);
+ if (!remote_pad) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ state = v4l2_subdev_lock_and_get_active_state(&csi->subdev);
+
+ routing = &state->routing;
+
+ /* Find the stream to process. */
+ for (i = 0; i < routing->num_routes; i++) {
+ struct v4l2_subdev_route *r = &routing->routes[i];
+
+ if (!(r->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE))
+ continue;
+
+ if (r->source_pad != remote_pad->index)
+ continue;
+
+ route = r;
+ break;
+ }
+
+ if (!route) {
+ ret = -ENODEV;
+ v4l2_subdev_unlock_state(state);
+ goto err;
+ }
+
+ ctx->stream = route->sink_stream;
+
+ v4l2_subdev_unlock_state(state);
+
ret = ti_csi2rx_get_vc(ctx);
if (ret == -ENOIOCTLCMD)
ctx->vc = 0;
@@ -921,7 +956,10 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count)
dma->state = TI_CSI2RX_DMA_ACTIVE;
spin_unlock_irqrestore(&dma->lock, flags);
- ret = v4l2_subdev_call(&csi->subdev, video, s_stream, 1);
+ /* Start stream 0, we don't allow multiple streams on the source pad */
+ ret = v4l2_subdev_enable_streams(&csi->subdev,
+ TI_CSI2RX_PAD_FIRST_SOURCE + ctx->idx,
+ BIT(0));
if (ret)
goto err_dma;
@@ -944,12 +982,16 @@ static void ti_csi2rx_stop_streaming(struct vb2_queue *vq)
struct ti_csi2rx_dev *csi = ctx->csi;
int ret;
- video_device_pipeline_stop(&ctx->vdev);
-
+ /* assert pixel reset to prevent stale data */
writel(0, csi->shim + SHIM_CNTL);
+
+ video_device_pipeline_stop(&ctx->vdev);
writel(0, csi->shim + SHIM_DMACNTX(ctx->idx));
- ret = v4l2_subdev_call(&csi->subdev, video, s_stream, 0);
+ ret = v4l2_subdev_disable_streams(&csi->subdev,
+ TI_CSI2RX_PAD_FIRST_SOURCE + ctx->idx,
+ BIT(0));
+
if (ret)
dev_err(csi->dev, "Failed to stop subdev stream\n");
@@ -995,8 +1037,8 @@ static int ti_csi2rx_sd_set_fmt(struct v4l2_subdev *sd,
}
*fmt = format->format;
- fmt = v4l2_subdev_state_get_format(state, TI_CSI2RX_PAD_FIRST_SOURCE,
- format->stream);
+ fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad,
+ format->stream);
if (!fmt) {
ret = -EINVAL;
goto out;
@@ -1007,72 +1049,169 @@ static int ti_csi2rx_sd_set_fmt(struct v4l2_subdev *sd,
return ret;
}
+static int _ti_csi2rx_sd_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_krouting *routing)
+{
+ int ret;
+
+ const struct v4l2_mbus_framefmt format = {
+ .width = 640,
+ .height = 480,
+ .code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .ycbcr_enc = V4L2_YCBCR_ENC_601,
+ .quantization = V4L2_QUANTIZATION_LIM_RANGE,
+ .xfer_func = V4L2_XFER_FUNC_SRGB,
+ };
+
+ ret = v4l2_subdev_routing_validate(sd, routing,
+ V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 |
+ V4L2_SUBDEV_ROUTING_NO_SOURCE_MULTIPLEXING);
+
+ if (ret)
+ return ret;
+
+ /* Only stream ID 0 allowed on source pads */
+ for (unsigned int i = 0; i < routing->num_routes; ++i) {
+ const struct v4l2_subdev_route *route = &routing->routes[i];
+
+ if (route->source_stream != 0)
+ return -EINVAL;
+ }
+
+ ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format);
+
+ return ret;
+}
+
+static int ti_csi2rx_sd_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ enum v4l2_subdev_format_whence which,
+ struct v4l2_subdev_krouting *routing)
+{
+ struct ti_csi2rx_dev *csi = to_csi2rx_dev(sd);
+
+ if (csi->enable_count > 0)
+ return -EBUSY;
+
+ return _ti_csi2rx_sd_set_routing(sd, state, routing);
+}
+
static int ti_csi2rx_sd_init_state(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state)
{
- struct v4l2_subdev_format format = {
- .pad = TI_CSI2RX_PAD_SINK,
- .format = {
- .width = 640,
- .height = 480,
- .code = MEDIA_BUS_FMT_UYVY8_1X16,
- .field = V4L2_FIELD_NONE,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .ycbcr_enc = V4L2_YCBCR_ENC_601,
- .quantization = V4L2_QUANTIZATION_LIM_RANGE,
- .xfer_func = V4L2_XFER_FUNC_SRGB,
- },
+ struct v4l2_subdev_route routes[] = { {
+ .sink_pad = 0,
+ .sink_stream = 0,
+ .source_pad = TI_CSI2RX_PAD_FIRST_SOURCE,
+ .source_stream = 0,
+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
+ } };
+
+ struct v4l2_subdev_krouting routing = {
+ .num_routes = 1,
+ .routes = routes,
};
- return ti_csi2rx_sd_set_fmt(sd, state, &format);
+ /* Initialize routing to single route to the fist source pad */
+ return _ti_csi2rx_sd_set_routing(sd, state, &routing);
}
-static int ti_csi2rx_sd_s_stream(struct v4l2_subdev *sd, int enable)
+static int ti_csi2rx_sd_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
{
struct ti_csi2rx_dev *csi = to_csi2rx_dev(sd);
+ struct v4l2_subdev_route *route;
+ struct media_pad *remote_pad;
+ u64 sink_streams;
int ret = 0;
+ u32 reg;
+ remote_pad = media_entity_remote_source_pad_unique(&csi->subdev.entity);
+ if (!remote_pad)
+ return -ENODEV;
+ sink_streams = v4l2_subdev_state_xlate_streams(state, pad,
+ TI_CSI2RX_PAD_SINK,
+ &streams_mask);
mutex_lock(&csi->mutex);
- if (enable) {
- if (csi->enable_count > 0) {
- csi->enable_count++;
+ csi->streams_mask |= sink_streams;
+ /*
+ * Check if all active streams are enabled, and only then start
+ * streaming on the source
+ */
+ for_each_active_route(&state->routing, route) {
+ if (!(csi->streams_mask & BIT_ULL(route->sink_stream)))
goto out;
- }
+ }
- ret = v4l2_subdev_call(csi->source, video, s_stream, 1);
- if (ret)
- goto out;
+ /* De-assert the pixel interface reset. */
+ reg = SHIM_CNTL_PIX_RST;
+ writel(reg, csi->shim + SHIM_CNTL);
+ ret = v4l2_subdev_enable_streams(csi->source, remote_pad->index,
+ csi->streams_mask);
+out:
+ if (!ret)
csi->enable_count++;
- } else {
- if (csi->enable_count == 0) {
- ret = -EINVAL;
- goto out;
- }
+ mutex_unlock(&csi->mutex);
- if (--csi->enable_count > 0)
- goto out;
+ return ret;
+}
+
+static int ti_csi2rx_sd_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct ti_csi2rx_dev *csi = to_csi2rx_dev(sd);
+ struct v4l2_subdev_route *route;
+ struct media_pad *remote_pad;
+ u64 sink_streams;
+ int ret = 0;
- ret = v4l2_subdev_call(csi->source, video, s_stream, 0);
+ remote_pad = media_entity_remote_source_pad_unique(&csi->subdev.entity);
+ if (!remote_pad)
+ return -ENODEV;
+ sink_streams = v4l2_subdev_state_xlate_streams(state, pad,
+ TI_CSI2RX_PAD_SINK,
+ &streams_mask);
+
+ mutex_lock(&csi->mutex);
+ if (csi->enable_count == 0) {
+ ret = -EINVAL;
+ goto out;
}
+ /* Check if any of the active streams are already disabled */
+ for_each_active_route(&state->routing, route) {
+ if (!(csi->streams_mask & BIT_ULL(route->sink_stream))) {
+ --csi->enable_count;
+ goto out;
+ }
+ }
+ /* Disable all streams on the source */
+ ret = v4l2_subdev_disable_streams(csi->source, remote_pad->index,
+ csi->streams_mask);
+ if (!ret)
+ --csi->enable_count;
out:
+ csi->streams_mask &= ~sink_streams;
mutex_unlock(&csi->mutex);
return ret;
}
static const struct v4l2_subdev_pad_ops ti_csi2rx_subdev_pad_ops = {
+ .set_routing = ti_csi2rx_sd_set_routing,
.get_fmt = v4l2_subdev_get_fmt,
.set_fmt = ti_csi2rx_sd_set_fmt,
-};
-
-static const struct v4l2_subdev_video_ops ti_csi2rx_subdev_video_ops = {
- .s_stream = ti_csi2rx_sd_s_stream,
+ .enable_streams = ti_csi2rx_sd_enable_streams,
+ .disable_streams = ti_csi2rx_sd_disable_streams,
};
static const struct v4l2_subdev_ops ti_csi2rx_subdev_ops = {
- .video = &ti_csi2rx_subdev_video_ops,
.pad = &ti_csi2rx_subdev_pad_ops,
};
@@ -1264,7 +1403,7 @@ static int ti_csi2rx_v4l2_init(struct ti_csi2rx_dev *csi)
v4l2_subdev_init(sd, &ti_csi2rx_subdev_ops);
sd->internal_ops = &ti_csi2rx_internal_ops;
sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
- sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;
strscpy(sd->name, dev_name(csi->dev), sizeof(sd->name));
sd->dev = csi->dev;
sd->entity.ops = &ti_csi2rx_subdev_entity_ops;