|
@@ -399,14 +399,10 @@ static int vsp1_video_pipeline_build(struct vsp1_pipeline *pipe,
|
|
|
unsigned int i;
|
|
|
int ret;
|
|
|
|
|
|
- mutex_lock(&mdev->graph_mutex);
|
|
|
-
|
|
|
/* Walk the graph to locate the entities and video nodes. */
|
|
|
ret = media_entity_graph_walk_init(&graph, mdev);
|
|
|
- if (ret) {
|
|
|
- mutex_unlock(&mdev->graph_mutex);
|
|
|
+ if (ret)
|
|
|
return ret;
|
|
|
- }
|
|
|
|
|
|
media_entity_graph_walk_start(&graph, entity);
|
|
|
|
|
@@ -439,15 +435,11 @@ static int vsp1_video_pipeline_build(struct vsp1_pipeline *pipe,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- mutex_unlock(&mdev->graph_mutex);
|
|
|
-
|
|
|
media_entity_graph_walk_cleanup(&graph);
|
|
|
|
|
|
/* We need one output and at least one input. */
|
|
|
- if (pipe->num_inputs == 0 || !pipe->output) {
|
|
|
- ret = -EPIPE;
|
|
|
- goto error;
|
|
|
- }
|
|
|
+ if (pipe->num_inputs == 0 || !pipe->output)
|
|
|
+ return -EPIPE;
|
|
|
|
|
|
/* Follow links downstream for each input and make sure the graph
|
|
|
* contains no loop and that all branches end at the output WPF.
|
|
@@ -459,47 +451,66 @@ static int vsp1_video_pipeline_build(struct vsp1_pipeline *pipe,
|
|
|
ret = vsp1_video_pipeline_build_branch(pipe, pipe->inputs[i],
|
|
|
pipe->output);
|
|
|
if (ret < 0)
|
|
|
- goto error;
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
-
|
|
|
-error:
|
|
|
- vsp1_pipeline_reset(pipe);
|
|
|
- return ret;
|
|
|
}
|
|
|
|
|
|
static int vsp1_video_pipeline_init(struct vsp1_pipeline *pipe,
|
|
|
struct vsp1_video *video)
|
|
|
{
|
|
|
+ vsp1_pipeline_init(pipe);
|
|
|
+
|
|
|
+ pipe->frame_end = vsp1_video_pipeline_frame_end;
|
|
|
+
|
|
|
+ return vsp1_video_pipeline_build(pipe, video);
|
|
|
+}
|
|
|
+
|
|
|
+static struct vsp1_pipeline *vsp1_video_pipeline_get(struct vsp1_video *video)
|
|
|
+{
|
|
|
+ struct vsp1_pipeline *pipe;
|
|
|
int ret;
|
|
|
|
|
|
- mutex_lock(&pipe->lock);
|
|
|
+ /* Get a pipeline object for the video node. If a pipeline has already
|
|
|
+ * been allocated just increment its reference count and return it.
|
|
|
+ * Otherwise allocate a new pipeline and initialize it, it will be freed
|
|
|
+ * when the last reference is released.
|
|
|
+ */
|
|
|
+ if (!video->rwpf->pipe) {
|
|
|
+ pipe = kzalloc(sizeof(*pipe), GFP_KERNEL);
|
|
|
+ if (!pipe)
|
|
|
+ return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
- /* If we're the first user build and validate the pipeline. */
|
|
|
- if (pipe->use_count == 0) {
|
|
|
- ret = vsp1_video_pipeline_build(pipe, video);
|
|
|
- if (ret < 0)
|
|
|
- goto done;
|
|
|
+ ret = vsp1_video_pipeline_init(pipe, video);
|
|
|
+ if (ret < 0) {
|
|
|
+ vsp1_pipeline_reset(pipe);
|
|
|
+ kfree(pipe);
|
|
|
+ return ERR_PTR(ret);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ pipe = video->rwpf->pipe;
|
|
|
+ kref_get(&pipe->kref);
|
|
|
}
|
|
|
|
|
|
- pipe->use_count++;
|
|
|
- ret = 0;
|
|
|
-
|
|
|
-done:
|
|
|
- mutex_unlock(&pipe->lock);
|
|
|
- return ret;
|
|
|
+ return pipe;
|
|
|
}
|
|
|
|
|
|
-static void vsp1_video_pipeline_cleanup(struct vsp1_pipeline *pipe)
|
|
|
+static void vsp1_video_pipeline_release(struct kref *kref)
|
|
|
{
|
|
|
- mutex_lock(&pipe->lock);
|
|
|
+ struct vsp1_pipeline *pipe = container_of(kref, typeof(*pipe), kref);
|
|
|
|
|
|
- /* If we're the last user clean up the pipeline. */
|
|
|
- if (--pipe->use_count == 0)
|
|
|
- vsp1_pipeline_reset(pipe);
|
|
|
+ vsp1_pipeline_reset(pipe);
|
|
|
+ kfree(pipe);
|
|
|
+}
|
|
|
|
|
|
- mutex_unlock(&pipe->lock);
|
|
|
+static void vsp1_video_pipeline_put(struct vsp1_pipeline *pipe)
|
|
|
+{
|
|
|
+ struct media_device *mdev = &pipe->output->entity.vsp1->media_dev;
|
|
|
+
|
|
|
+ mutex_lock(&mdev->graph_mutex);
|
|
|
+ kref_put(&pipe->kref, vsp1_video_pipeline_release);
|
|
|
+ mutex_unlock(&mdev->graph_mutex);
|
|
|
}
|
|
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
@@ -674,8 +685,8 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq)
|
|
|
}
|
|
|
mutex_unlock(&pipe->lock);
|
|
|
|
|
|
- vsp1_video_pipeline_cleanup(pipe);
|
|
|
media_entity_pipeline_stop(&video->video.entity);
|
|
|
+ vsp1_video_pipeline_put(pipe);
|
|
|
|
|
|
/* Remove all buffers from the IRQ queue. */
|
|
|
spin_lock_irqsave(&video->irqlock, flags);
|
|
@@ -787,6 +798,7 @@ vsp1_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
|
|
|
{
|
|
|
struct v4l2_fh *vfh = file->private_data;
|
|
|
struct vsp1_video *video = to_vsp1_video(vfh->vdev);
|
|
|
+ struct media_device *mdev = &video->vsp1->media_dev;
|
|
|
struct vsp1_pipeline *pipe;
|
|
|
int ret;
|
|
|
|
|
@@ -795,20 +807,25 @@ vsp1_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
|
|
|
|
|
|
video->sequence = 0;
|
|
|
|
|
|
- /* Start streaming on the pipeline. No link touching an entity in the
|
|
|
- * pipeline can be activated or deactivated once streaming is started.
|
|
|
- *
|
|
|
- * Use the VSP1 pipeline object embedded in the first video object that
|
|
|
- * starts streaming.
|
|
|
- *
|
|
|
- * FIXME: This is racy, the ioctl is only protected by the video node
|
|
|
- * lock.
|
|
|
+ /* Get a pipeline for the video node and start streaming on it. No link
|
|
|
+ * touching an entity in the pipeline can be activated or deactivated
|
|
|
+ * once streaming is started.
|
|
|
*/
|
|
|
- pipe = video->rwpf->pipe ? video->rwpf->pipe : &video->pipe;
|
|
|
+ mutex_lock(&mdev->graph_mutex);
|
|
|
|
|
|
- ret = media_entity_pipeline_start(&video->video.entity, &pipe->pipe);
|
|
|
- if (ret < 0)
|
|
|
- return ret;
|
|
|
+ pipe = vsp1_video_pipeline_get(video);
|
|
|
+ if (IS_ERR(pipe)) {
|
|
|
+ mutex_unlock(&mdev->graph_mutex);
|
|
|
+ return PTR_ERR(pipe);
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = __media_entity_pipeline_start(&video->video.entity, &pipe->pipe);
|
|
|
+ if (ret < 0) {
|
|
|
+ mutex_unlock(&mdev->graph_mutex);
|
|
|
+ goto err_pipe;
|
|
|
+ }
|
|
|
+
|
|
|
+ mutex_unlock(&mdev->graph_mutex);
|
|
|
|
|
|
/* Verify that the configured format matches the output of the connected
|
|
|
* subdev.
|
|
@@ -817,21 +834,17 @@ vsp1_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
|
|
|
if (ret < 0)
|
|
|
goto err_stop;
|
|
|
|
|
|
- ret = vsp1_video_pipeline_init(pipe, video);
|
|
|
- if (ret < 0)
|
|
|
- goto err_stop;
|
|
|
-
|
|
|
/* Start the queue. */
|
|
|
ret = vb2_streamon(&video->queue, type);
|
|
|
if (ret < 0)
|
|
|
- goto err_cleanup;
|
|
|
+ goto err_stop;
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
-err_cleanup:
|
|
|
- vsp1_video_pipeline_cleanup(pipe);
|
|
|
err_stop:
|
|
|
media_entity_pipeline_stop(&video->video.entity);
|
|
|
+err_pipe:
|
|
|
+ vsp1_video_pipeline_put(pipe);
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
@@ -947,9 +960,6 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1,
|
|
|
spin_lock_init(&video->irqlock);
|
|
|
INIT_LIST_HEAD(&video->irqqueue);
|
|
|
|
|
|
- vsp1_pipeline_init(&video->pipe);
|
|
|
- video->pipe.frame_end = vsp1_video_pipeline_frame_end;
|
|
|
-
|
|
|
/* Initialize the media entity... */
|
|
|
ret = media_entity_pads_init(&video->video.entity, 1, &video->pad);
|
|
|
if (ret < 0)
|