@@ -755,8 +755,51 @@ static int media_pipeline_populate(struct media_pipeline *pipe,
return ret;
}
-__must_check int __media_pipeline_start(struct media_pad *pad,
- struct media_pipeline *pipe)
+static int
+media_pipeline_validate_context(struct media_device_context *mdev_context,
+ struct media_entity *entity,
+ struct media_pipeline_pad *ppad)
+{
+ struct media_entity_context *context;
+
+ if (!mdev_context)
+ return 0;
+
+ /*
+ * It's not mandatory for all entities in the pipeline to support
+ * contexts.
+ */
+ if (!entity->ops || !entity->ops->alloc_context ||
+ !entity->ops->destroy_context)
+ return 0;
+
+ /*
+ * But if they do they should be bound to the same media device context
+ * as all other entities.
+ */
+ context = media_device_get_entity_context(mdev_context, entity);
+
+ /*
+ * Fail validation if no context is associated with this media context.
+ */
+ if (!context)
+ return -EINVAL;
+
+ /*
+ * Increase the ref-counting of the context. Store a reference in the
+ * ppad for later decreasing the reference count when the pipeline is
+ * stopped.
+ */
+ media_entity_context_get(context);
+ ppad->context = context;
+
+ return 0;
+}
+
+__must_check int
+__media_pipeline_start_context(struct media_pad *pad,
+ struct media_pipeline *pipe,
+ struct media_device_context *mdev_context)
{
struct media_device *mdev = pad->graph_obj.mdev;
struct media_pipeline_pad *err_ppad;
@@ -816,7 +859,15 @@ __must_check int __media_pipeline_start(struct media_pad *pad,
}
/*
- * 2. Validate all active links whose sink is the current pad.
+ * 2. If we have a media context, ensure the entity has a device
+ * context associated with it.
+ */
+ ret = media_pipeline_validate_context(mdev_context, entity, ppad);
+ if (ret)
+ goto error;
+
+ /*
+ * 3. Validate all active links whose sink is the current pad.
* Validation of the source pads is performed in the context of
* the connected sink pad to avoid duplicating checks.
*/
@@ -862,7 +913,7 @@ __must_check int __media_pipeline_start(struct media_pad *pad,
}
/*
- * 3. If the pad has the MEDIA_PAD_FL_MUST_CONNECT flag set,
+ * 4. If the pad has the MEDIA_PAD_FL_MUST_CONNECT flag set,
* ensure that it has either no link or an enabled link.
*/
if ((pad->flags & MEDIA_PAD_FL_MUST_CONNECT) &&
@@ -892,6 +943,9 @@ __must_check int __media_pipeline_start(struct media_pad *pad,
if (err_ppad == ppad)
break;
+ if (err_ppad->context)
+ media_entity_context_put(err_ppad->context);
+
err_ppad->pad->pipe = NULL;
}
@@ -899,19 +953,35 @@ __must_check int __media_pipeline_start(struct media_pad *pad,
return ret;
}
+EXPORT_SYMBOL_GPL(__media_pipeline_start_context);
+
+__must_check int __media_pipeline_start(struct media_pad *pad,
+ struct media_pipeline *pipe)
+{
+ return __media_pipeline_start_context(pad, pipe, NULL);
+}
EXPORT_SYMBOL_GPL(__media_pipeline_start);
-__must_check int media_pipeline_start(struct media_pad *pad,
- struct media_pipeline *pipe)
+__must_check int
+media_pipeline_start_context(struct media_pad *pad,
+ struct media_pipeline *pipe,
+ struct media_device_context *context)
{
struct media_device *mdev = pad->graph_obj.mdev;
int ret;
mutex_lock(&mdev->graph_mutex);
- ret = __media_pipeline_start(pad, pipe);
+ ret = __media_pipeline_start_context(pad, pipe, context);
mutex_unlock(&mdev->graph_mutex);
return ret;
}
+EXPORT_SYMBOL_GPL(media_pipeline_start_context);
+
+__must_check int media_pipeline_start(struct media_pad *pad,
+ struct media_pipeline *pipe)
+{
+ return media_pipeline_start_context(pad, pipe, NULL);
+}
EXPORT_SYMBOL_GPL(media_pipeline_start);
void __media_pipeline_stop(struct media_pad *pad)
@@ -929,8 +999,11 @@ void __media_pipeline_stop(struct media_pad *pad)
if (--pipe->start_count)
return;
- list_for_each_entry(ppad, &pipe->pads, list)
+ list_for_each_entry(ppad, &pipe->pads, list) {
+ if (ppad->context)
+ media_entity_context_put(ppad->context);
ppad->pad->pipe = NULL;
+ }
media_pipeline_cleanup(pipe);
@@ -1222,6 +1222,36 @@ __must_check int __video_device_pipeline_start(struct video_device *vdev,
}
EXPORT_SYMBOL_GPL(__video_device_pipeline_start);
+__must_check int
+video_device_context_pipeline_start(struct video_device_context *context,
+ struct media_pipeline *pipe)
+{
+ struct video_device *vdev = context->vdev;
+ struct media_entity *entity = &vdev->entity;
+
+ if (entity->num_pads != 1)
+ return -ENODEV;
+
+ return media_pipeline_start_context(&entity->pads[0], pipe,
+ context->base.mdev_context);
+}
+EXPORT_SYMBOL(video_device_context_pipeline_start);
+
+__must_check int
+__video_device_context_pipeline_start(struct video_device_context *context,
+ struct media_pipeline *pipe)
+{
+ struct video_device *vdev = context->vdev;
+ struct media_entity *entity = &vdev->entity;
+
+ if (entity->num_pads != 1)
+ return -ENODEV;
+
+ return __media_pipeline_start_context(&entity->pads[0], pipe,
+ context->base.mdev_context);
+}
+EXPORT_SYMBOL(__video_device_context_pipeline_start);
+
void video_device_pipeline_stop(struct video_device *vdev)
{
struct media_entity *entity = &vdev->entity;
@@ -21,6 +21,7 @@
#include <linux/minmax.h>
#include <linux/types.h>
+
/* Enums used internally at the media controller to represent graphs */
/**
@@ -119,16 +120,20 @@ struct media_pipeline {
* @list: Entry in the media_pad pads list
* @pipe: The media_pipeline that the pad is part of
* @pad: The media pad
+ * @context: Reference to a video device or subdevice context
*
* This structure associate a pad with a media pipeline. Instances of
* media_pipeline_pad are created by media_pipeline_start() when it builds the
* pipeline, and stored in the &media_pad.pads list. media_pipeline_stop()
- * removes the entries from the list and deletes them.
+ * removes the entries from the list and deletes them. The context field is
+ * populated only if a valid context has been associated with the pad.
*/
+struct media_entity_context;
struct media_pipeline_pad {
struct list_head list;
struct media_pipeline *pipe;
struct media_pad *pad;
+ struct media_entity_context *context;
};
/**
@@ -1205,6 +1210,7 @@ struct media_entity *media_graph_walk_next(struct media_graph *graph);
*/
__must_check int media_pipeline_start(struct media_pad *pad,
struct media_pipeline *pipe);
+
/**
* __media_pipeline_start - Mark a pipeline as streaming
*
@@ -1216,6 +1222,40 @@ __must_check int media_pipeline_start(struct media_pad *pad,
__must_check int __media_pipeline_start(struct media_pad *pad,
struct media_pipeline *pipe);
+/**
+ * media_pipeline_start_context - Mark a pipeline as streaming
+ * @pad: Starting pad
+ * @pipe: Media pipeline to be assigned to all pads in the pipeline.
+ * @context: The media device context the pipeline belongs to
+ *
+ * Mark all pads connected to a given pad through enabled links, either
+ * directly or indirectly, as streaming. The given pipeline object is assigned
+ * to every pad in the pipeline and stored in the media_pad pipe field.
+ *
+ * Calls to this function can be nested, in which case the same number of
+ * media_pipeline_stop() calls will be required to stop streaming. The
+ * pipeline pointer must be identical for all nested calls to
+ * media_pipeline_start().
+ */
+__must_check int
+media_pipeline_start_context(struct media_pad *pad,
+ struct media_pipeline *pipe,
+ struct media_device_context *context);
+
+/**
+ * __media_pipeline_start_context - Mark a pipeline as streaming
+ *
+ * @pad: Starting pad
+ * @pipe: Media pipeline to be assigned to all pads in the pipeline.
+ * @context: The media device context the pipeline belongs to
+ *
+ * ..note:: This is the non-locking version of media_pipeline_start()
+ */
+__must_check int
+__media_pipeline_start_context(struct media_pad *pad,
+ struct media_pipeline *pipe,
+ struct media_device_context *mdev_context);
+
/**
* media_pipeline_stop - Mark a pipeline as not streaming
* @pad: Starting pad
@@ -579,6 +579,48 @@ __must_check int video_device_pipeline_start(struct video_device *vdev,
__must_check int __video_device_pipeline_start(struct video_device *vdev,
struct media_pipeline *pipe);
+/**
+ * video_device_context_pipeline_start - Mark a pipeline as streaming starting
+ * from a video device context
+ * @context: The video device context that starts the streaming
+ * @pipe: Media pipeline to be assigned to all entities in the pipeline.
+ *
+ * ..note:: This is the multi-context version of video_device_pipeline_start()
+ * Documentation of this function only describes specific aspects of
+ * this version. Refer to the video_device_pipeline_start()
+ * documentation for a complete reference.
+ *
+ * Validate that all entities connected to a video device through enabled links
+ * by ensuring that a context associated with the same media device context
+ * exists for them. Increase the reference counting of each of the contexts part
+ * of the pipeline to guarantee their lifetime is maintained as long as the
+ * pipeline is streaming.
+ *
+ * Context validation and refcounting of all entities that are part of a
+ * streaming pipeline ensures that device drivers can safely access device
+ * contexts in a media device context during streaming. References to contexts
+ * retried by a call to media_device_get_entity_context(), are guaranteed to be
+ * valid as long as the pipeline is streaming. Likewise, the media device
+ * context that contains the device contexts is guaranteed to be valid as long
+ * as the pipeline is streaming.
+ */
+__must_check int
+video_device_context_pipeline_start(struct video_device_context *context,
+ struct media_pipeline *pipe);
+
+/**
+ * video_device_context_pipeline_start - Mark a pipeline as streaming starting
+ * from a video device context
+ * @context: The video device context that starts the streaming
+ * @pipe: Media pipeline to be assigned to all entities in the pipeline.
+ *
+ * ..note:: This is the non-locking version of
+ * __video_device_context_pipeline_start()
+ */
+__must_check int
+__video_device_context_pipeline_start(struct video_device_context *context,
+ struct media_pipeline *pipe);
+
/**
* video_device_pipeline_stop - Mark a pipeline as not streaming
* @vdev: Starting video device