From c6582c38df2f78dc9d4f8fd920780a82a01e4d8e Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@siol.net>
Date: Sat, 25 May 2019 13:58:17 +0200
Subject: [PATCH 2/3] WIP: HEVC improvements

Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
---
 drivers/media/v4l2-core/v4l2-ctrls.c          |   8 +
 drivers/staging/media/sunxi/cedrus/cedrus.c   |   6 +
 drivers/staging/media/sunxi/cedrus/cedrus.h   |  11 +-
 .../staging/media/sunxi/cedrus/cedrus_dec.c   |   2 +
 .../staging/media/sunxi/cedrus/cedrus_h265.c  | 348 +++++++++++++-----
 .../staging/media/sunxi/cedrus/cedrus_regs.h  |   3 +
 .../staging/media/sunxi/cedrus/cedrus_video.c |  12 +-
 include/media/hevc-ctrls.h                    |  20 +-
 8 files changed, 301 insertions(+), 109 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
index 1107698b9d06..aed7a4526193 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls.c
@@ -947,6 +947,7 @@ const char *v4l2_ctrl_get_name(u32 id)
 	case V4L2_CID_MPEG_VIDEO_HEVC_SPS:			return "HEVC Sequence Parameter Set";
 	case V4L2_CID_MPEG_VIDEO_HEVC_PPS:			return "HEVC Picture Parameter Set";
 	case V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS:		return "HEVC Slice Parameters";
+	case V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX:		return "HEVC Scaling Matrix";
 
 	/* CAMERA controls */
 	/* Keep the order of the 'case's the same as in v4l2-controls.h! */
@@ -1368,6 +1369,9 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
 	case V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS:
 		*type = V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS;
 		break;
+	case V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX:
+		*type = V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX;
+		break;
 	default:
 		*type = V4L2_CTRL_TYPE_INTEGER;
 		break;
@@ -1747,6 +1751,7 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx,
 	case V4L2_CTRL_TYPE_HEVC_SPS:
 	case V4L2_CTRL_TYPE_HEVC_PPS:
 	case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS:
+	case V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX:
 		return 0;
 
 	default:
@@ -2356,6 +2361,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
 	case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS:
 		elem_size = sizeof(struct v4l2_ctrl_hevc_slice_params);
 		break;
+	case V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX:
+		elem_size = sizeof(struct v4l2_ctrl_hevc_scaling_matrix);
+		break;
 	default:
 		if (type < V4L2_CTRL_COMPOUND_TYPES)
 			elem_size = sizeof(s32);
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c
index 70642834f351..01860f247aa6 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus.c
@@ -88,6 +88,12 @@ static const struct cedrus_control cedrus_controls[] = {
 		.codec		= CEDRUS_CODEC_H265,
 		.required	= true,
 	},
