From d117460aed81ee5cd384045a1189c9de758d17c6 Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@siol.net>
Date: Tue, 28 May 2019 21:05:34 +0200
Subject: [PATCH] 10-bit HEVC hack

Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
---
 drivers/staging/media/sunxi/cedrus/cedrus_h265.c  | 12 ++++++++++++
 drivers/staging/media/sunxi/cedrus/cedrus_regs.h  |  4 ++++
 drivers/staging/media/sunxi/cedrus/cedrus_video.c | 13 +++++++++++--
 3 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c
index 8bbbe69ae51f..04ba7d60ebcd 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c
@@ -453,6 +453,18 @@ static void cedrus_h265_setup(struct cedrus_ctx *ctx,
 
 	cedrus_write(dev, VE_DEC_H265_DEC_PCM_CTRL, reg);
 
+	if (sps->bit_depth_luma_minus8) {
+		unsigned int size;
+
+		size = ALIGN(ctx->src_fmt.width, 16) * ALIGN(ctx->src_fmt.height, 16);
+
+		reg = (size * 3) / 2;
+		cedrus_write(dev, VE_DEC_H265_OFFSET_ADDR_FIRST_OUT, reg);
+
+		reg = DIV_ROUND_UP(ctx->src_fmt.width, 4);
+		cedrus_write(dev, VE_DEC_H265_10BIT_CONFIGURE, ALIGN(reg, 32));
+	}
+
 	reg = VE_DEC_H265_DEC_PPS_CTRL0_PPS_CR_QP_OFFSET(pps->pps_cr_qp_offset) |
 	      VE_DEC_H265_DEC_PPS_CTRL0_PPS_CB_QP_OFFSET(pps->pps_cb_qp_offset) |
 	      VE_DEC_H265_DEC_PPS_CTRL0_INIT_QP_MINUS26(pps->init_qp_minus26) |
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h
index d1f010ae49ef..dd69031a2779 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h
@@ -505,6 +505,10 @@
 
 #define VE_DEC_H265_LOW_ADDR			(VE_ENGINE_DEC_H265 + 0x80)
 
+#define VE_DEC_H265_OFFSET_ADDR_FIRST_OUT	(VE_ENGINE_DEC_H265 + 0x84)
+#define VE_DEC_H265_OFFSET_ADDR_SECOND_OUT	(VE_ENGINE_DEC_H265 + 0x88)
+#define VE_DEC_H265_10BIT_CONFIGURE		(VE_ENGINE_DEC_H265 + 0x8c)
+
 #define VE_DEC_H265_LOW_ADDR_PRIMARY_CHROMA(a) \
 	(((a) << 24) & GENMASK(31, 24))
 #define VE_DEC_H265_LOW_ADDR_SECONDARY_CHROMA(a) \
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.c b/drivers/staging/media/sunxi/cedrus/cedrus_video.c
index d27a9e82ff91..2c2288319c9d 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_video.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.c
@@ -372,6 +372,7 @@ static int cedrus_queue_setup(struct vb2_queue *vq, unsigned int *nbufs,
 	struct cedrus_ctx *ctx = vb2_get_drv_priv(vq);
 	struct cedrus_dev *dev = ctx->dev;
 	struct v4l2_pix_format *pix_fmt;
+	unsigned int extra_size = 0;
 	u32 directions;
 
 	if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
@@ -380,6 +381,14 @@ static int cedrus_queue_setup(struct vb2_queue *vq, unsigned int *nbufs,
 	} else {
 		directions = CEDRUS_DECODE_DST;
 		pix_fmt = &ctx->dst_fmt;
+
+		/* The HEVC decoder needs extra size on the output buffer. */
+		if (ctx->src_fmt.pixelformat == V4L2_PIX_FMT_HEVC_SLICE) {
+			extra_size = DIV_ROUND_UP(pix_fmt->width, 4);
+			extra_size = ALIGN(extra_size, 32);
+			extra_size *= ALIGN(pix_fmt->height, 16) * 3;
+			extra_size /= 2;
+		}
 	}
 
 	if (!cedrus_check_format(pix_fmt->pixelformat, directions,
@@ -387,8 +396,8 @@ static int cedrus_queue_setup(struct vb2_queue *vq, unsigned int *nbufs,
 		return -EINVAL;
 
 	if (*nplanes) {
-		if (sizes[0] < pix_fmt->sizeimage)
-			return -EINVAL;
+		if (sizes[0] < (pix_fmt->sizeimage + extra_size))
+			sizes[0] = pix_fmt->sizeimage + extra_size;
 	} else {
 		sizes[0] = pix_fmt->sizeimage;
 		*nplanes = 1;
-- 
2.21.0

