forked from Minki/linux
media: v4l2-mem2mem: handle draining, stopped and next-buf-is-last states
Since the draining and stop phase of the HW decoder mem2mem bahaviour is now clearly defined, we can move handling of the following states to the common v4l2-mem2mem core code: - draining - stopped - next-buf-is-last By introducing the following v4l2-mem2mem APIs: - v4l2_m2m_encoder_cmd/v4l2_m2m_ioctl_encoder_cmd to handle start/stop command - v4l2_m2m_decoder_cmd/v4l2_m2m_ioctl_decoder_cmd to handle start/stop command - v4l2_m2m_update_start_streaming_state to update state on start of streaming of the de/encoder queue - v4l2_m2m_update_stop_streaming_state to update state on stop of streaming of the de/encoder queue - v4l2_m2m_last_buffer_done to make the current dest buffer as the last one And inline helpers: - v4l2_m2m_mark_stopped to mark the de/encoding process as stopped - v4l2_m2m_clear_state to clear the de/encoding state - v4l2_m2m_dst_buf_is_last to detect the current dequeued dst_buf is the last - v4l2_m2m_has_stopped to detect the de/encoding stopped state - v4l2_m2m_is_last_draining_src_buf to detect the current source buffer should be the last processing before stopping the de/encoding process The special next-buf-is-last when min_buffers != 1 case is also handled in v4l2_m2m_qbuf() by reusing the other introduced APIs. This state management has been stolen from the vicodec implementation, and is no-op for drivers not calling the v4l2_m2m_encoder_cmd or v4l2_m2m_decoder_cmd and v4l2_m2m_update_start/stop_streaming_state. The vicodec will be the first one to be converted as an example. Signed-off-by: Neil Armstrong <narmstrong@baylibre.com> Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
This commit is contained in:
parent
2fae4d6aab
commit
2b48e11386
@ -340,6 +340,11 @@ static void __v4l2_m2m_try_queue(struct v4l2_m2m_dev *m2m_dev,
|
||||
m2m_ctx->new_frame = !dst->vb2_buf.copied_timestamp ||
|
||||
dst->vb2_buf.timestamp != src->vb2_buf.timestamp;
|
||||
|
||||
if (m2m_ctx->has_stopped) {
|
||||
dprintk("Device has stopped\n");
|
||||
goto job_unlock;
|
||||
}
|
||||
|
||||
if (m2m_dev->m2m_ops->job_ready
|
||||
&& (!m2m_dev->m2m_ops->job_ready(m2m_ctx->priv))) {
|
||||
dprintk("Driver not ready\n");
|
||||
@ -556,6 +561,140 @@ int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(v4l2_m2m_querybuf);
|
||||
|
||||
/*
|
||||
* This will add the LAST flag and mark the buffer management
|
||||
* state as stopped.
|
||||
* This is called when the last capture buffer must be flagged as LAST
|
||||
* in draining mode from the encoder/decoder driver buf_queue() callback
|
||||
* or from v4l2_update_last_buf_state() when a capture buffer is available.
|
||||
*/
|
||||
void v4l2_m2m_last_buffer_done(struct v4l2_m2m_ctx *m2m_ctx,
|
||||
struct vb2_v4l2_buffer *vbuf)
|
||||
{
|
||||
vbuf->flags |= V4L2_BUF_FLAG_LAST;
|
||||
vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE);
|
||||
|
||||
v4l2_m2m_mark_stopped(m2m_ctx);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(v4l2_m2m_last_buffer_done);
|
||||
|
||||
/* When stop command is issued, update buffer management state */
|
||||
static int v4l2_update_last_buf_state(struct v4l2_m2m_ctx *m2m_ctx)
|
||||
{
|
||||
struct vb2_v4l2_buffer *next_dst_buf;
|
||||
|
||||
if (m2m_ctx->is_draining)
|
||||
return -EBUSY;
|
||||
|
||||
if (m2m_ctx->has_stopped)
|
||||
return 0;
|
||||
|
||||
m2m_ctx->last_src_buf = v4l2_m2m_last_src_buf(m2m_ctx);
|
||||
m2m_ctx->is_draining = true;
|
||||
|
||||
/*
|
||||
* The processing of the last output buffer queued before
|
||||
* the STOP command is expected to mark the buffer management
|
||||
* state as stopped with v4l2_m2m_mark_stopped().
|
||||
*/
|
||||
if (m2m_ctx->last_src_buf)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* In case the output queue is empty, try to mark the last capture
|
||||
* buffer as LAST.
|
||||
*/
|
||||
next_dst_buf = v4l2_m2m_dst_buf_remove(m2m_ctx);
|
||||
if (!next_dst_buf) {
|
||||
/*
|
||||
* Wait for the next queued one in encoder/decoder driver
|
||||
* buf_queue() callback using the v4l2_m2m_dst_buf_is_last()
|
||||
* helper or in v4l2_m2m_qbuf() if encoder/decoder is not yet
|
||||
* streaming.
|
||||
*/
|
||||
m2m_ctx->next_buf_last = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
v4l2_m2m_last_buffer_done(m2m_ctx, next_dst_buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates the encoding/decoding buffer management state, should
|
||||
* be called from encoder/decoder drivers start_streaming()
|
||||
*/
|
||||
void v4l2_m2m_update_start_streaming_state(struct v4l2_m2m_ctx *m2m_ctx,
|
||||
struct vb2_queue *q)
|
||||
{
|
||||
/* If start streaming again, untag the last output buffer */
|
||||
if (V4L2_TYPE_IS_OUTPUT(q->type))
|
||||
m2m_ctx->last_src_buf = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(v4l2_m2m_update_start_streaming_state);
|
||||
|
||||
/*
|
||||
* Updates the encoding/decoding buffer management state, should
|
||||
* be called from encoder/decoder driver stop_streaming()
|
||||
*/
|
||||
void v4l2_m2m_update_stop_streaming_state(struct v4l2_m2m_ctx *m2m_ctx,
|
||||
struct vb2_queue *q)
|
||||
{
|
||||
if (V4L2_TYPE_IS_OUTPUT(q->type)) {
|
||||
/*
|
||||
* If in draining state, either mark next dst buffer as
|
||||
* done or flag next one to be marked as done either
|
||||
* in encoder/decoder driver buf_queue() callback using
|
||||
* the v4l2_m2m_dst_buf_is_last() helper or in v4l2_m2m_qbuf()
|
||||
* if encoder/decoder is not yet streaming
|
||||
*/
|
||||
if (m2m_ctx->is_draining) {
|
||||
struct vb2_v4l2_buffer *next_dst_buf;
|
||||
|
||||
m2m_ctx->last_src_buf = NULL;
|
||||
next_dst_buf = v4l2_m2m_dst_buf_remove(m2m_ctx);
|
||||
if (!next_dst_buf)
|
||||
m2m_ctx->next_buf_last = true;
|
||||
else
|
||||
v4l2_m2m_last_buffer_done(m2m_ctx,
|
||||
next_dst_buf);
|
||||
}
|
||||
} else {
|
||||
v4l2_m2m_clear_state(m2m_ctx);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(v4l2_m2m_update_stop_streaming_state);
|
||||
|
||||
static void v4l2_m2m_force_last_buf_done(struct v4l2_m2m_ctx *m2m_ctx,
|
||||
struct vb2_queue *q)
|
||||
{
|
||||
struct vb2_buffer *vb;
|
||||
struct vb2_v4l2_buffer *vbuf;
|
||||
unsigned int i;
|
||||
|
||||
if (WARN_ON(q->is_output))
|
||||
return;
|
||||
if (list_empty(&q->queued_list))
|
||||
return;
|
||||
|
||||
vb = list_first_entry(&q->queued_list, struct vb2_buffer, queued_entry);
|
||||
for (i = 0; i < vb->num_planes; i++)
|
||||
vb2_set_plane_payload(vb, i, 0);
|
||||
|
||||
/*
|
||||
* Since the buffer hasn't been queued to the ready queue,
|
||||
* mark is active and owned before marking it LAST and DONE
|
||||
*/
|
||||
vb->state = VB2_BUF_STATE_ACTIVE;
|
||||
atomic_inc(&q->owned_by_drv_count);
|
||||
|
||||
vbuf = to_vb2_v4l2_buffer(vb);
|
||||
vbuf->field = V4L2_FIELD_NONE;
|
||||
|
||||
v4l2_m2m_last_buffer_done(m2m_ctx, vbuf);
|
||||
}
|
||||
|
||||
int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
|
||||
struct v4l2_buffer *buf)
|
||||
{
|
||||
@ -570,11 +709,25 @@ int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
|
||||
__func__);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
ret = vb2_qbuf(vq, vdev->v4l2_dev->mdev, buf);
|
||||
if (!ret && !(buf->flags & V4L2_BUF_FLAG_IN_REQUEST))
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* If the capture queue is streaming, but streaming hasn't started
|
||||
* on the device, but was asked to stop, mark the previously queued
|
||||
* buffer as DONE with LAST flag since it won't be queued on the
|
||||
* device.
|
||||
*/
|
||||
if (!V4L2_TYPE_IS_OUTPUT(vq->type) &&
|
||||
vb2_is_streaming(vq) && !vb2_start_streaming_called(vq) &&
|
||||
(v4l2_m2m_has_stopped(m2m_ctx) || v4l2_m2m_dst_buf_is_last(m2m_ctx)))
|
||||
v4l2_m2m_force_last_buf_done(m2m_ctx, vq);
|
||||
else if (!(buf->flags & V4L2_BUF_FLAG_IN_REQUEST))
|
||||
v4l2_m2m_try_schedule(m2m_ctx);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(v4l2_m2m_qbuf);
|
||||
|
||||
@ -1225,6 +1378,70 @@ int v4l2_m2m_ioctl_try_decoder_cmd(struct file *file, void *fh,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_try_decoder_cmd);
|
||||
|
||||
/*
|
||||
* Updates the encoding state on ENC_CMD_STOP/ENC_CMD_START
|
||||
* Should be called from the encoder driver encoder_cmd() callback
|
||||
*/
|
||||
int v4l2_m2m_encoder_cmd(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
|
||||
struct v4l2_encoder_cmd *ec)
|
||||
{
|
||||
if (ec->cmd != V4L2_ENC_CMD_STOP && ec->cmd != V4L2_ENC_CMD_START)
|
||||
return -EINVAL;
|
||||
|
||||
if (ec->cmd == V4L2_ENC_CMD_STOP)
|
||||
return v4l2_update_last_buf_state(m2m_ctx);
|
||||
|
||||
if (m2m_ctx->is_draining)
|
||||
return -EBUSY;
|
||||
|
||||
if (m2m_ctx->has_stopped)
|
||||
m2m_ctx->has_stopped = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(v4l2_m2m_encoder_cmd);
|
||||
|
||||
/*
|
||||
* Updates the decoding state on DEC_CMD_STOP/DEC_CMD_START
|
||||
* Should be called from the decoder driver decoder_cmd() callback
|
||||
*/
|
||||
int v4l2_m2m_decoder_cmd(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
|
||||
struct v4l2_decoder_cmd *dc)
|
||||
{
|
||||
if (dc->cmd != V4L2_DEC_CMD_STOP && dc->cmd != V4L2_DEC_CMD_START)
|
||||
return -EINVAL;
|
||||
|
||||
if (dc->cmd == V4L2_DEC_CMD_STOP)
|
||||
return v4l2_update_last_buf_state(m2m_ctx);
|
||||
|
||||
if (m2m_ctx->is_draining)
|
||||
return -EBUSY;
|
||||
|
||||
if (m2m_ctx->has_stopped)
|
||||
m2m_ctx->has_stopped = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(v4l2_m2m_decoder_cmd);
|
||||
|
||||
int v4l2_m2m_ioctl_encoder_cmd(struct file *file, void *priv,
|
||||
struct v4l2_encoder_cmd *ec)
|
||||
{
|
||||
struct v4l2_fh *fh = file->private_data;
|
||||
|
||||
return v4l2_m2m_encoder_cmd(file, fh->m2m_ctx, ec);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_encoder_cmd);
|
||||
|
||||
int v4l2_m2m_ioctl_decoder_cmd(struct file *file, void *priv,
|
||||
struct v4l2_decoder_cmd *dc)
|
||||
{
|
||||
struct v4l2_fh *fh = file->private_data;
|
||||
|
||||
return v4l2_m2m_decoder_cmd(file, fh->m2m_ctx, dc);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_decoder_cmd);
|
||||
|
||||
int v4l2_m2m_ioctl_stateless_try_decoder_cmd(struct file *file, void *fh,
|
||||
struct v4l2_decoder_cmd *dc)
|
||||
{
|
||||
|
@ -80,6 +80,10 @@ struct v4l2_m2m_queue_ctx {
|
||||
* for an existing frame. This is always true unless
|
||||
* V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF is set, which
|
||||
* indicates slicing support.
|
||||
* @is_draining: indicates device is in draining phase
|
||||
* @last_src_buf: indicate the last source buffer for draining
|
||||
* @next_buf_last: next capture queud buffer will be tagged as last
|
||||
* @has_stopped: indicate the device has been stopped
|
||||
* @m2m_dev: opaque pointer to the internal data to handle M2M context
|
||||
* @cap_q_ctx: Capture (output to memory) queue context
|
||||
* @out_q_ctx: Output (input from memory) queue context
|
||||
@ -98,6 +102,11 @@ struct v4l2_m2m_ctx {
|
||||
|
||||
bool new_frame;
|
||||
|
||||
bool is_draining;
|
||||
struct vb2_v4l2_buffer *last_src_buf;
|
||||
bool next_buf_last;
|
||||
bool has_stopped;
|
||||
|
||||
/* internal use only */
|
||||
struct v4l2_m2m_dev *m2m_dev;
|
||||
|
||||
@ -215,6 +224,86 @@ v4l2_m2m_buf_done(struct vb2_v4l2_buffer *buf, enum vb2_buffer_state state)
|
||||
vb2_buffer_done(&buf->vb2_buf, state);
|
||||
}
|
||||
|
||||
/**
|
||||
* v4l2_m2m_clear_state() - clear encoding/decoding state
|
||||
*
|
||||
* @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx
|
||||
*/
|
||||
static inline void
|
||||
v4l2_m2m_clear_state(struct v4l2_m2m_ctx *m2m_ctx)
|
||||
{
|
||||
m2m_ctx->next_buf_last = false;
|
||||
m2m_ctx->is_draining = false;
|
||||
m2m_ctx->has_stopped = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* v4l2_m2m_mark_stopped() - set current encoding/decoding state as stopped
|
||||
*
|
||||
* @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx
|
||||
*/
|
||||
static inline void
|
||||
v4l2_m2m_mark_stopped(struct v4l2_m2m_ctx *m2m_ctx)
|
||||
{
|
||||
m2m_ctx->next_buf_last = false;
|
||||
m2m_ctx->is_draining = false;
|
||||
m2m_ctx->has_stopped = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* v4l2_m2m_dst_buf_is_last() - return the current encoding/decoding session
|
||||
* draining management state of next queued capture buffer
|
||||
*
|
||||
* This last capture buffer should be tagged with V4L2_BUF_FLAG_LAST to notify
|
||||
* the end of the capture session.
|
||||
*
|
||||
* @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx
|
||||
*/
|
||||
static inline bool
|
||||
v4l2_m2m_dst_buf_is_last(struct v4l2_m2m_ctx *m2m_ctx)
|
||||
{
|
||||
return m2m_ctx->is_draining && m2m_ctx->next_buf_last;
|
||||
}
|
||||
|
||||
/**
|
||||
* v4l2_m2m_has_stopped() - return the current encoding/decoding session
|
||||
* stopped state
|
||||
*
|
||||
* @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx
|
||||
*/
|
||||
static inline bool
|
||||
v4l2_m2m_has_stopped(struct v4l2_m2m_ctx *m2m_ctx)
|
||||
{
|
||||
return m2m_ctx->has_stopped;
|
||||
}
|
||||
|
||||
/**
|
||||
* v4l2_m2m_is_last_draining_src_buf() - return the output buffer draining
|
||||
* state in the current encoding/decoding session
|
||||
*
|
||||
* This will identify the last output buffer queued before a session stop
|
||||
* was required, leading to an actual encoding/decoding session stop state
|
||||
* in the encoding/decoding process after being processed.
|
||||
*
|
||||
* @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx
|
||||
* @vbuf: pointer to struct &v4l2_buffer
|
||||
*/
|
||||
static inline bool
|
||||
v4l2_m2m_is_last_draining_src_buf(struct v4l2_m2m_ctx *m2m_ctx,
|
||||
struct vb2_v4l2_buffer *vbuf)
|
||||
{
|
||||
return m2m_ctx->is_draining && vbuf == m2m_ctx->last_src_buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* v4l2_m2m_last_buffer_done() - marks the buffer with LAST flag and DONE
|
||||
*
|
||||
* @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx
|
||||
* @vbuf: pointer to struct &v4l2_buffer
|
||||
*/
|
||||
void v4l2_m2m_last_buffer_done(struct v4l2_m2m_ctx *m2m_ctx,
|
||||
struct vb2_v4l2_buffer *vbuf);
|
||||
|
||||
/**
|
||||
* v4l2_m2m_reqbufs() - multi-queue-aware REQBUFS multiplexer
|
||||
*
|
||||
@ -312,6 +401,46 @@ int v4l2_m2m_streamon(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
|
||||
int v4l2_m2m_streamoff(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
|
||||
enum v4l2_buf_type type);
|
||||
|
||||
/**
|
||||
* v4l2_m2m_update_start_streaming_state() - update the encoding/decoding
|
||||
* session state when a start of streaming of a video queue is requested
|
||||
*
|
||||
* @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx
|
||||
* @q: queue
|
||||
*/
|
||||
void v4l2_m2m_update_start_streaming_state(struct v4l2_m2m_ctx *m2m_ctx,
|
||||
struct vb2_queue *q);
|
||||
|
||||
/**
|
||||
* v4l2_m2m_update_stop_streaming_state() - update the encoding/decoding
|
||||
* session state when a stop of streaming of a video queue is requested
|
||||
*
|
||||
* @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx
|
||||
* @q: queue
|
||||
*/
|
||||
void v4l2_m2m_update_stop_streaming_state(struct v4l2_m2m_ctx *m2m_ctx,
|
||||
struct vb2_queue *q);
|
||||
|
||||
/**
|
||||
* v4l2_m2m_encoder_cmd() - execute an encoder command
|
||||
*
|
||||
* @file: pointer to struct &file
|
||||
* @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx
|
||||
* @ec: pointer to the encoder command
|
||||
*/
|
||||
int v4l2_m2m_encoder_cmd(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
|
||||
struct v4l2_encoder_cmd *ec);
|
||||
|
||||
/**
|
||||
* v4l2_m2m_decoder_cmd() - execute a decoder command
|
||||
*
|
||||
* @file: pointer to struct &file
|
||||
* @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx
|
||||
* @dc: pointer to the decoder command
|
||||
*/
|
||||
int v4l2_m2m_decoder_cmd(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
|
||||
struct v4l2_decoder_cmd *dc);
|
||||
|
||||
/**
|
||||
* v4l2_m2m_poll() - poll replacement, for destination buffers only
|
||||
*
|
||||
@ -704,6 +833,10 @@ int v4l2_m2m_ioctl_streamon(struct file *file, void *fh,
|
||||
enum v4l2_buf_type type);
|
||||
int v4l2_m2m_ioctl_streamoff(struct file *file, void *fh,
|
||||
enum v4l2_buf_type type);
|
||||
int v4l2_m2m_ioctl_encoder_cmd(struct file *file, void *fh,
|
||||
struct v4l2_encoder_cmd *ec);
|
||||
int v4l2_m2m_ioctl_decoder_cmd(struct file *file, void *fh,
|
||||
struct v4l2_decoder_cmd *dc);
|
||||
int v4l2_m2m_ioctl_try_encoder_cmd(struct file *file, void *fh,
|
||||
struct v4l2_encoder_cmd *ec);
|
||||
int v4l2_m2m_ioctl_try_decoder_cmd(struct file *file, void *fh,
|
||||
|
Loading…
Reference in New Issue
Block a user