+	{
+		.id		= V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX,
+		.elem_size	= sizeof(struct v4l2_ctrl_hevc_scaling_matrix),
+		.codec		= CEDRUS_CODEC_H265,
+		.required	= true,
+	},
 };
 
 #define CEDRUS_CONTROLS_COUNT	ARRAY_SIZE(cedrus_controls)
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.h b/drivers/staging/media/sunxi/cedrus/cedrus.h
index f19be772d78b..b518c5613fdf 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus.h
+++ b/drivers/staging/media/sunxi/cedrus/cedrus.h
@@ -74,6 +74,7 @@ struct cedrus_h265_run {
 	const struct v4l2_ctrl_hevc_sps			*sps;
 	const struct v4l2_ctrl_hevc_pps			*pps;
 	const struct v4l2_ctrl_hevc_slice_params	*slice_params;
+	const struct v4l2_ctrl_hevc_scaling_matrix	*scaling_matrix;
 };
 
 struct cedrus_run {
@@ -90,6 +91,10 @@ struct cedrus_run {
 struct cedrus_buffer {
 	struct v4l2_m2m_buffer          m2m_buf;
 
+	void		*mv_col_buf;
+	dma_addr_t	mv_col_buf_dma;
+	ssize_t		mv_col_buf_size;
+
 	union {
 		struct {
 			unsigned int			position;
@@ -123,12 +128,10 @@ struct cedrus_ctx {
 			dma_addr_t	neighbor_info_buf_dma;
 		} h264;
 		struct {
-			void		*mv_col_buf;
-			dma_addr_t	mv_col_buf_addr;
-			ssize_t		mv_col_buf_size;
-			ssize_t		mv_col_buf_unit_size;
 			void		*neighbor_info_buf;
 			dma_addr_t	neighbor_info_buf_addr;
+			void		*entry_points_buf;
+			dma_addr_t	entry_points_buf_addr;
 		} h265;
 	} codec;
 };
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c
index c6d0ef66cdd0..104adb08492c 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c
@@ -66,6 +66,8 @@ void cedrus_device_run(void *priv)
 			V4L2_CID_MPEG_VIDEO_HEVC_PPS);
 		run.h265.slice_params = cedrus_find_control_data(ctx,
 			V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS);
+		run.h265.scaling_matrix = cedrus_find_control_data(ctx,
+			V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX);
 		break;
 
 	default:
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c
index fd4d86b02156..82d29c59b787 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c
@@ -77,24 +77,25 @@ static void cedrus_h265_sram_write_offset(struct cedrus_dev *dev, u32 offset)
 	cedrus_write(dev, VE_DEC_H265_SRAM_OFFSET, offset);
 }
 
-static void cedrus_h265_sram_write_data(struct cedrus_dev *dev, void *data,
+static void cedrus_h265_sram_write_data(struct cedrus_dev *dev, const void *data,
 					unsigned int size)
 {
-	u32 *word = data;
+	size_t count = DIV_ROUND_UP(size, 4);
+	const u32 *word = data;
 
-	while (size >= sizeof(u32)) {
+	while (count--)
 		cedrus_write(dev, VE_DEC_H265_SRAM_DATA, *word++);
-		size -= sizeof(u32);
-	}
 }
 
 static inline dma_addr_t
 cedrus_h265_frame_info_mv_col_buf_addr(struct cedrus_ctx *ctx,
-				       unsigned int index, unsigned int field)
+				       unsigned int index)
 {
-	return ctx->codec.h265.mv_col_buf_addr + index *
-	       ctx->codec.h265.mv_col_buf_unit_size +
-	       field * ctx->codec.h265.mv_col_buf_unit_size / 2;
+	struct cedrus_buffer *cedrus_buf;
+
+	cedrus_buf = vb2_to_cedrus_buffer(ctx->dst_bufs[index]);
+
+	return cedrus_buf->mv_col_buf_dma;
 }
 
 static void cedrus_h265_frame_info_write_single(struct cedrus_ctx *ctx,
@@ -107,9 +108,8 @@ static void cedrus_h265_frame_info_write_single(struct cedrus_ctx *ctx,
 	dma_addr_t dst_luma_addr = cedrus_dst_buf_addr(ctx, buffer_index, 0);
 	dma_addr_t dst_chroma_addr = cedrus_dst_buf_addr(ctx, buffer_index, 1);
 	dma_addr_t mv_col_buf_addr[2] = {
-		cedrus_h265_frame_info_mv_col_buf_addr(ctx, buffer_index, 0),
-		cedrus_h265_frame_info_mv_col_buf_addr(ctx, buffer_index,
-						       field_pic ? 1 : 0)
+		cedrus_h265_frame_info_mv_col_buf_addr(ctx, buffer_index),
+		cedrus_h265_frame_info_mv_col_buf_addr(ctx, buffer_index)
 	};
 	u32 offset = VE_DEC_H265_SRAM_OFFSET_FRAME_INFO +
 		     VE_DEC_H265_SRAM_OFFSET_FRAME_INFO_UNIT * index;
@@ -158,28 +158,24 @@ static void cedrus_h265_ref_pic_list_write(struct cedrus_dev *dev,
 					   u8 num_ref_idx_active,
 					   u32 sram_offset)
 {
+	u8 sram_array[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
 	unsigned int i;
-	u32 word = 0;
+
+	memset(sram_array, 0, sizeof(sram_array));
+	num_ref_idx_active = min(num_ref_idx_active,
+				 (u8)V4L2_HEVC_DPB_ENTRIES_NUM_MAX);
 
 	cedrus_h265_sram_write_offset(dev, sram_offset);
 
 	for (i = 0; i < num_ref_idx_active; i++) {
-		unsigned int shift = (i % 4) * 8;
 		unsigned int index = list[i];
-		u8 value = list[i];
 
+		sram_array[i] = index;
 		if (dpb[index].rps == V4L2_HEVC_DPB_ENTRY_RPS_LT_CURR)
-			value |= VE_DEC_H265_SRAM_REF_PIC_LIST_LT_REF;
-
-		/* Each SRAM word gathers up to 4 references. */
-		word |= value << shift;
-
-		/* Write the word to SRAM and clear it for the next batch. */
-		if ((i % 4) == 3 || i == (num_ref_idx_active - 1)) {
-			cedrus_h265_sram_write_data(dev, &word, sizeof(word));
-			word = 0;
-		}
+			sram_array[i] |= VE_DEC_H265_SRAM_REF_PIC_LIST_LT_REF;
 	}
+
+	cedrus_h265_sram_write_data(dev, &sram_array, num_ref_idx_active);
 }
 
 static void cedrus_h265_pred_weight_write(struct cedrus_dev *dev,
@@ -220,6 +216,131 @@ static void cedrus_h265_pred_weight_write(struct cedrus_dev *dev,
 	}
 }
 
+static void cedrus_h265_write_scaling_list(struct cedrus_ctx *ctx,
+					   struct cedrus_run *run)
+{
+	const struct v4l2_ctrl_hevc_scaling_matrix *scaling;
+	struct cedrus_dev *dev = ctx->dev;
+	u32 i, j, k, val;
+
+	scaling = run->h265.scaling_matrix;
+
+	cedrus_write(dev, VE_DEC_H265_SCALING_LIST_DC_COEF0,
+		     (scaling->scaling_list_dc_coef_32x32[1] << 24) |
+		     (scaling->scaling_list_dc_coef_32x32[0] << 16) |
+		     (scaling->scaling_list_dc_coef_16x16[1] << 8) |
+		     (scaling->scaling_list_dc_coef_16x16[0] << 0));
+
+	cedrus_write(dev, VE_DEC_H265_SCALING_LIST_DC_COEF1,
+		     (scaling->scaling_list_dc_coef_16x16[5] << 24) |
+		     (scaling->scaling_list_dc_coef_16x16[4] << 16) |
+		     (scaling->scaling_list_dc_coef_16x16[3] << 8) |
+		     (scaling->scaling_list_dc_coef_16x16[2] << 0));
+
+	cedrus_h265_sram_write_offset(dev, VE_DEC_H265_SRAM_OFFSET_SCALING_LISTS);
+
+	for (i = 0; i < 6; i++)
+		for (j = 0; j < 8; j++)
+			for (k = 0; k < 8; k += 4) {
+				val = ((u32)scaling->scaling_list_8x8[i][j + (k + 3) * 8] << 24) |
+				      ((u32)scaling->scaling_list_8x8[i][j + (k + 2) * 8] << 16) |
+				      ((u32)scaling->scaling_list_8x8[i][j + (k + 1) * 8] << 8) |
+				      scaling->scaling_list_8x8[i][j + k * 8];
+				cedrus_write(dev, VE_DEC_H265_SRAM_DATA, val);
+			}
+
+	for (i = 0; i < 2; i++)
+		for (j = 0; j < 8; j++)
+			for (k = 0; k < 8; k += 4) {
+				val = ((u32)scaling->scaling_list_32x32[i][j + (k + 3) * 8] << 24) |
+				      ((u32)scaling->scaling_list_32x32[i][j + (k + 2) * 8] << 16) |
+				      ((u32)scaling->scaling_list_32x32[i][j + (k + 1) * 8] << 8) |
+				      scaling->scaling_list_32x32[i][j + k * 8];
+				cedrus_write(dev, VE_DEC_H265_SRAM_DATA, val);
+			}
+
+	for (i = 0; i < 6; i++)
+		for (j = 0; j < 8; j++)
+			for (k = 0; k < 8; k += 4) {
+				val = ((u32)scaling->scaling_list_16x16[i][j + (k + 3) * 8] << 24) |
+				      ((u32)scaling->scaling_list_16x16[i][j + (k + 2) * 8] << 16) |
+				      ((u32)scaling->scaling_list_16x16[i][j + (k + 1) * 8] << 8) |
+				      scaling->scaling_list_16x16[i][j + k * 8];
+				cedrus_write(dev, VE_DEC_H265_SRAM_DATA, val);
+			}
+
+	for (i = 0; i < 6; i++)
+		for (j = 0; j < 4; j++) {
+			val = ((u32)scaling->scaling_list_4x4[i][j + 12] << 24) |
+			      ((u32)scaling->scaling_list_4x4[i][j + 8] << 16) |
+			      ((u32)scaling->scaling_list_4x4[i][j + 4] << 8) |
+			      scaling->scaling_list_4x4[i][j];
+			cedrus_write(dev, VE_DEC_H265_SRAM_DATA, val);
+		}
+}
+
+static void write_entry_point_list(struct cedrus_ctx *ctx,
+				   struct cedrus_run *run)
+{
+	const struct v4l2_ctrl_hevc_slice_params *slice_params;
+	unsigned int ctb_size_luma, width_in_ctb_luma;
+	unsigned int log2_max_luma_coding_block_size;
+	const struct v4l2_ctrl_hevc_pps *pps;
+	const struct v4l2_ctrl_hevc_sps *sps;
+	struct cedrus_dev *dev = ctx->dev;
+	uint32_t *entry_points;
+	int i, x, tx, y, ty;
+
+	pps = run->h265.pps;
+	sps = run->h265.sps;
+	slice_params = run->h265.slice_params;
+
+	log2_max_luma_coding_block_size =
+		sps->log2_min_luma_coding_block_size_minus3 + 3 +
+		sps->log2_diff_max_min_luma_coding_block_size;
+	ctb_size_luma = 1 << log2_max_luma_coding_block_size;
+	width_in_ctb_luma = DIV_ROUND_UP(sps->pic_width_in_luma_samples, ctb_size_luma);
+
+	for (x = 0, tx = 0; tx < pps->num_tile_columns_minus1 + 1; tx++) {
+		if (x + pps->column_width_minus1[tx] + 1 > (slice_params->slice_segment_addr % width_in_ctb_luma))
+			break;
+
+		x += pps->column_width_minus1[tx] + 1;
+	}
+
+	for (y = 0, ty = 0; ty < pps->num_tile_rows_minus1 + 1; ty++) {
+		if (y + pps->row_height_minus1[ty] + 1 > (slice_params->slice_segment_addr / width_in_ctb_luma))
+			break;
+
+		y += pps->row_height_minus1[ty] + 1;
+	}
+
+	cedrus_write(dev, VE_DEC_H265_TILE_START_CTB, (y << 16) | (x << 0));
+	cedrus_write(dev, VE_DEC_H265_TILE_END_CTB,
+		     ((y + pps->row_height_minus1[ty]) << 16) |
+		     ((x + pps->column_width_minus1[tx]) << 0));
+
+	entry_points = ctx->codec.h265.entry_points_buf;
+	if (pps->entropy_coding_sync_enabled_flag) {
+		for (i = 0; i < slice_params->num_entry_point_offsets; i++)
+			entry_points[i] = slice_params->entry_point_offset_minus1[i] + 1;
+	} else {
+		for (i = 0; i < slice_params->num_entry_point_offsets; i++) {
+			if (tx + 1 >= pps->num_tile_columns_minus1 + 1) {
+				x = tx = 0;
+				y += pps->row_height_minus1[ty++] + 1;
+			} else {
+				x += pps->column_width_minus1[tx++] + 1;
+			}
+
+			entry_points[i * 4 + 0] = slice_params->entry_point_offset_minus1[i] + 1;
+			entry_points[i * 4 + 1] = 0x0;
+			entry_points[i * 4 + 2] = (y << 16) | (x << 0);
+			entry_points[i * 4 + 3] = ((y + pps->row_height_minus1[ty]) << 16) | ((x + pps->column_width_minus1[tx]) << 0);
+		}
+	}
+}
+
 static void cedrus_h265_setup(struct cedrus_ctx *ctx,
 			      struct cedrus_run *run)
 {
@@ -228,6 +349,7 @@ static void cedrus_h265_setup(struct cedrus_ctx *ctx,
 	const struct v4l2_ctrl_hevc_pps *pps;
 	const struct v4l2_ctrl_hevc_slice_params *slice_params;
 	const struct v4l2_hevc_pred_weight_table *pred_weight_table;
+	struct cedrus_buffer *cedrus_buf;
 	dma_addr_t src_buf_addr;
 	dma_addr_t src_buf_end_addr;
 	u32 chroma_log2_weight_denom;
@@ -240,43 +362,10 @@ static void cedrus_h265_setup(struct cedrus_ctx *ctx,
 	slice_params = run->h265.slice_params;
 	pred_weight_table = &slice_params->pred_weight_table;
 
-	/* MV column buffer size and allocation. */
-	if (!ctx->codec.h265.mv_col_buf_size) {
-		unsigned int num_buffers =
-			run->dst->vb2_buf.vb2_queue->num_buffers;
-		unsigned int log2_max_luma_coding_block_size =
-			sps->log2_min_luma_coding_block_size_minus3 + 3 +
-			sps->log2_diff_max_min_luma_coding_block_size;
-		unsigned int ctb_size_luma =
-			1 << log2_max_luma_coding_block_size;
-
-		/*
-		 * Each CTB requires a MV col buffer with a specific unit size.
-		 * Since the address is given with missing lsb bits, 1 KiB is
-		 * added to each buffer to ensure proper alignment.
-		 */
-		ctx->codec.h265.mv_col_buf_unit_size =
-			DIV_ROUND_UP(ctx->src_fmt.width, ctb_size_luma) *
-			DIV_ROUND_UP(ctx->src_fmt.height, ctb_size_luma) *
-			CEDRUS_H265_MV_COL_BUF_UNIT_CTB_SIZE + SZ_1K;
-
-		ctx->codec.h265.mv_col_buf_size = num_buffers *
-			ctx->codec.h265.mv_col_buf_unit_size;
-
-		ctx->codec.h265.mv_col_buf =
-			dma_alloc_coherent(dev->dev,
-					   ctx->codec.h265.mv_col_buf_size,
-					   &ctx->codec.h265.mv_col_buf_addr,
-					   GFP_KERNEL);
-		if (!ctx->codec.h265.mv_col_buf) {
-			ctx->codec.h265.mv_col_buf_size = 0;
-			// TODO: Abort the process here.
-			return;
-		}
-	}
-
 	/* Activate H265 engine. */
 	cedrus_engine_enable(dev, CEDRUS_CODEC_H265);
+	if (sps->pic_width_in_luma_samples > 2048)
+		cedrus_write(dev, VE_MODE, cedrus_read(dev, VE_MODE) | BIT(21));
 
 	/* Source offset and length in bits. */
 
@@ -300,18 +389,35 @@ static void cedrus_h265_setup(struct cedrus_ctx *ctx,
 	src_buf_end_addr = src_buf_addr +
 			   DIV_ROUND_UP(slice_params->bit_size, 8);
 
-	reg = VE_DEC_H265_BITS_END_ADDR_BASE(src_buf_end_addr);
+	reg = VE_DEC_H265_BITS_END_ADDR_BASE(ALIGN(src_buf_end_addr, 1024) - 1);
 	cedrus_write(dev, VE_DEC_H265_BITS_END_ADDR, reg);
 
-	/* Coding tree block address: start at the beginning. */
-	reg = VE_DEC_H265_DEC_CTB_ADDR_X(0) | VE_DEC_H265_DEC_CTB_ADDR_Y(0);
-	cedrus_write(dev, VE_DEC_H265_DEC_CTB_ADDR, reg);
-
 	cedrus_write(dev, VE_DEC_H265_TILE_START_CTB, 0);
 	cedrus_write(dev, VE_DEC_H265_TILE_END_CTB, 0);
 
+	if (pps->tiles_enabled_flag || pps->entropy_coding_sync_enabled_flag)
+		write_entry_point_list(ctx, run);
+
+	/* Coding tree block address */
+	reg = 0;
+	if (!slice_params->first_slice_segment_in_pic_flag) {
+		unsigned int ctb_size_luma, width_in_ctb_luma;
+		unsigned int log2_max_luma_coding_block_size;
+
+		log2_max_luma_coding_block_size =
+			sps->log2_min_luma_coding_block_size_minus3 + 3 +
+			sps->log2_diff_max_min_luma_coding_block_size;
+		ctb_size_luma = 1 << log2_max_luma_coding_block_size;
+		width_in_ctb_luma = DIV_ROUND_UP(sps->pic_width_in_luma_samples, ctb_size_luma);
+
+		reg = VE_DEC_H265_DEC_CTB_ADDR_X(slice_params->slice_segment_addr % width_in_ctb_luma);
+		reg |= VE_DEC_H265_DEC_CTB_ADDR_Y(slice_params->slice_segment_addr / width_in_ctb_luma);
+	}
+	cedrus_write(dev, VE_DEC_H265_DEC_CTB_ADDR, reg);
+
 	/* Clear the number of correctly-decoded coding tree blocks. */
-	cedrus_write(dev, VE_DEC_H265_DEC_CTB_NUM, 0);
+	if (slice_params->first_slice_segment_in_pic_flag)
+		cedrus_write(dev, VE_DEC_H265_DEC_CTB_NUM, 0);
 
 	/* Initialize bitstream access. */
 	cedrus_write(dev, VE_DEC_H265_TRIGGER, VE_DEC_H265_TRIGGER_INIT_SWDEC);
@@ -334,6 +440,7 @@ static void cedrus_h265_setup(struct cedrus_ctx *ctx,
 	      VE_DEC_H265_DEC_SPS_HDR_LOG2_DIFF_MAX_MIN_LUMA_CODING_BLOCK_SIZE(sps->log2_diff_max_min_luma_coding_block_size) |
 	      VE_DEC_H265_DEC_SPS_HDR_LOG2_MIN_LUMA_CODING_BLOCK_SIZE_MINUS3(sps->log2_min_luma_coding_block_size_minus3) |
 	      VE_DEC_H265_DEC_SPS_HDR_BIT_DEPTH_CHROMA_MINUS8(sps->bit_depth_chroma_minus8) |
+	      VE_DEC_H265_DEC_SPS_HDR_BIT_DEPTH_LUMA_MINUS8(sps->bit_depth_luma_minus8) |
 	      VE_DEC_H265_DEC_SPS_HDR_SEPARATE_COLOUR_PLANE_FLAG(sps->separate_colour_plane_flag) |
 	      VE_DEC_H265_DEC_SPS_HDR_CHROMA_FORMAT_IDC(sps->chroma_format_idc);
 
@@ -363,7 +470,7 @@ static void cedrus_h265_setup(struct cedrus_ctx *ctx,
 	      VE_DEC_H265_DEC_PPS_CTRL1_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG(pps->pps_loop_filter_across_slices_enabled_flag) |
 	      VE_DEC_H265_DEC_PPS_CTRL1_LOOP_FILTER_ACROSS_TILES_ENABLED_FLAG(pps->loop_filter_across_tiles_enabled_flag) |
 	      VE_DEC_H265_DEC_PPS_CTRL1_ENTROPY_CODING_SYNC_ENABLED_FLAG(pps->entropy_coding_sync_enabled_flag) |
-	      VE_DEC_H265_DEC_PPS_CTRL1_TILES_ENABLED_FLAG(0) |
+	      VE_DEC_H265_DEC_PPS_CTRL1_TILES_ENABLED_FLAG(pps->tiles_enabled_flag) |
 	      VE_DEC_H265_DEC_PPS_CTRL1_TRANSQUANT_BYPASS_ENABLE_FLAG(pps->transquant_bypass_enabled_flag) |
 	      VE_DEC_H265_DEC_PPS_CTRL1_WEIGHTED_BIPRED_FLAG(pps->weighted_bipred_flag) |
 	      VE_DEC_H265_DEC_PPS_CTRL1_WEIGHTED_PRED_FLAG(pps->weighted_pred_flag);
@@ -384,7 +491,7 @@ static void cedrus_h265_setup(struct cedrus_ctx *ctx,
 	      VE_DEC_H265_DEC_SLICE_HDR_INFO0_COLOUR_PLANE_ID(slice_params->colour_plane_id) |
 	      VE_DEC_H265_DEC_SLICE_HDR_INFO0_SLICE_TYPE(slice_params->slice_type) |
 	      VE_DEC_H265_DEC_SLICE_HDR_INFO0_DEPENDENT_SLICE_SEGMENT_FLAG(pps->dependent_slice_segment_flag) |
-	      VE_DEC_H265_DEC_SLICE_HDR_INFO0_FIRST_SLICE_SEGMENT_IN_PIC_FLAG(1);
+	      VE_DEC_H265_DEC_SLICE_HDR_INFO0_FIRST_SLICE_SEGMENT_IN_PIC_FLAG(slice_params->first_slice_segment_in_pic_flag);
 
 	cedrus_write(dev, VE_DEC_H265_DEC_SLICE_HDR_INFO0, reg);
 
@@ -401,34 +508,68 @@ static void cedrus_h265_setup(struct cedrus_ctx *ctx,
 
 	chroma_log2_weight_denom = pred_weight_table->luma_log2_weight_denom +
 				   pred_weight_table->delta_chroma_log2_weight_denom;
-	reg = VE_DEC_H265_DEC_SLICE_HDR_INFO2_NUM_ENTRY_POINT_OFFSETS(0) |
+	reg = VE_DEC_H265_DEC_SLICE_HDR_INFO2_NUM_ENTRY_POINT_OFFSETS(slice_params->num_entry_point_offsets) |
 	      VE_DEC_H265_DEC_SLICE_HDR_INFO2_CHROMA_LOG2_WEIGHT_DENOM(chroma_log2_weight_denom) |
 	      VE_DEC_H265_DEC_SLICE_HDR_INFO2_LUMA_LOG2_WEIGHT_DENOM(pred_weight_table->luma_log2_weight_denom);
 
 	cedrus_write(dev, VE_DEC_H265_DEC_SLICE_HDR_INFO2, reg);
 
+	cedrus_write(dev, VE_DEC_H265_ENTRY_POINT_OFFSET_ADDR, ctx->codec.h265.entry_points_buf_addr >> 8);
+
 	/* Decoded picture size. */
 
-	reg = VE_DEC_H265_DEC_PIC_SIZE_WIDTH(ctx->src_fmt.width) |
-	      VE_DEC_H265_DEC_PIC_SIZE_HEIGHT(ctx->src_fmt.height);
+	reg = VE_DEC_H265_DEC_PIC_SIZE_WIDTH(sps->pic_width_in_luma_samples) |
+	      VE_DEC_H265_DEC_PIC_SIZE_HEIGHT(sps->pic_height_in_luma_samples);
 
 	cedrus_write(dev, VE_DEC_H265_DEC_PIC_SIZE, reg);
 
-	/* Scaling list. */
+	/* Scaling list */
 
-	reg = VE_DEC_H265_SCALING_LIST_CTRL0_DEFAULT;
+	if (sps->scaling_list_enabled_flag) {
+		cedrus_h265_write_scaling_list(ctx, run);
+		reg = VE_DEC_H265_SCALING_LIST_CTRL0_ENABLED_FLAG(1);
+	} else {
+		reg = VE_DEC_H265_SCALING_LIST_CTRL0_DEFAULT;
+	}
 	cedrus_write(dev, VE_DEC_H265_SCALING_LIST_CTRL0, reg);
 
 	/* Neightbor information address. */
 	reg = VE_DEC_H265_NEIGHBOR_INFO_ADDR_BASE(ctx->codec.h265.neighbor_info_buf_addr);
 	cedrus_write(dev, VE_DEC_H265_NEIGHBOR_INFO_ADDR, reg);
 
+	cedrus_write(dev, VE_DEC_H265_LOW_ADDR, 0);
+
 	/* Write decoded picture buffer in pic list. */
 	cedrus_h265_frame_info_write_dpb(ctx, slice_params->dpb,
 					 slice_params->num_active_dpb_entries);
 
 	/* Output frame. */
 
+	cedrus_buf = vb2_to_cedrus_buffer(ctx->dst_bufs[run->dst->vb2_buf.index]);
+	if (!cedrus_buf->mv_col_buf_size) {
+		unsigned int ctb_size_luma, width_in_ctb_luma;
+		unsigned int log2_max_luma_coding_block_size;
+
+		log2_max_luma_coding_block_size =
+			sps->log2_min_luma_coding_block_size_minus3 + 3 +
+			sps->log2_diff_max_min_luma_coding_block_size;
+		ctb_size_luma = 1 << log2_max_luma_coding_block_size;
+		width_in_ctb_luma = DIV_ROUND_UP(sps->pic_width_in_luma_samples, ctb_size_luma);
+
+		cedrus_buf->mv_col_buf_size = ALIGN(width_in_ctb_luma *
+		DIV_ROUND_UP(sps->pic_height_in_luma_samples, ctb_size_luma) *
+		CEDRUS_H265_MV_COL_BUF_UNIT_CTB_SIZE, 1024);
+
+		cedrus_buf->mv_col_buf =
+			dma_alloc_coherent(dev->dev,
+					   cedrus_buf->mv_col_buf_size,
+					   &cedrus_buf->mv_col_buf_dma,
+					   GFP_KERNEL);
+
+		if (!cedrus_buf->mv_col_buf)
+			cedrus_buf->mv_col_buf_size = 0;
+	}
+
 	output_pic_list_index = V4L2_HEVC_DPB_ENTRIES_NUM_MAX;
 	pic_order_cnt[0] = slice_params->slice_pic_order_cnt;
 	pic_order_cnt[1] = slice_params->slice_pic_order_cnt;
@@ -444,36 +585,36 @@ static void cedrus_h265_setup(struct cedrus_ctx *ctx,
 	if (slice_params->slice_type != V4L2_HEVC_SLICE_TYPE_I) {
 		cedrus_h265_ref_pic_list_write(dev, slice_params->dpb,
 					       slice_params->ref_idx_l0,
-					       slice_params->num_ref_idx_l0_active_minus1 + 1,
-					       VE_DEC_H265_SRAM_OFFSET_REF_PIC_LIST0);
+			slice_params->num_ref_idx_l0_active_minus1 + 1,
+			VE_DEC_H265_SRAM_OFFSET_REF_PIC_LIST0);
 
 		if (pps->weighted_pred_flag || pps->weighted_bipred_flag)
 			cedrus_h265_pred_weight_write(dev,
-						      pred_weight_table->delta_luma_weight_l0,
-						      pred_weight_table->luma_offset_l0,
-						      pred_weight_table->delta_chroma_weight_l0,
-						      pred_weight_table->chroma_offset_l0,
-						      slice_params->num_ref_idx_l0_active_minus1 + 1,
-						      VE_DEC_H265_SRAM_OFFSET_PRED_WEIGHT_LUMA_L0,
-						      VE_DEC_H265_SRAM_OFFSET_PRED_WEIGHT_CHROMA_L0);
+				pred_weight_table->delta_luma_weight_l0,
+				pred_weight_table->luma_offset_l0,
+				pred_weight_table->delta_chroma_weight_l0,
+				pred_weight_table->chroma_offset_l0,
+				slice_params->num_ref_idx_l0_active_minus1 + 1,
+				VE_DEC_H265_SRAM_OFFSET_PRED_WEIGHT_LUMA_L0,
+				VE_DEC_H265_SRAM_OFFSET_PRED_WEIGHT_CHROMA_L0);
 	}
 
 	/* Reference picture list 1 (for B frames). */
 	if (slice_params->slice_type == V4L2_HEVC_SLICE_TYPE_B) {
 		cedrus_h265_ref_pic_list_write(dev, slice_params->dpb,
 					       slice_params->ref_idx_l1,
-					       slice_params->num_ref_idx_l1_active_minus1 + 1,
-					       VE_DEC_H265_SRAM_OFFSET_REF_PIC_LIST1);
+			slice_params->num_ref_idx_l1_active_minus1 + 1,
+			VE_DEC_H265_SRAM_OFFSET_REF_PIC_LIST1);
 
 		if (pps->weighted_bipred_flag)
 			cedrus_h265_pred_weight_write(dev,
-						      pred_weight_table->delta_luma_weight_l1,
-						      pred_weight_table->luma_offset_l1,
-						      pred_weight_table->delta_chroma_weight_l1,
-						      pred_weight_table->chroma_offset_l1,
-						      slice_params->num_ref_idx_l1_active_minus1 + 1,
-						      VE_DEC_H265_SRAM_OFFSET_PRED_WEIGHT_LUMA_L1,
-						      VE_DEC_H265_SRAM_OFFSET_PRED_WEIGHT_CHROMA_L1);
+				pred_weight_table->delta_luma_weight_l1,
+				pred_weight_table->luma_offset_l1,
+				pred_weight_table->delta_chroma_weight_l1,
+				pred_weight_table->chroma_offset_l1,
+				slice_params->num_ref_idx_l1_active_minus1 + 1,
+				VE_DEC_H265_SRAM_OFFSET_PRED_WEIGHT_LUMA_L1,
+				VE_DEC_H265_SRAM_OFFSET_PRED_WEIGHT_CHROMA_L1);
 	}
 
 	/* Enable appropriate interruptions. */
@@ -484,9 +625,6 @@ static int cedrus_h265_start(struct cedrus_ctx *ctx)
 {
 	struct cedrus_dev *dev = ctx->dev;
 
-	/* The buffer size is calculated at setup time. */
-	ctx->codec.h265.mv_col_buf_size = 0;
-
 	ctx->codec.h265.neighbor_info_buf =
 		dma_alloc_coherent(dev->dev, CEDRUS_H265_NEIGHBOR_INFO_BUF_SIZE,
 				   &ctx->codec.h265.neighbor_info_buf_addr,
@@ -494,6 +632,17 @@ static int cedrus_h265_start(struct cedrus_ctx *ctx)
 	if (!ctx->codec.h265.neighbor_info_buf)
 		return -ENOMEM;
 
+	ctx->codec.h265.entry_points_buf =
+		dma_alloc_coherent(dev->dev, CEDRUS_H265_ENTRY_POINTS_BUF_SIZE,
+				   &ctx->codec.h265.entry_points_buf_addr,
+				   GFP_KERNEL);
+	if (!ctx->codec.h265.entry_points_buf) {
+		dma_free_coherent(dev->dev, CEDRUS_H265_NEIGHBOR_INFO_BUF_SIZE,
+				  ctx->codec.h265.neighbor_info_buf,
+				  ctx->codec.h265.neighbor_info_buf_addr);
+		return -ENOMEM;
+	}
+
 	return 0;
 }
 
@@ -501,17 +650,12 @@ static void cedrus_h265_stop(struct cedrus_ctx *ctx)
 {
 	struct cedrus_dev *dev = ctx->dev;
 
-	if (ctx->codec.h265.mv_col_buf_size > 0) {
-		dma_free_coherent(dev->dev, ctx->codec.h265.mv_col_buf_size,
-				  ctx->codec.h265.mv_col_buf,
-				  ctx->codec.h265.mv_col_buf_addr);
-
-		ctx->codec.h265.mv_col_buf_size = 0;
-	}
-
 	dma_free_coherent(dev->dev, CEDRUS_H265_NEIGHBOR_INFO_BUF_SIZE,
 			  ctx->codec.h265.neighbor_info_buf,
 			  ctx->codec.h265.neighbor_info_buf_addr);
+	dma_free_coherent(dev->dev, CEDRUS_H265_ENTRY_POINTS_BUF_SIZE,
+			  ctx->codec.h265.entry_points_buf,
+			  ctx->codec.h265.entry_points_buf_addr);
 }
 
 static void cedrus_h265_trigger(struct cedrus_ctx *ctx)
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h
index 87651d6b6227..8d153dbe4f83 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h
@@ -496,6 +496,9 @@
 #define VE_DEC_H265_TILE_START_CTB		(VE_ENGINE_DEC_H265 + 0x68)
 #define VE_DEC_H265_TILE_END_CTB		(VE_ENGINE_DEC_H265 + 0x6c)
 
+#define VE_DEC_H265_SCALING_LIST_DC_COEF0	(VE_ENGINE_DEC_H265 + 0x78)
+#define VE_DEC_H265_SCALING_LIST_DC_COEF1	(VE_ENGINE_DEC_H265 + 0x7c)
+
 #define VE_DEC_H265_LOW_ADDR			(VE_ENGINE_DEC_H265 + 0x80)
 
 #define VE_DEC_H265_LOW_ADDR_PRIMARY_CHROMA(a) \
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.c b/drivers/staging/media/sunxi/cedrus/cedrus_video.c
index dbe6f9510641..a0817cae1d69 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_video.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.c
@@ -433,8 +433,18 @@ static void cedrus_buf_cleanup(struct vb2_buffer *vb)
 	struct vb2_queue *vq = vb->vb2_queue;
 	struct cedrus_ctx *ctx = vb2_get_drv_priv(vq);
 
-	if (!V4L2_TYPE_IS_OUTPUT(vq->type))
+	if (!V4L2_TYPE_IS_OUTPUT(vq->type)) {
+		struct cedrus_buffer *cedrus_buf;
+
+		cedrus_buf = vb2_to_cedrus_buffer(ctx->dst_bufs[vb->index]);
+
+		if (cedrus_buf->mv_col_buf_size)
+			dma_free_coherent(ctx->dev->dev,
+					  cedrus_buf->mv_col_buf_size,
+					  cedrus_buf->mv_col_buf,
+					  cedrus_buf->mv_col_buf_dma);
 		ctx->dst_bufs[vb->index] = NULL;
+	}
 }
 
 static int cedrus_buf_out_validate(struct vb2_buffer *vb)
diff --git a/include/media/hevc-ctrls.h b/include/media/hevc-ctrls.h
index 2de83d9f6d47..19469097c6d4 100644
--- a/include/media/hevc-ctrls.h
+++ b/include/media/hevc-ctrls.h
@@ -17,11 +17,13 @@
 #define V4L2_CID_MPEG_VIDEO_HEVC_SPS		(V4L2_CID_MPEG_BASE + 1008)
 #define V4L2_CID_MPEG_VIDEO_HEVC_PPS		(V4L2_CID_MPEG_BASE + 1009)
 #define V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS	(V4L2_CID_MPEG_BASE + 1010)
+#define V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX	(V4L2_CID_MPEG_BASE + 1011)
 
 /* enum v4l2_ctrl_type type values */
 #define V4L2_CTRL_TYPE_HEVC_SPS 0x0120
 #define V4L2_CTRL_TYPE_HEVC_PPS 0x0121
 #define V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS 0x0122
+#define V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX 0x0123
 
 #define V4L2_HEVC_SLICE_TYPE_B	0
 #define V4L2_HEVC_SLICE_TYPE_P	1
@@ -95,7 +97,7 @@ struct v4l2_ctrl_hevc_pps {
 	__u8	lists_modification_present_flag;
 	__u8	log2_parallel_merge_level_minus2;
 	__u8	slice_segment_header_extension_present_flag;
-	__u8	padding;
+	__u8	scaling_list_enable_flag;
 };
 
 #define V4L2_HEVC_DPB_ENTRY_RPS_ST_CURR_BEFORE	0x01
@@ -179,7 +181,21 @@ struct v4l2_ctrl_hevc_slice_params {
 	/* ISO/IEC 23008-2, ITU-T Rec. H.265: Weighted prediction parameter */
 	struct v4l2_hevc_pred_weight_table pred_weight_table;
 
-	__u8	padding[2];
+	__u32	slice_segment_addr;
+	__u32	num_entry_point_offsets;
+	__u32	entry_point_offset_minus1[256];
+	__u8	first_slice_segment_in_pic_flag;
+
+	__u8	padding;
+};
+
+struct v4l2_ctrl_hevc_scaling_matrix {
+	__u8	scaling_list_4x4[6][16];
+	__u8	scaling_list_8x8[6][64];
+	__u8	scaling_list_16x16[6][64];
+	__u8	scaling_list_32x32[2][64];
+	__u8	scaling_list_dc_coef_16x16[6];
+	__u8	scaling_list_dc_coef_32x32[2];
 };
 
 #endif
-- 
2.21.0

