summaryrefslogtreecommitdiff
path: root/drivers/media/v4l2-core/v4l2-subdev.c
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>2021-12-13 15:11:56 +0100
committerMauro Carvalho Chehab <mchehab@kernel.org>2023-01-22 09:50:06 +0100
commitd0749adb30706f4e3af452698db96f671444341a (patch)
tree6cc9a1b6883651c7b69b94d68e6f485b57a92f97 /drivers/media/v4l2-core/v4l2-subdev.c
parentc4a73f316d04594689dffcae8d800787e4fc9f75 (diff)
media: v4l2-subdev: Add subdev .(enable|disable)_streams() operations
Add two new subdev pad operations, .enable_streams() and .disable_streams(), to allow control of individual streams per pad. This is a superset of what the video .s_stream() operation implements. To help with handling of backward compatibility, add two wrapper functions around those operations, and require their usage in drivers. Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Jacopo Mondi <jacopo@jmondi.org> Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
Diffstat (limited to 'drivers/media/v4l2-core/v4l2-subdev.c')
-rw-r--r--drivers/media/v4l2-core/v4l2-subdev.c224
1 files changed, 224 insertions, 0 deletions
diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
index 13f1a100496e..c94fa2c2cbda 100644
--- a/drivers/media/v4l2-core/v4l2-subdev.c
+++ b/drivers/media/v4l2-core/v4l2-subdev.c
@@ -1743,6 +1743,230 @@ out:
}
EXPORT_SYMBOL_GPL(v4l2_subdev_routing_validate);
+static int v4l2_subdev_enable_streams_fallback(struct v4l2_subdev *sd, u32 pad,
+ u64 streams_mask)
+{
+ struct device *dev = sd->entity.graph_obj.mdev->dev;
+ unsigned int i;
+ int ret;
+
+ /*
+ * The subdev doesn't implement pad-based stream enable, fall back
+ * on the .s_stream() operation. This can only be done for subdevs that
+ * have a single source pad, as sd->enabled_streams is global to the
+ * subdev.
+ */
+ if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE))
+ return -EOPNOTSUPP;
+
+ for (i = 0; i < sd->entity.num_pads; ++i) {
+ if (i != pad && sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE)
+ return -EOPNOTSUPP;
+ }
+
+ if (sd->enabled_streams & streams_mask) {
+ dev_dbg(dev, "set of streams %#llx already enabled on %s:%u\n",
+ streams_mask, sd->entity.name, pad);
+ return -EALREADY;
+ }
+
+ /* Start streaming when the first streams are enabled. */
+ if (!sd->enabled_streams) {
+ ret = v4l2_subdev_call(sd, video, s_stream, 1);
+ if (ret)
+ return ret;
+ }
+
+ sd->enabled_streams |= streams_mask;
+
+ return 0;
+}
+
+int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad,
+ u64 streams_mask)
+{
+ struct device *dev = sd->entity.graph_obj.mdev->dev;
+ struct v4l2_subdev_state *state;
+ u64 found_streams = 0;
+ unsigned int i;
+ int ret;
+
+ /* A few basic sanity checks first. */
+ if (pad >= sd->entity.num_pads)
+ return -EINVAL;
+
+ if (!streams_mask)
+ return 0;
+
+ /* Fallback on .s_stream() if .enable_streams() isn't available. */
+ if (!sd->ops->pad || !sd->ops->pad->enable_streams)
+ return v4l2_subdev_enable_streams_fallback(sd, pad,
+ streams_mask);
+
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+
+ /*
+ * Verify that the requested streams exist and that they are not
+ * already enabled.
+ */
+ for (i = 0; i < state->stream_configs.num_configs; ++i) {
+ struct v4l2_subdev_stream_config *cfg =
+ &state->stream_configs.configs[i];
+
+ if (cfg->pad != pad || !(streams_mask & BIT_ULL(cfg->stream)))
+ continue;
+
+ found_streams |= BIT_ULL(cfg->stream);
+
+ if (cfg->enabled) {
+ dev_dbg(dev, "stream %u already enabled on %s:%u\n",
+ cfg->stream, sd->entity.name, pad);
+ ret = -EALREADY;
+ goto done;
+ }
+ }
+
+ if (found_streams != streams_mask) {
+ dev_dbg(dev, "streams 0x%llx not found on %s:%u\n",
+ streams_mask & ~found_streams, sd->entity.name, pad);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ /* Call the .enable_streams() operation. */
+ ret = v4l2_subdev_call(sd, pad, enable_streams, state, pad,
+ streams_mask);
+ if (ret)
+ goto done;
+
+ /* Mark the streams as enabled. */
+ for (i = 0; i < state->stream_configs.num_configs; ++i) {
+ struct v4l2_subdev_stream_config *cfg =
+ &state->stream_configs.configs[i];
+
+ if (cfg->pad == pad && (streams_mask & BIT_ULL(cfg->stream)))
+ cfg->enabled = true;
+ }
+
+done:
+ v4l2_subdev_unlock_state(state);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_enable_streams);
+
+static int v4l2_subdev_disable_streams_fallback(struct v4l2_subdev *sd, u32 pad,
+ u64 streams_mask)
+{
+ struct device *dev = sd->entity.graph_obj.mdev->dev;
+ unsigned int i;
+ int ret;
+
+ /*
+ * If the subdev doesn't implement pad-based stream enable, fall back
+ * on the .s_stream() operation. This can only be done for subdevs that
+ * have a single source pad, as sd->enabled_streams is global to the
+ * subdev.
+ */
+ if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE))
+ return -EOPNOTSUPP;
+
+ for (i = 0; i < sd->entity.num_pads; ++i) {
+ if (i != pad && sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE)
+ return -EOPNOTSUPP;
+ }
+
+ if ((sd->enabled_streams & streams_mask) != streams_mask) {
+ dev_dbg(dev, "set of streams %#llx already disabled on %s:%u\n",
+ streams_mask, sd->entity.name, pad);
+ return -EALREADY;
+ }
+
+ /* Stop streaming when the last streams are disabled. */
+ if (!(sd->enabled_streams & ~streams_mask)) {
+ ret = v4l2_subdev_call(sd, video, s_stream, 0);
+ if (ret)
+ return ret;
+ }
+
+ sd->enabled_streams &= ~streams_mask;
+
+ return 0;
+}
+
+int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad,
+ u64 streams_mask)
+{
+ struct device *dev = sd->entity.graph_obj.mdev->dev;
+ struct v4l2_subdev_state *state;
+ u64 found_streams = 0;
+ unsigned int i;
+ int ret;
+
+ /* A few basic sanity checks first. */
+ if (pad >= sd->entity.num_pads)
+ return -EINVAL;
+
+ if (!streams_mask)
+ return 0;
+
+ /* Fallback on .s_stream() if .disable_streams() isn't available. */
+ if (!sd->ops->pad || !sd->ops->pad->disable_streams)
+ return v4l2_subdev_disable_streams_fallback(sd, pad,
+ streams_mask);
+
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+
+ /*
+ * Verify that the requested streams exist and that they are not
+ * already disabled.
+ */
+ for (i = 0; i < state->stream_configs.num_configs; ++i) {
+ struct v4l2_subdev_stream_config *cfg =
+ &state->stream_configs.configs[i];
+
+ if (cfg->pad != pad || !(streams_mask & BIT_ULL(cfg->stream)))
+ continue;
+
+ found_streams |= BIT_ULL(cfg->stream);
+
+ if (!cfg->enabled) {
+ dev_dbg(dev, "stream %u already disabled on %s:%u\n",
+ cfg->stream, sd->entity.name, pad);
+ ret = -EALREADY;
+ goto done;
+ }
+ }
+
+ if (found_streams != streams_mask) {
+ dev_dbg(dev, "streams 0x%llx not found on %s:%u\n",
+ streams_mask & ~found_streams, sd->entity.name, pad);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ /* Call the .disable_streams() operation. */
+ ret = v4l2_subdev_call(sd, pad, disable_streams, state, pad,
+ streams_mask);
+ if (ret)
+ goto done;
+
+ /* Mark the streams as disabled. */
+ for (i = 0; i < state->stream_configs.num_configs; ++i) {
+ struct v4l2_subdev_stream_config *cfg =
+ &state->stream_configs.configs[i];
+
+ if (cfg->pad == pad && (streams_mask & BIT_ULL(cfg->stream)))
+ cfg->enabled = false;
+ }
+
+done:
+ v4l2_subdev_unlock_state(state);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_disable_streams);
+
#endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */
#endif /* CONFIG_MEDIA_CONTROLLER */