aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/bcm27xx/patches-5.4/950-0495-media-v4l2-mem2mem-support-held-capture-buffers.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/bcm27xx/patches-5.4/950-0495-media-v4l2-mem2mem-support-held-capture-buffers.patch')
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0495-media-v4l2-mem2mem-support-held-capture-buffers.patch260
1 files changed, 260 insertions, 0 deletions
diff --git a/target/linux/bcm27xx/patches-5.4/950-0495-media-v4l2-mem2mem-support-held-capture-buffers.patch b/target/linux/bcm27xx/patches-5.4/950-0495-media-v4l2-mem2mem-support-held-capture-buffers.patch
new file mode 100644
index 0000000000..bb66baf07d
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0495-media-v4l2-mem2mem-support-held-capture-buffers.patch
@@ -0,0 +1,260 @@
+From dc9b786e4b9a1262b536b3c9d0fa88e34a2b3f8f Mon Sep 17 00:00:00 2001
+From: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Date: Fri, 11 Oct 2019 06:32:41 -0300
+Subject: [PATCH] media: v4l2-mem2mem: support held capture buffers
+
+Commit f8cca8c97a63d77f48334cde81d15014f43530ef upstream.
+
+Check for held buffers that are ready to be returned to vb2 in
+__v4l2_m2m_try_queue(). This avoids drivers having to handle this
+case.
+
+Add v4l2_m2m_buf_done_and_job_finish() to correctly return source
+and destination buffers and mark the job as finished while taking
+a held destination buffer into account (i.e. that buffer won't be
+returned). This has to be done while job_spinlock is held to avoid
+race conditions.
+
+Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
+---
+ drivers/media/v4l2-core/v4l2-mem2mem.c | 130 ++++++++++++++++++-------
+ include/media/v4l2-mem2mem.h | 33 ++++++-
+ 2 files changed, 128 insertions(+), 35 deletions(-)
+
+--- a/drivers/media/v4l2-core/v4l2-mem2mem.c
++++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
+@@ -284,7 +284,8 @@ static void v4l2_m2m_try_run(struct v4l2
+ static void __v4l2_m2m_try_queue(struct v4l2_m2m_dev *m2m_dev,
+ struct v4l2_m2m_ctx *m2m_ctx)
+ {
+- unsigned long flags_job, flags_out, flags_cap;
++ unsigned long flags_job;
++ struct vb2_v4l2_buffer *dst, *src;
+
+ dprintk("Trying to schedule a job for m2m_ctx: %p\n", m2m_ctx);
+
+@@ -307,20 +308,30 @@ static void __v4l2_m2m_try_queue(struct
+ goto job_unlock;
+ }
+
+- spin_lock_irqsave(&m2m_ctx->out_q_ctx.rdy_spinlock, flags_out);
+- if (list_empty(&m2m_ctx->out_q_ctx.rdy_queue)
+- && !m2m_ctx->out_q_ctx.buffered) {
++ src = v4l2_m2m_next_src_buf(m2m_ctx);
++ dst = v4l2_m2m_next_dst_buf(m2m_ctx);
++ if (!src && !m2m_ctx->out_q_ctx.buffered) {
+ dprintk("No input buffers available\n");
+- goto out_unlock;
++ goto job_unlock;
+ }
+- spin_lock_irqsave(&m2m_ctx->cap_q_ctx.rdy_spinlock, flags_cap);
+- if (list_empty(&m2m_ctx->cap_q_ctx.rdy_queue)
+- && !m2m_ctx->cap_q_ctx.buffered) {
++ if (!dst && !m2m_ctx->cap_q_ctx.buffered) {
+ dprintk("No output buffers available\n");
+- goto cap_unlock;
++ goto job_unlock;
++ }
++
++ if (src && dst &&
++ dst->is_held && dst->vb2_buf.copied_timestamp &&
++ dst->vb2_buf.timestamp != src->vb2_buf.timestamp) {
++ dst->is_held = false;
++ v4l2_m2m_dst_buf_remove(m2m_ctx);
++ v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE);
++ dst = v4l2_m2m_next_dst_buf(m2m_ctx);
++
++ if (!dst && !m2m_ctx->cap_q_ctx.buffered) {
++ dprintk("No output buffers available after returning held buffer\n");
++ goto job_unlock;
++ }
+ }
+- spin_unlock_irqrestore(&m2m_ctx->cap_q_ctx.rdy_spinlock, flags_cap);
+- spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, flags_out);
+
+ if (m2m_dev->m2m_ops->job_ready
+ && (!m2m_dev->m2m_ops->job_ready(m2m_ctx->priv))) {
+@@ -331,13 +342,6 @@ static void __v4l2_m2m_try_queue(struct
+ list_add_tail(&m2m_ctx->queue, &m2m_dev->job_queue);
+ m2m_ctx->job_flags |= TRANS_QUEUED;
+
+- spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
+- return;
+-
+-cap_unlock:
+- spin_unlock_irqrestore(&m2m_ctx->cap_q_ctx.rdy_spinlock, flags_cap);
+-out_unlock:
+- spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, flags_out);
+ job_unlock:
+ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
+ }
+@@ -412,37 +416,97 @@ static void v4l2_m2m_cancel_job(struct v
+ }
+ }
+
+-void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev,
+- struct v4l2_m2m_ctx *m2m_ctx)
++/*
++ * Schedule the next job, called from v4l2_m2m_job_finish() or
++ * v4l2_m2m_buf_done_and_job_finish().
++ */
++static void v4l2_m2m_schedule_next_job(struct v4l2_m2m_dev *m2m_dev,
++ struct v4l2_m2m_ctx *m2m_ctx)
+ {
+- unsigned long flags;
++ /*
++ * This instance might have more buffers ready, but since we do not
++ * allow more than one job on the job_queue per instance, each has
++ * to be scheduled separately after the previous one finishes.
++ */
++ __v4l2_m2m_try_queue(m2m_dev, m2m_ctx);
+
+- spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
++ /*
++ * We might be running in atomic context,
++ * but the job must be run in non-atomic context.
++ */
++ schedule_work(&m2m_dev->job_work);
++}
++
++/*
++ * Assumes job_spinlock is held, called from v4l2_m2m_job_finish() or
++ * v4l2_m2m_buf_done_and_job_finish().
++ */
++static bool _v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev,
++ struct v4l2_m2m_ctx *m2m_ctx)
++{
+ if (!m2m_dev->curr_ctx || m2m_dev->curr_ctx != m2m_ctx) {
+- spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+ dprintk("Called by an instance not currently running\n");
+- return;
++ return false;
+ }
+
+ list_del(&m2m_dev->curr_ctx->queue);
+ m2m_dev->curr_ctx->job_flags &= ~(TRANS_QUEUED | TRANS_RUNNING);
+ wake_up(&m2m_dev->curr_ctx->finished);
+ m2m_dev->curr_ctx = NULL;
++ return true;
++}
+
+- spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+-
+- /* This instance might have more buffers ready, but since we do not
+- * allow more than one job on the job_queue per instance, each has
+- * to be scheduled separately after the previous one finishes. */
+- __v4l2_m2m_try_queue(m2m_dev, m2m_ctx);
++void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev,
++ struct v4l2_m2m_ctx *m2m_ctx)
++{
++ unsigned long flags;
++ bool schedule_next;
+
+- /* We might be running in atomic context,
+- * but the job must be run in non-atomic context.
++ /*
++ * This function should not be used for drivers that support
++ * holding capture buffers. Those should use
++ * v4l2_m2m_buf_done_and_job_finish() instead.
+ */
+- schedule_work(&m2m_dev->job_work);
++ WARN_ON(m2m_ctx->cap_q_ctx.q.subsystem_flags &
++ VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF);
++ spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
++ schedule_next = _v4l2_m2m_job_finish(m2m_dev, m2m_ctx);
++ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
++
++ if (schedule_next)
++ v4l2_m2m_schedule_next_job(m2m_dev, m2m_ctx);
+ }
+ EXPORT_SYMBOL(v4l2_m2m_job_finish);
+
++void v4l2_m2m_buf_done_and_job_finish(struct v4l2_m2m_dev *m2m_dev,
++ struct v4l2_m2m_ctx *m2m_ctx,
++ enum vb2_buffer_state state)
++{
++ struct vb2_v4l2_buffer *src_buf, *dst_buf;
++ bool schedule_next = false;
++ unsigned long flags;
++
++ spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
++ src_buf = v4l2_m2m_src_buf_remove(m2m_ctx);
++ dst_buf = v4l2_m2m_next_dst_buf(m2m_ctx);
++
++ if (WARN_ON(!src_buf || !dst_buf))
++ goto unlock;
++ v4l2_m2m_buf_done(src_buf, state);
++ dst_buf->is_held = src_buf->flags & V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF;
++ if (!dst_buf->is_held) {
++ v4l2_m2m_dst_buf_remove(m2m_ctx);
++ v4l2_m2m_buf_done(dst_buf, state);
++ }
++ schedule_next = _v4l2_m2m_job_finish(m2m_dev, m2m_ctx);
++unlock:
++ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
++
++ if (schedule_next)
++ v4l2_m2m_schedule_next_job(m2m_dev, m2m_ctx);
++}
++EXPORT_SYMBOL(v4l2_m2m_buf_done_and_job_finish);
++
+ int v4l2_m2m_reqbufs(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
+ struct v4l2_requestbuffers *reqbufs)
+ {
+--- a/include/media/v4l2-mem2mem.h
++++ b/include/media/v4l2-mem2mem.h
+@@ -21,7 +21,8 @@
+ * callback.
+ * The job does NOT have to end before this callback returns
+ * (and it will be the usual case). When the job finishes,
+- * v4l2_m2m_job_finish() has to be called.
++ * v4l2_m2m_job_finish() or v4l2_m2m_buf_done_and_job_finish()
++ * has to be called.
+ * @job_ready: optional. Should return 0 if the driver does not have a job
+ * fully prepared to run yet (i.e. it will not be able to finish a
+ * transaction without sleeping). If not provided, it will be
+@@ -33,7 +34,8 @@
+ * stop the device safely; e.g. in the next interrupt handler),
+ * even if the transaction would not have been finished by then.
+ * After the driver performs the necessary steps, it has to call
+- * v4l2_m2m_job_finish() (as if the transaction ended normally).
++ * v4l2_m2m_job_finish() or v4l2_m2m_buf_done_and_job_finish() as
++ * if the transaction ended normally.
+ * This function does not have to (and will usually not) wait
+ * until the device enters a state when it can be stopped.
+ */
+@@ -173,6 +175,33 @@ void v4l2_m2m_try_schedule(struct v4l2_m
+ void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev,
+ struct v4l2_m2m_ctx *m2m_ctx);
+
++/**
++ * v4l2_m2m_buf_done_and_job_finish() - return source/destination buffers with
++ * state and inform the framework that a job has been finished and have it
++ * clean up
++ *
++ * @m2m_dev: opaque pointer to the internal data to handle M2M context
++ * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx
++ * @state: vb2 buffer state passed to v4l2_m2m_buf_done().
++ *
++ * Drivers that set V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF must use this
++ * function instead of job_finish() to take held buffers into account. It is
++ * optional for other drivers.
++ *
++ * This function removes the source buffer from the ready list and returns
++ * it with the given state. The same is done for the destination buffer, unless
++ * it is marked 'held'. In that case the buffer is kept on the ready list.
++ *
++ * After that the job is finished (see job_finish()).
++ *
++ * This allows for multiple output buffers to be used to fill in a single
++ * capture buffer. This is typically used by stateless decoders where
++ * multiple e.g. H.264 slices contribute to a single decoded frame.
++ */
++void v4l2_m2m_buf_done_and_job_finish(struct v4l2_m2m_dev *m2m_dev,
++ struct v4l2_m2m_ctx *m2m_ctx,
++ enum vb2_buffer_state state);
++
+ static inline void
+ v4l2_m2m_buf_done(struct vb2_v4l2_buffer *buf, enum vb2_buffer_state state)
+ {