Commit 876f123b authored by Maxime Jourdan's avatar Maxime Jourdan Committed by Mauro Carvalho Chehab

media: meson: vdec: bring up to compliance

Add all the necessary bits to pass v4l2-compliance in stateful decoding
mode.

Mostly includes tracking the state of the decoder, allowing the OUTPUT
queue to stream while the CAPTURE queue is inactive, handling resolution
change events, draining with V4L2_DEC_CMD_STOP, copying more metadata
from the src buffers to the dst buffers, etc.
Signed-off-by: default avatarMaxime Jourdan <mjourdan@baylibre.com>
Signed-off-by: default avatarNeil Armstrong <narmstrong@baylibre.com>
Signed-off-by: default avatarHans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab+huawei@kernel.org>
parent d4d137de
...@@ -79,22 +79,28 @@ static irqreturn_t esparser_isr(int irq, void *dev) ...@@ -79,22 +79,28 @@ static irqreturn_t esparser_isr(int irq, void *dev)
* Also append a start code 000001ff at the end to trigger * Also append a start code 000001ff at the end to trigger
* the ESPARSER interrupt. * the ESPARSER interrupt.
*/ */
static u32 esparser_pad_start_code(struct vb2_buffer *vb) static u32 esparser_pad_start_code(struct amvdec_core *core, struct vb2_buffer *vb)
{ {
u32 payload_size = vb2_get_plane_payload(vb, 0); u32 payload_size = vb2_get_plane_payload(vb, 0);
u32 pad_size = 0; u32 pad_size = 0;
u8 *vaddr = vb2_plane_vaddr(vb, 0) + payload_size; u8 *vaddr = vb2_plane_vaddr(vb, 0);
if (payload_size < ESPARSER_MIN_PACKET_SIZE) { if (payload_size < ESPARSER_MIN_PACKET_SIZE) {
pad_size = ESPARSER_MIN_PACKET_SIZE - payload_size; pad_size = ESPARSER_MIN_PACKET_SIZE - payload_size;
memset(vaddr, 0, pad_size); memset(vaddr + payload_size, 0, pad_size);
} }
memset(vaddr + pad_size, 0, SEARCH_PATTERN_LEN); if ((payload_size + pad_size + SEARCH_PATTERN_LEN) >
vaddr[pad_size] = 0x00; vb2_plane_size(vb, 0)) {
vaddr[pad_size + 1] = 0x00; dev_warn(core->dev, "%s: unable to pad start code\n", __func__);
vaddr[pad_size + 2] = 0x01; return pad_size;
vaddr[pad_size + 3] = 0xff; }
memset(vaddr + payload_size + pad_size, 0, SEARCH_PATTERN_LEN);
vaddr[payload_size + pad_size] = 0x00;
vaddr[payload_size + pad_size + 1] = 0x00;
vaddr[payload_size + pad_size + 2] = 0x01;
vaddr[payload_size + pad_size + 3] = 0xff;
return pad_size; return pad_size;
} }
...@@ -180,31 +186,27 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf) ...@@ -180,31 +186,27 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf)
int ret; int ret;
struct vb2_buffer *vb = &vbuf->vb2_buf; struct vb2_buffer *vb = &vbuf->vb2_buf;
struct amvdec_core *core = sess->core; struct amvdec_core *core = sess->core;
struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops;
u32 num_dst_bufs = 0;
u32 payload_size = vb2_get_plane_payload(vb, 0); u32 payload_size = vb2_get_plane_payload(vb, 0);
dma_addr_t phy = vb2_dma_contig_plane_dma_addr(vb, 0); dma_addr_t phy = vb2_dma_contig_plane_dma_addr(vb, 0);
u32 offset; u32 offset;
u32 pad_size; u32 pad_size;
if (codec_ops->num_pending_bufs) if (esparser_vififo_get_free_space(sess) < payload_size)
num_dst_bufs = codec_ops->num_pending_bufs(sess);
num_dst_bufs += v4l2_m2m_num_dst_bufs_ready(sess->m2m_ctx);
if (esparser_vififo_get_free_space(sess) < payload_size ||
atomic_read(&sess->esparser_queued_bufs) >= num_dst_bufs)
return -EAGAIN; return -EAGAIN;
v4l2_m2m_src_buf_remove_by_buf(sess->m2m_ctx, vbuf); v4l2_m2m_src_buf_remove_by_buf(sess->m2m_ctx, vbuf);
offset = esparser_get_offset(sess); offset = esparser_get_offset(sess);
amvdec_add_ts_reorder(sess, vb->timestamp, offset); amvdec_add_ts(sess, vb->timestamp, vbuf->timecode, offset, vbuf->flags);
dev_dbg(core->dev, "esparser: ts = %llu pld_size = %u offset = %08X\n", dev_dbg(core->dev, "esparser: ts = %llu pld_size = %u offset = %08X flags = %08X\n",
vb->timestamp, payload_size, offset); vb->timestamp, payload_size, offset, vbuf->flags);
vbuf->flags = 0;
vbuf->field = V4L2_FIELD_NONE;
vbuf->sequence = sess->sequence_out++;
pad_size = esparser_pad_start_code(vb); pad_size = esparser_pad_start_code(core, vb);
ret = esparser_write_data(core, phy, payload_size + pad_size); ret = esparser_write_data(core, phy, payload_size + pad_size);
if (ret <= 0) { if (ret <= 0) {
...@@ -216,19 +218,7 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf) ...@@ -216,19 +218,7 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf)
return 0; return 0;
} }
/* We need to wait until we parse the first keyframe.
* All buffers prior to the first keyframe must be dropped.
*/
if (!sess->keyframe_found)
usleep_range(1000, 2000);
if (sess->keyframe_found)
atomic_inc(&sess->esparser_queued_bufs); atomic_inc(&sess->esparser_queued_bufs);
else
amvdec_remove_ts(sess, vb->timestamp);
vbuf->flags = 0;
vbuf->field = V4L2_FIELD_NONE;
v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE); v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
return 0; return 0;
......
...@@ -168,7 +168,10 @@ static void process_num_buffers(struct vb2_queue *q, ...@@ -168,7 +168,10 @@ static void process_num_buffers(struct vb2_queue *q,
{ {
const struct amvdec_format *fmt_out = sess->fmt_out; const struct amvdec_format *fmt_out = sess->fmt_out;
unsigned int buffers_total = q->num_buffers + *num_buffers; unsigned int buffers_total = q->num_buffers + *num_buffers;
u32 min_buf_capture = v4l2_ctrl_g_ctrl(sess->ctrl_min_buf_capture);
if (q->num_buffers + *num_buffers < min_buf_capture)
*num_buffers = min_buf_capture - q->num_buffers;
if (is_reqbufs && buffers_total < fmt_out->min_buffers) if (is_reqbufs && buffers_total < fmt_out->min_buffers)
*num_buffers = fmt_out->min_buffers - q->num_buffers; *num_buffers = fmt_out->min_buffers - q->num_buffers;
if (buffers_total > fmt_out->max_buffers) if (buffers_total > fmt_out->max_buffers)
...@@ -193,7 +196,8 @@ static int vdec_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, ...@@ -193,7 +196,8 @@ static int vdec_queue_setup(struct vb2_queue *q, unsigned int *num_buffers,
if (*num_planes) { if (*num_planes) {
switch (q->type) { switch (q->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
if (*num_planes != 1 || sizes[0] < output_size) if (*num_planes != 1 ||
sizes[0] < sess->src_buffer_size)
return -EINVAL; return -EINVAL;
break; break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
...@@ -224,7 +228,7 @@ static int vdec_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, ...@@ -224,7 +228,7 @@ static int vdec_queue_setup(struct vb2_queue *q, unsigned int *num_buffers,
switch (q->type) { switch (q->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
sizes[0] = amvdec_get_output_size(sess); sizes[0] = sess->src_buffer_size;
*num_planes = 1; *num_planes = 1;
break; break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
...@@ -250,6 +254,7 @@ static int vdec_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, ...@@ -250,6 +254,7 @@ static int vdec_queue_setup(struct vb2_queue *q, unsigned int *num_buffers,
return -EINVAL; return -EINVAL;
} }
sess->changed_format = 1;
return 0; return 0;
} }
...@@ -261,10 +266,11 @@ static void vdec_vb2_buf_queue(struct vb2_buffer *vb) ...@@ -261,10 +266,11 @@ static void vdec_vb2_buf_queue(struct vb2_buffer *vb)
v4l2_m2m_buf_queue(m2m_ctx, vbuf); v4l2_m2m_buf_queue(m2m_ctx, vbuf);
if (!sess->streamon_out || !sess->streamon_cap) if (!sess->streamon_out)
return; return;
if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && if (sess->streamon_cap &&
vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
vdec_codec_needs_recycle(sess)) vdec_codec_needs_recycle(sess))
vdec_queue_recycle(sess, vb); vdec_queue_recycle(sess, vb);
...@@ -289,16 +295,22 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) ...@@ -289,16 +295,22 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
else else
sess->streamon_cap = 1; sess->streamon_cap = 1;
if (!sess->streamon_out || !sess->streamon_cap) if (!sess->streamon_out)
return 0; return 0;
if (sess->status == STATUS_NEEDS_RESUME && if (sess->status == STATUS_NEEDS_RESUME &&
q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
sess->changed_format) {
codec_ops->resume(sess); codec_ops->resume(sess);
sess->status = STATUS_RUNNING; sess->status = STATUS_RUNNING;
return 0; return 0;
} }
if (sess->status == STATUS_RUNNING ||
sess->status == STATUS_NEEDS_RESUME ||
sess->status == STATUS_INIT)
return 0;
sess->vififo_size = SIZE_VIFIFO; sess->vififo_size = SIZE_VIFIFO;
sess->vififo_vaddr = sess->vififo_vaddr =
dma_alloc_coherent(sess->core->dev, sess->vififo_size, dma_alloc_coherent(sess->core->dev, sess->vififo_size,
...@@ -323,13 +335,14 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) ...@@ -323,13 +335,14 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
goto vififo_free; goto vififo_free;
sess->sequence_cap = 0; sess->sequence_cap = 0;
sess->sequence_out = 0;
if (vdec_codec_needs_recycle(sess)) if (vdec_codec_needs_recycle(sess))
sess->recycle_thread = kthread_run(vdec_recycle_thread, sess, sess->recycle_thread = kthread_run(vdec_recycle_thread, sess,
"vdec_recycle"); "vdec_recycle");
sess->status = STATUS_RUNNING; sess->status = STATUS_INIT;
core->cur_sess = sess; core->cur_sess = sess;
schedule_work(&sess->esparser_queue_work);
return 0; return 0;
vififo_free: vififo_free:
...@@ -386,6 +399,7 @@ static void vdec_stop_streaming(struct vb2_queue *q) ...@@ -386,6 +399,7 @@ static void vdec_stop_streaming(struct vb2_queue *q)
struct vb2_v4l2_buffer *buf; struct vb2_v4l2_buffer *buf;
if (sess->status == STATUS_RUNNING || if (sess->status == STATUS_RUNNING ||
sess->status == STATUS_INIT ||
(sess->status == STATUS_NEEDS_RESUME && (sess->status == STATUS_NEEDS_RESUME &&
(!sess->streamon_out || !sess->streamon_cap))) { (!sess->streamon_out || !sess->streamon_cap))) {
if (vdec_codec_needs_recycle(sess)) if (vdec_codec_needs_recycle(sess))
...@@ -476,20 +490,34 @@ vdec_try_fmt_common(struct amvdec_session *sess, u32 size, ...@@ -476,20 +490,34 @@ vdec_try_fmt_common(struct amvdec_session *sess, u32 size,
struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp;
struct v4l2_plane_pix_format *pfmt = pixmp->plane_fmt; struct v4l2_plane_pix_format *pfmt = pixmp->plane_fmt;
const struct amvdec_format *fmts = sess->core->platform->formats; const struct amvdec_format *fmts = sess->core->platform->formats;
const struct amvdec_format *fmt_out; const struct amvdec_format *fmt_out = NULL;
u32 output_size = 0;
memset(pfmt[0].reserved, 0, sizeof(pfmt[0].reserved)); memset(pfmt[0].reserved, 0, sizeof(pfmt[0].reserved));
memset(pixmp->reserved, 0, sizeof(pixmp->reserved)); memset(pixmp->reserved, 0, sizeof(pixmp->reserved));
if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
fmt_out = find_format(fmts, size, pixmp->pixelformat); fmt_out = find_format(fmts, size, pixmp->pixelformat);
if (!fmt_out) { if (!fmt_out) {
pixmp->pixelformat = V4L2_PIX_FMT_MPEG2; pixmp->pixelformat = V4L2_PIX_FMT_MPEG2;
fmt_out = find_format(fmts, size, pixmp->pixelformat); fmt_out = find_format(fmts, size, pixmp->pixelformat);
} }
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
fmt_out = sess->fmt_out;
break;
default:
return NULL;
}
pfmt[0].sizeimage = pixmp->width = clamp(pixmp->width, (u32)256, fmt_out->max_width);
get_output_size(pixmp->width, pixmp->height); pixmp->height = clamp(pixmp->height, (u32)144, fmt_out->max_height);
output_size = get_output_size(pixmp->width, pixmp->height);
if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
if (!pfmt[0].sizeimage)
pfmt[0].sizeimage = sess->src_buffer_size;
pfmt[0].bytesperline = 0; pfmt[0].bytesperline = 0;
pixmp->num_planes = 1; pixmp->num_planes = 1;
} else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
...@@ -499,35 +527,25 @@ vdec_try_fmt_common(struct amvdec_session *sess, u32 size, ...@@ -499,35 +527,25 @@ vdec_try_fmt_common(struct amvdec_session *sess, u32 size,
memset(pfmt[1].reserved, 0, sizeof(pfmt[1].reserved)); memset(pfmt[1].reserved, 0, sizeof(pfmt[1].reserved));
if (pixmp->pixelformat == V4L2_PIX_FMT_NV12M) { if (pixmp->pixelformat == V4L2_PIX_FMT_NV12M) {
pfmt[0].sizeimage = pfmt[0].sizeimage = output_size;
get_output_size(pixmp->width, pixmp->height);
pfmt[0].bytesperline = ALIGN(pixmp->width, 64); pfmt[0].bytesperline = ALIGN(pixmp->width, 64);
pfmt[1].sizeimage = pfmt[1].sizeimage = output_size / 2;
get_output_size(pixmp->width, pixmp->height) / 2;
pfmt[1].bytesperline = ALIGN(pixmp->width, 64); pfmt[1].bytesperline = ALIGN(pixmp->width, 64);
pixmp->num_planes = 2; pixmp->num_planes = 2;
} else if (pixmp->pixelformat == V4L2_PIX_FMT_YUV420M) { } else if (pixmp->pixelformat == V4L2_PIX_FMT_YUV420M) {
pfmt[0].sizeimage = pfmt[0].sizeimage = output_size;
get_output_size(pixmp->width, pixmp->height);
pfmt[0].bytesperline = ALIGN(pixmp->width, 64); pfmt[0].bytesperline = ALIGN(pixmp->width, 64);
pfmt[1].sizeimage = pfmt[1].sizeimage = output_size / 4;
get_output_size(pixmp->width, pixmp->height) / 4;
pfmt[1].bytesperline = ALIGN(pixmp->width, 64) / 2; pfmt[1].bytesperline = ALIGN(pixmp->width, 64) / 2;
pfmt[2].sizeimage = pfmt[2].sizeimage = output_size / 2;
get_output_size(pixmp->width, pixmp->height) / 4;
pfmt[2].bytesperline = ALIGN(pixmp->width, 64) / 2; pfmt[2].bytesperline = ALIGN(pixmp->width, 64) / 2;
pixmp->num_planes = 3; pixmp->num_planes = 3;
} }
} else {
return NULL;
} }
pixmp->width = clamp(pixmp->width, (u32)256, fmt_out->max_width);
pixmp->height = clamp(pixmp->height, (u32)144, fmt_out->max_height);
if (pixmp->field == V4L2_FIELD_ANY) if (pixmp->field == V4L2_FIELD_ANY)
pixmp->field = V4L2_FIELD_NONE; pixmp->field = V4L2_FIELD_NONE;
...@@ -586,6 +604,8 @@ static int vdec_s_fmt(struct file *file, void *fh, struct v4l2_format *f) ...@@ -586,6 +604,8 @@ static int vdec_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
orig_pixmp = *pixmp; orig_pixmp = *pixmp;
fmt_out = vdec_try_fmt_common(sess, num_formats, f); fmt_out = vdec_try_fmt_common(sess, num_formats, f);
if (!fmt_out)
return -EINVAL;
if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
pixfmt_out = pixmp->pixelformat; pixfmt_out = pixmp->pixelformat;
...@@ -610,6 +630,7 @@ static int vdec_s_fmt(struct file *file, void *fh, struct v4l2_format *f) ...@@ -610,6 +630,7 @@ static int vdec_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
sess->ycbcr_enc = pixmp->ycbcr_enc; sess->ycbcr_enc = pixmp->ycbcr_enc;
sess->quantization = pixmp->quantization; sess->quantization = pixmp->quantization;
sess->xfer_func = pixmp->xfer_func; sess->xfer_func = pixmp->xfer_func;
sess->src_buffer_size = pixmp->plane_fmt[0].sizeimage;
} }
memset(&format, 0, sizeof(format)); memset(&format, 0, sizeof(format));
...@@ -701,29 +722,31 @@ vdec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd) ...@@ -701,29 +722,31 @@ vdec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd)
if (!(sess->streamon_out & sess->streamon_cap)) if (!(sess->streamon_out & sess->streamon_cap))
return 0; return 0;
/* Currently not handled since we do not support dynamic resolution if (cmd->cmd == V4L2_DEC_CMD_START) {
* for MPEG2. We consider both queues streaming to mean that the v4l2_m2m_clear_state(sess->m2m_ctx);
* decoding session is started sess->should_stop = 0;
*/
if (cmd->cmd == V4L2_DEC_CMD_START)
return 0; return 0;
}
/* Should not happen */ /* Should not happen */
if (cmd->cmd != V4L2_DEC_CMD_STOP) if (cmd->cmd != V4L2_DEC_CMD_STOP)
return -EINVAL; return -EINVAL;
dev_dbg(dev, "Received V4L2_DEC_CMD_STOP\n"); dev_dbg(dev, "Received V4L2_DEC_CMD_STOP\n");
sess->should_stop = 1; sess->should_stop = 1;
vdec_wait_inactive(sess); v4l2_m2m_mark_stopped(sess->m2m_ctx);
if (codec_ops->drain) { if (codec_ops->drain) {
vdec_wait_inactive(sess);
codec_ops->drain(sess); codec_ops->drain(sess);
} else if (codec_ops->eos_sequence) { } else if (codec_ops->eos_sequence) {
u32 len; u32 len;
const u8 *data = codec_ops->eos_sequence(&len); const u8 *data = codec_ops->eos_sequence(&len);
esparser_queue_eos(sess->core, data, len); esparser_queue_eos(sess->core, data, len);
vdec_wait_inactive(sess);
} }
return ret; return ret;
...@@ -883,6 +906,7 @@ static int vdec_open(struct file *file) ...@@ -883,6 +906,7 @@ static int vdec_open(struct file *file)
sess->height = 720; sess->height = 720;
sess->pixelaspect.numerator = 1; sess->pixelaspect.numerator = 1;
sess->pixelaspect.denominator = 1; sess->pixelaspect.denominator = 1;
sess->src_buffer_size = SZ_1M;
INIT_LIST_HEAD(&sess->timestamps); INIT_LIST_HEAD(&sess->timestamps);
INIT_LIST_HEAD(&sess->bufs_recycle); INIT_LIST_HEAD(&sess->bufs_recycle);
......
...@@ -29,13 +29,19 @@ struct amvdec_buffer { ...@@ -29,13 +29,19 @@ struct amvdec_buffer {
* struct amvdec_timestamp - stores a src timestamp along with a VIFIFO offset * struct amvdec_timestamp - stores a src timestamp along with a VIFIFO offset
* *
* @list: used to make lists out of this struct * @list: used to make lists out of this struct
* @ts: timestamp * @tc: timecode from the v4l2 buffer
* @ts: timestamp from the VB2 buffer
* @offset: offset in the VIFIFO where the associated packet was written * @offset: offset in the VIFIFO where the associated packet was written
* @flags: flags from the v4l2 buffer
* @used_count: times this timestamp was checked for a match with a dst buffer
*/ */
struct amvdec_timestamp { struct amvdec_timestamp {
struct list_head list; struct list_head list;
struct v4l2_timecode tc;
u64 ts; u64 ts;
u32 offset; u32 offset;
u32 flags;
u32 used_count;
}; };
struct amvdec_session; struct amvdec_session;
...@@ -165,6 +171,7 @@ struct amvdec_format { ...@@ -165,6 +171,7 @@ struct amvdec_format {
enum amvdec_status { enum amvdec_status {
STATUS_STOPPED, STATUS_STOPPED,
STATUS_INIT,
STATUS_RUNNING, STATUS_RUNNING,
STATUS_NEEDS_RESUME, STATUS_NEEDS_RESUME,
}; };
...@@ -180,6 +187,7 @@ enum amvdec_status { ...@@ -180,6 +187,7 @@ enum amvdec_status {
* @ctrl_min_buf_capture: V4L2 control V4L2_CID_MIN_BUFFERS_FOR_CAPTURE * @ctrl_min_buf_capture: V4L2 control V4L2_CID_MIN_BUFFERS_FOR_CAPTURE
* @fmt_out: vdec pixel format for the OUTPUT queue * @fmt_out: vdec pixel format for the OUTPUT queue
* @pixfmt_cap: V4L2 pixel format for the CAPTURE queue * @pixfmt_cap: V4L2 pixel format for the CAPTURE queue
* @src_buffer_size: size in bytes of the OUTPUT buffers' only plane
* @width: current picture width * @width: current picture width
* @height: current picture height * @height: current picture height
* @colorspace: current colorspace * @colorspace: current colorspace
...@@ -221,6 +229,7 @@ struct amvdec_session { ...@@ -221,6 +229,7 @@ struct amvdec_session {
const struct amvdec_format *fmt_out; const struct amvdec_format *fmt_out;
u32 pixfmt_cap; u32 pixfmt_cap;
u32 src_buffer_size;
u32 width; u32 width;
u32 height; u32 height;
...@@ -235,10 +244,11 @@ struct amvdec_session { ...@@ -235,10 +244,11 @@ struct amvdec_session {
struct work_struct esparser_queue_work; struct work_struct esparser_queue_work;
unsigned int streamon_cap, streamon_out; unsigned int streamon_cap, streamon_out;
unsigned int sequence_cap; unsigned int sequence_cap, sequence_out;
unsigned int should_stop; unsigned int should_stop;
unsigned int keyframe_found; unsigned int keyframe_found;
unsigned int num_dst_bufs; unsigned int num_dst_bufs;
unsigned int changed_format;
u8 canvas_alloc[MAX_CANVAS]; u8 canvas_alloc[MAX_CANVAS];
u32 canvas_num; u32 canvas_num;
......
...@@ -200,33 +200,23 @@ int amvdec_set_canvases(struct amvdec_session *sess, ...@@ -200,33 +200,23 @@ int amvdec_set_canvases(struct amvdec_session *sess,
} }
EXPORT_SYMBOL_GPL(amvdec_set_canvases); EXPORT_SYMBOL_GPL(amvdec_set_canvases);
void amvdec_add_ts_reorder(struct amvdec_session *sess, u64 ts, u32 offset) void amvdec_add_ts(struct amvdec_session *sess, u64 ts,
struct v4l2_timecode tc, u32 offset, u32 vbuf_flags)
{ {
struct amvdec_timestamp *new_ts, *tmp; struct amvdec_timestamp *new_ts;
unsigned long flags; unsigned long flags;
new_ts = kmalloc(sizeof(*new_ts), GFP_KERNEL); new_ts = kzalloc(sizeof(*new_ts), GFP_KERNEL);
new_ts->ts = ts; new_ts->ts = ts;
new_ts->tc = tc;
new_ts->offset = offset; new_ts->offset = offset;
new_ts->flags = vbuf_flags;
spin_lock_irqsave(&sess->ts_spinlock, flags); spin_lock_irqsave(&sess->ts_spinlock, flags);
if (list_empty(&sess->timestamps))
goto add_tail;
list_for_each_entry(tmp, &sess->timestamps, list) {
if (ts <= tmp->ts) {
list_add_tail(&new_ts->list, &tmp->list);
goto unlock;
}
}
add_tail:
list_add_tail(&new_ts->list, &sess->timestamps); list_add_tail(&new_ts->list, &sess->timestamps);
unlock:
spin_unlock_irqrestore(&sess->ts_spinlock, flags); spin_unlock_irqrestore(&sess->ts_spinlock, flags);
} }
EXPORT_SYMBOL_GPL(amvdec_add_ts_reorder); EXPORT_SYMBOL_GPL(amvdec_add_ts);
void amvdec_remove_ts(struct amvdec_session *sess, u64 ts) void amvdec_remove_ts(struct amvdec_session *sess, u64 ts)
{ {
...@@ -251,8 +241,8 @@ EXPORT_SYMBOL_GPL(amvdec_remove_ts); ...@@ -251,8 +241,8 @@ EXPORT_SYMBOL_GPL(amvdec_remove_ts);
static void dst_buf_done(struct amvdec_session *sess, static void dst_buf_done(struct amvdec_session *sess,
struct vb2_v4l2_buffer *vbuf, struct vb2_v4l2_buffer *vbuf,
u32 field, u32 field, u64 timestamp,
u64 timestamp) struct v4l2_timecode timecode, u32 flags)
{ {
struct device *dev = sess->core->dev_dec; struct device *dev = sess->core->dev_dec;
u32 output_size = amvdec_get_output_size(sess); u32 output_size = amvdec_get_output_size(sess);
...@@ -271,19 +261,23 @@ static void dst_buf_done(struct amvdec_session *sess, ...@@ -271,19 +261,23 @@ static void dst_buf_done(struct amvdec_session *sess,
vbuf->vb2_buf.timestamp = timestamp; vbuf->vb2_buf.timestamp = timestamp;
vbuf->sequence = sess->sequence_cap++; vbuf->sequence = sess->sequence_cap++;
vbuf->flags = flags;
vbuf->timecode = timecode;
if (sess->should_stop && if (sess->should_stop &&
atomic_read(&sess->esparser_queued_bufs) <= 2) { atomic_read(&sess->esparser_queued_bufs) <= 1) {
const struct v4l2_event ev = { .type = V4L2_EVENT_EOS }; const struct v4l2_event ev = { .type = V4L2_EVENT_EOS };
dev_dbg(dev, "Signaling EOS\n"); dev_dbg(dev, "Signaling EOS, sequence_cap = %u\n",
sess->sequence_cap - 1);
v4l2_event_queue_fh(&sess->fh, &ev); v4l2_event_queue_fh(&sess->fh, &ev);
vbuf->flags |= V4L2_BUF_FLAG_LAST; vbuf->flags |= V4L2_BUF_FLAG_LAST;
} else if (sess->should_stop) } else if (sess->should_stop)
dev_dbg(dev, "should_stop, %u bufs remain\n", dev_dbg(dev, "should_stop, %u bufs remain\n",
atomic_read(&sess->esparser_queued_bufs)); atomic_read(&sess->esparser_queued_bufs));
dev_dbg(dev, "Buffer %u done\n", vbuf->vb2_buf.index); dev_dbg(dev, "Buffer %u done, ts = %llu, flags = %08X\n",
vbuf->vb2_buf.index, timestamp, flags);
vbuf->field = field; vbuf->field = field;
v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE); v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
...@@ -297,7 +291,9 @@ void amvdec_dst_buf_done(struct amvdec_session *sess, ...@@ -297,7 +291,9 @@ void amvdec_dst_buf_done(struct amvdec_session *sess,
struct device *dev = sess->core->dev_dec; struct device *dev = sess->core->dev_dec;
struct amvdec_timestamp *tmp; struct amvdec_timestamp *tmp;
struct list_head *timestamps = &sess->timestamps; struct list_head *timestamps = &sess->timestamps;
struct v4l2_timecode timecode;
u64 timestamp; u64 timestamp;
u32 vbuf_flags;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&sess->ts_spinlock, flags); spin_lock_irqsave(&sess->ts_spinlock, flags);
...@@ -312,11 +308,13 @@ void amvdec_dst_buf_done(struct amvdec_session *sess, ...@@ -312,11 +308,13 @@ void amvdec_dst_buf_done(struct amvdec_session *sess,
tmp = list_first_entry(timestamps, struct amvdec_timestamp, list); tmp = list_first_entry(timestamps, struct amvdec_timestamp, list);
timestamp = tmp->ts; timestamp = tmp->ts;
timecode = tmp->tc;
vbuf_flags = tmp->flags;
list_del(&tmp->list); list_del(&tmp->list);
kfree(tmp); kfree(tmp);
spin_unlock_irqrestore(&sess->ts_spinlock, flags); spin_unlock_irqrestore(&sess->ts_spinlock, flags);
dst_buf_done(sess, vbuf, field, timestamp); dst_buf_done(sess, vbuf, field, timestamp, timecode, vbuf_flags);
atomic_dec(&sess->esparser_queued_bufs); atomic_dec(&sess->esparser_queued_bufs);
} }
EXPORT_SYMBOL_GPL(amvdec_dst_buf_done); EXPORT_SYMBOL_GPL(amvdec_dst_buf_done);
...@@ -328,48 +326,43 @@ void amvdec_dst_buf_done_offset(struct amvdec_session *sess, ...@@ -328,48 +326,43 @@ void amvdec_dst_buf_done_offset(struct amvdec_session *sess,
struct device *dev = sess->core->dev_dec; struct device *dev = sess->core->dev_dec;
struct amvdec_timestamp *match = NULL; struct amvdec_timestamp *match = NULL;
struct amvdec_timestamp *tmp, *n; struct amvdec_timestamp *tmp, *n;
struct v4l2_timecode timecode = { 0 };
u64 timestamp = 0; u64 timestamp = 0;
u32 vbuf_flags = 0;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&sess->ts_spinlock, flags); spin_lock_irqsave(&sess->ts_spinlock, flags);
/* Look for our vififo offset to get the corresponding timestamp. */ /* Look for our vififo offset to get the corresponding timestamp. */
list_for_each_entry_safe(tmp, n, &sess->timestamps, list) { list_for_each_entry_safe(tmp, n, &sess->timestamps, list) {
s64 delta = (s64)offset - tmp->offset; if (tmp->offset > offset) {
/*
/* Offsets reported by codecs usually differ slightly, * Delete any record that remained unused for 32 match
* so we need some wiggle room. * checks
* 4KiB being the minimum packet size, there is no risk here.
*/
if (delta > (-1 * (s32)SZ_4K) && delta < SZ_4K) {
match = tmp;
break;
}
if (!allow_drop)
continue;
/* Delete any timestamp entry that appears before our target
* (not all src packets/timestamps lead to a frame)
*/ */
if (delta > 0 || delta < -1 * (s32)sess->vififo_size) { if (tmp->used_count++ >= 32) {
atomic_dec(&sess->esparser_queued_bufs);
list_del(&tmp->list); list_del(&tmp->list);
kfree(tmp); kfree(tmp);
} }
break;
}
match = tmp;
} }
if (!match) { if (!match) {
dev_dbg(dev, "Buffer %u done but can't match offset (%08X)\n", dev_err(dev, "Buffer %u done but can't match offset (%08X)\n",
vbuf->vb2_buf.index, offset); vbuf->vb2_buf.index, offset);
} else { } else {
timestamp = match->ts; timestamp = match->ts;
timecode = match->tc;
vbuf_flags = match->flags;
list_del(&match->list); list_del(&match->list);
kfree(match); kfree(match);
} }
spin_unlock_irqrestore(&sess->ts_spinlock, flags); spin_unlock_irqrestore(&sess->ts_spinlock, flags);
dst_buf_done(sess, vbuf, field, timestamp); dst_buf_done(sess, vbuf, field, timestamp, timecode, vbuf_flags);
if (match) if (match)
atomic_dec(&sess->esparser_queued_bufs); atomic_dec(&sess->esparser_queued_bufs);
} }
...@@ -420,16 +413,19 @@ void amvdec_src_change(struct amvdec_session *sess, u32 width, ...@@ -420,16 +413,19 @@ void amvdec_src_change(struct amvdec_session *sess, u32 width,
v4l2_ctrl_s_ctrl(sess->ctrl_min_buf_capture, dpb_size); v4l2_ctrl_s_ctrl(sess->ctrl_min_buf_capture, dpb_size);
/* Check if the capture queue is already configured well for our /*
* Check if the capture queue is already configured well for our
* usecase. If so, keep decoding with it and do not send the event * usecase. If so, keep decoding with it and do not send the event
*/ */
if (sess->width == width && if (sess->streamon_cap &&
sess->width == width &&
sess->height == height && sess->height == height &&
dpb_size <= sess->num_dst_bufs) { dpb_size <= sess->num_dst_bufs) {
sess->fmt_out->codec_ops->resume(sess); sess->fmt_out->codec_ops->resume(sess);
return; return;
} }
sess->changed_format = 0;
sess->width = width; sess->width = width;
sess->height = height; sess->height = height;
sess->status = STATUS_NEEDS_RESUME; sess->status = STATUS_NEEDS_RESUME;
......
...@@ -44,13 +44,15 @@ void amvdec_dst_buf_done_offset(struct amvdec_session *sess, ...@@ -44,13 +44,15 @@ void amvdec_dst_buf_done_offset(struct amvdec_session *sess,
u32 offset, u32 field, bool allow_drop); u32 offset, u32 field, bool allow_drop);
/** /**
* amvdec_add_ts_reorder() - Add a timestamp to the list in chronological order * amvdec_add_ts() - Add a timestamp to the list
* *
* @sess: current session * @sess: current session
* @ts: timestamp to add * @ts: timestamp to add
* @offset: offset in the VIFIFO where the associated packet was written * @offset: offset in the VIFIFO where the associated packet was written
* @flags the vb2_v4l2_buffer flags
*/ */
void amvdec_add_ts_reorder(struct amvdec_session *sess, u64 ts, u32 offset); void amvdec_add_ts(struct amvdec_session *sess, u64 ts,
struct v4l2_timecode tc, u32 offset, u32 flags);
void amvdec_remove_ts(struct amvdec_session *sess, u64 ts); void amvdec_remove_ts(struct amvdec_session *sess, u64 ts);
/** /**
......
...@@ -21,6 +21,7 @@ static const struct amvdec_format vdec_formats_gxbb[] = { ...@@ -21,6 +21,7 @@ static const struct amvdec_format vdec_formats_gxbb[] = {
.codec_ops = &codec_mpeg12_ops, .codec_ops = &codec_mpeg12_ops,
.firmware_path = "meson/vdec/gxl_mpeg12.bin", .firmware_path = "meson/vdec/gxl_mpeg12.bin",
.pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 },
.flags = V4L2_FMT_FLAG_COMPRESSED,
}, { }, {
.pixfmt = V4L2_PIX_FMT_MPEG2, .pixfmt = V4L2_PIX_FMT_MPEG2,
.min_buffers = 8, .min_buffers = 8,
...@@ -31,6 +32,7 @@ static const struct amvdec_format vdec_formats_gxbb[] = { ...@@ -31,6 +32,7 @@ static const struct amvdec_format vdec_formats_gxbb[] = {
.codec_ops = &codec_mpeg12_ops, .codec_ops = &codec_mpeg12_ops,
.firmware_path = "meson/vdec/gxl_mpeg12.bin", .firmware_path = "meson/vdec/gxl_mpeg12.bin",
.pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 },
.flags = V4L2_FMT_FLAG_COMPRESSED,
}, },
}; };
...@@ -45,6 +47,7 @@ static const struct amvdec_format vdec_formats_gxl[] = { ...@@ -45,6 +47,7 @@ static const struct amvdec_format vdec_formats_gxl[] = {
.codec_ops = &codec_mpeg12_ops, .codec_ops = &codec_mpeg12_ops,
.firmware_path = "meson/vdec/gxl_mpeg12.bin", .firmware_path = "meson/vdec/gxl_mpeg12.bin",
.pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 },
.flags = V4L2_FMT_FLAG_COMPRESSED,
}, { }, {
.pixfmt = V4L2_PIX_FMT_MPEG2, .pixfmt = V4L2_PIX_FMT_MPEG2,
.min_buffers = 8, .min_buffers = 8,
...@@ -55,6 +58,7 @@ static const struct amvdec_format vdec_formats_gxl[] = { ...@@ -55,6 +58,7 @@ static const struct amvdec_format vdec_formats_gxl[] = {
.codec_ops = &codec_mpeg12_ops, .codec_ops = &codec_mpeg12_ops,
.firmware_path = "meson/vdec/gxl_mpeg12.bin", .firmware_path = "meson/vdec/gxl_mpeg12.bin",
.pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 },
.flags = V4L2_FMT_FLAG_COMPRESSED,
}, },
}; };
...@@ -69,6 +73,7 @@ static const struct amvdec_format vdec_formats_gxm[] = { ...@@ -69,6 +73,7 @@ static const struct amvdec_format vdec_formats_gxm[] = {
.codec_ops = &codec_mpeg12_ops, .codec_ops = &codec_mpeg12_ops,
.firmware_path = "meson/vdec/gxl_mpeg12.bin", .firmware_path = "meson/vdec/gxl_mpeg12.bin",
.pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 },
.flags = V4L2_FMT_FLAG_COMPRESSED,
}, { }, {
.pixfmt = V4L2_PIX_FMT_MPEG2, .pixfmt = V4L2_PIX_FMT_MPEG2,
.min_buffers = 8, .min_buffers = 8,
...@@ -79,6 +84,7 @@ static const struct amvdec_format vdec_formats_gxm[] = { ...@@ -79,6 +84,7 @@ static const struct amvdec_format vdec_formats_gxm[] = {
.codec_ops = &codec_mpeg12_ops, .codec_ops = &codec_mpeg12_ops,
.firmware_path = "meson/vdec/gxl_mpeg12.bin", .firmware_path = "meson/vdec/gxl_mpeg12.bin",
.pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 },
.flags = V4L2_FMT_FLAG_COMPRESSED,
}, },
}; };
...@@ -93,6 +99,7 @@ static const struct amvdec_format vdec_formats_g12a[] = { ...@@ -93,6 +99,7 @@ static const struct amvdec_format vdec_formats_g12a[] = {
.codec_ops = &codec_mpeg12_ops, .codec_ops = &codec_mpeg12_ops,
.firmware_path = "meson/vdec/gxl_mpeg12.bin", .firmware_path = "meson/vdec/gxl_mpeg12.bin",
.pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 },
.flags = V4L2_FMT_FLAG_COMPRESSED,
}, { }, {
.pixfmt = V4L2_PIX_FMT_MPEG2, .pixfmt = V4L2_PIX_FMT_MPEG2,
.min_buffers = 8, .min_buffers = 8,
...@@ -103,6 +110,7 @@ static const struct amvdec_format vdec_formats_g12a[] = { ...@@ -103,6 +110,7 @@ static const struct amvdec_format vdec_formats_g12a[] = {
.codec_ops = &codec_mpeg12_ops, .codec_ops = &codec_mpeg12_ops,
.firmware_path = "meson/vdec/gxl_mpeg12.bin", .firmware_path = "meson/vdec/gxl_mpeg12.bin",
.pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 },
.flags = V4L2_FMT_FLAG_COMPRESSED,
}, },
}; };
...@@ -117,6 +125,7 @@ static const struct amvdec_format vdec_formats_sm1[] = { ...@@ -117,6 +125,7 @@ static const struct amvdec_format vdec_formats_sm1[] = {
.codec_ops = &codec_mpeg12_ops, .codec_ops = &codec_mpeg12_ops,
.firmware_path = "meson/vdec/gxl_mpeg12.bin", .firmware_path = "meson/vdec/gxl_mpeg12.bin",
.pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 },
.flags = V4L2_FMT_FLAG_COMPRESSED,
}, { }, {
.pixfmt = V4L2_PIX_FMT_MPEG2, .pixfmt = V4L2_PIX_FMT_MPEG2,
.min_buffers = 8, .min_buffers = 8,
...@@ -127,6 +136,7 @@ static const struct amvdec_format vdec_formats_sm1[] = { ...@@ -127,6 +136,7 @@ static const struct amvdec_format vdec_formats_sm1[] = {
.codec_ops = &codec_mpeg12_ops, .codec_ops = &codec_mpeg12_ops,
.firmware_path = "meson/vdec/gxl_mpeg12.bin", .firmware_path = "meson/vdec/gxl_mpeg12.bin",
.pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 },
.flags = V4L2_FMT_FLAG_COMPRESSED,
}, },
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment