aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/media/sunxi/cedrus/cedrus_h264.c
diff options
context:
space:
mode:
authorJernej Skrabec <jernej.skrabec@siol.net>2019-10-26 09:27:51 +0200
committerMauro Carvalho Chehab <mchehab@kernel.org>2019-11-09 08:44:09 +0100
commit61ad123318c28679b8846baab0df9240c29c21c9 (patch)
treef063dda1dad5013ed8f49eef1e556c37a7b9ebdd /drivers/staging/media/sunxi/cedrus/cedrus_h264.c
parentb95182a6e82209fa3c98bde8ec79c51b1870aca5 (diff)
media: cedrus: Fix decoding for some H264 videos
It seems that for some H264 videos at least one bitstream parsing trigger must be called in order to be decoded correctly. There is no explanation why this helps, but it was observed that two sample videos with this fix are now decoded correctly and there is no regression with others. Acked-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com> Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net> Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
Diffstat (limited to 'drivers/staging/media/sunxi/cedrus/cedrus_h264.c')
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus_h264.c30
1 files changed, 27 insertions, 3 deletions
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c
index cd85668f9c80..db336449c4f2 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c
@@ -6,6 +6,7 @@
* Copyright (c) 2018 Bootlin
*/
+#include <linux/delay.h>
#include <linux/types.h>
#include <media/videobuf2-dma-contig.h>
@@ -289,6 +290,28 @@ static void cedrus_write_pred_weight_table(struct cedrus_ctx *ctx,
}
}
+/*
+ * It turns out that using VE_H264_VLD_OFFSET to skip bits is not reliable. In
+ * rare cases frame is not decoded correctly. However, setting offset to 0 and
+ * skipping appropriate amount of bits with flush bits trigger always works.
+ */
+static void cedrus_skip_bits(struct cedrus_dev *dev, int num)
+{
+ int count = 0;
+
+ while (count < num) {
+ int tmp = min(num - count, 32);
+
+ cedrus_write(dev, VE_H264_TRIGGER_TYPE,
+ VE_H264_TRIGGER_TYPE_FLUSH_BITS |
+ VE_H264_TRIGGER_TYPE_N_BITS(tmp));
+ while (cedrus_read(dev, VE_H264_STATUS) & VE_H264_STATUS_VLD_BUSY)
+ udelay(1);
+
+ count += tmp;
+ }
+}
+
static void cedrus_set_params(struct cedrus_ctx *ctx,
struct cedrus_run *run)
{
@@ -299,14 +322,13 @@ static void cedrus_set_params(struct cedrus_ctx *ctx,
struct vb2_buffer *src_buf = &run->src->vb2_buf;
struct cedrus_dev *dev = ctx->dev;
dma_addr_t src_buf_addr;
- u32 offset = slice->header_bit_size;
- u32 len = (slice->size * 8) - offset;
+ u32 len = slice->size * 8;
unsigned int pic_width_in_mbs;
bool mbaff_pic;
u32 reg;
cedrus_write(dev, VE_H264_VLD_LEN, len);
- cedrus_write(dev, VE_H264_VLD_OFFSET, offset);
+ cedrus_write(dev, VE_H264_VLD_OFFSET, 0);
src_buf_addr = vb2_dma_contig_plane_dma_addr(src_buf, 0);
cedrus_write(dev, VE_H264_VLD_END,
@@ -325,6 +347,8 @@ static void cedrus_set_params(struct cedrus_ctx *ctx,
cedrus_write(dev, VE_H264_TRIGGER_TYPE,
VE_H264_TRIGGER_TYPE_INIT_SWDEC);
+ cedrus_skip_bits(dev, slice->header_bit_size);
+
if (((pps->flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED) &&
(slice->slice_type == V4L2_H264_SLICE_TYPE_P ||
slice->slice_type == V4L2_H264_SLICE_TYPE_SP)) ||

Privacy Policy