From 432246948309396204697a010342ba6a5a94f387 Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@siol.net>
Date: Sat, 9 Nov 2019 13:22:05 +0100
Subject: [PATCH 035/170] drv:media:cedrus: hevc: Improve buffer management

Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
---
 drivers/staging/media/sunxi/cedrus/cedrus.h   |   9 +-
 .../staging/media/sunxi/cedrus/cedrus_h265.c  | 120 ++++++++++--------
 2 files changed, 70 insertions(+), 59 deletions(-)

diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.h b/drivers/staging/media/sunxi/cedrus/cedrus.h
index 0a17ed0dc..1b20e23ee 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus.h
+++ b/drivers/staging/media/sunxi/cedrus/cedrus.h
@@ -107,6 +107,11 @@ struct cedrus_buffer {
 			unsigned int			position;
 			enum cedrus_h264_pic_type	pic_type;
 		} h264;
+		struct {
+			void		*mv_col_buf;
+			dma_addr_t	mv_col_buf_dma;
+			ssize_t		mv_col_buf_size;
+		} h265;
 	} codec;
 };
 
@@ -140,10 +145,6 @@ struct cedrus_ctx {
 			ssize_t		intra_pred_buf_size;
 		} 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;
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c
index 685cb00de..55a1874c8 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c
@@ -91,26 +91,66 @@ static void cedrus_h265_sram_write_data(struct cedrus_dev *dev, void *data,
 
 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,
+				       const struct v4l2_ctrl_hevc_sps *sps)
 {
-	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 = NULL;
+	struct vb2_buffer *buf = NULL;
+	struct vb2_queue *vq;
+
+	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+	if (vq)
+		buf = vb2_get_buffer(vq, index);
+
+	if (buf)
+		cedrus_buf = vb2_to_cedrus_buffer(buf);
+
+	if (!cedrus_buf)
+		return 0;
+
+	if (!cedrus_buf->codec.h265.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->codec.h265.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->codec.h265.mv_col_buf =
+			dma_alloc_attrs(ctx->dev->dev,
+					cedrus_buf->codec.h265.mv_col_buf_size,
+					&cedrus_buf->codec.h265.mv_col_buf_dma,
+					GFP_KERNEL, DMA_ATTR_NO_KERNEL_MAPPING);
+
+		if (!cedrus_buf->codec.h265.mv_col_buf) {
+			cedrus_buf->codec.h265.mv_col_buf_size = 0;
+			cedrus_buf->codec.h265.mv_col_buf_dma = 0;
+		}
+	}
+
+	return cedrus_buf->codec.h265.mv_col_buf_dma;
 }
 
 static void cedrus_h265_frame_info_write_single(struct cedrus_ctx *ctx,
 						unsigned int index,
 						bool field_pic,
 						u32 pic_order_cnt[],
-						int buffer_index)
+						int buffer_index,
+						const struct v4l2_ctrl_hevc_sps *sps)
 {
 	struct cedrus_dev *dev = ctx->dev;
 	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, sps),
+		cedrus_h265_frame_info_mv_col_buf_addr(ctx, buffer_index, sps)
 	};
 	u32 offset = VE_DEC_H265_SRAM_OFFSET_FRAME_INFO +
 		     VE_DEC_H265_SRAM_OFFSET_FRAME_INFO_UNIT * index;
@@ -134,7 +174,8 @@ static void cedrus_h265_frame_info_write_single(struct cedrus_ctx *ctx,
 
 static void cedrus_h265_frame_info_write_dpb(struct cedrus_ctx *ctx,
 					     const struct v4l2_hevc_dpb_entry *dpb,
-					     u8 num_active_dpb_entries)
+					     u8 num_active_dpb_entries,
+					     const struct v4l2_ctrl_hevc_sps *sps)
 {
 	struct vb2_queue *vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx,
 					       V4L2_BUF_TYPE_VIDEO_CAPTURE);
@@ -152,7 +193,7 @@ static void cedrus_h265_frame_info_write_dpb(struct cedrus_ctx *ctx,
 
 		cedrus_h265_frame_info_write_single(ctx, i, dpb[i].field_pic,
 						    pic_order_cnt,
-						    buffer_index);
+						    buffer_index, sps);
 	}
 }
 
@@ -416,37 +457,6 @@ static void cedrus_h265_setup(struct cedrus_ctx *ctx,
 	width_in_ctb_luma =
 		DIV_ROUND_UP(sps->pic_width_in_luma_samples, ctb_size_luma);
 
-	/* 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;
-
-		/*
-		 * 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;
-
-		/* Buffer is never accessed by CPU, so we can skip kernel mapping. */
-		ctx->codec.h265.mv_col_buf =
-			dma_alloc_attrs(dev->dev,
-					ctx->codec.h265.mv_col_buf_size,
-					&ctx->codec.h265.mv_col_buf_addr,
-					GFP_KERNEL, DMA_ATTR_NO_KERNEL_MAPPING);
-		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(ctx, CEDRUS_CODEC_H265);
 
@@ -702,7 +712,7 @@ static void cedrus_h265_setup(struct cedrus_ctx *ctx,
 
 	/* Write decoded picture buffer in pic list. */
 	cedrus_h265_frame_info_write_dpb(ctx, decode_params->dpb,
-					 decode_params->num_active_dpb_entries);
+					 decode_params->num_active_dpb_entries, sps);
 
 	/* Output frame. */
 
@@ -713,7 +723,7 @@ static void cedrus_h265_setup(struct cedrus_ctx *ctx,
 	cedrus_h265_frame_info_write_single(ctx, output_pic_list_index,
 					    slice_params->pic_struct != 0,
 					    pic_order_cnt,
-					    run->dst->vb2_buf.index);
+					    run->dst->vb2_buf.index, sps);
 
 	cedrus_write(dev, VE_DEC_H265_OUTPUT_FRAME_IDX, output_pic_list_index);
 
@@ -762,9 +772,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;
-
 	/* Buffer is never accessed by CPU, so we can skip kernel mapping. */
 	ctx->codec.h265.neighbor_info_buf =
 		dma_alloc_attrs(dev->dev, CEDRUS_H265_NEIGHBOR_INFO_BUF_SIZE,
@@ -791,15 +798,6 @@ 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_attrs(dev->dev, ctx->codec.h265.mv_col_buf_size,
-			       ctx->codec.h265.mv_col_buf,
-			       ctx->codec.h265.mv_col_buf_addr,
-			       DMA_ATTR_NO_KERNEL_MAPPING);
-
-		ctx->codec.h265.mv_col_buf_size = 0;
-	}
-
 	dma_free_attrs(dev->dev, CEDRUS_H265_NEIGHBOR_INFO_BUF_SIZE,
 		       ctx->codec.h265.neighbor_info_buf,
 		       ctx->codec.h265.neighbor_info_buf_addr,
@@ -816,6 +814,17 @@ static void cedrus_h265_trigger(struct cedrus_ctx *ctx)
 	cedrus_write(dev, VE_DEC_H265_TRIGGER, VE_DEC_H265_TRIGGER_DEC_SLICE);
 }
 
+static void cedrus_h265_buf_cleanup(struct cedrus_ctx *ctx,
+				    struct cedrus_buffer *buf)
+{
+	if (buf->codec.h265.mv_col_buf_size)
+		dma_free_attrs(ctx->dev->dev,
+			       buf->codec.h265.mv_col_buf_size,
+			       buf->codec.h265.mv_col_buf,
+			       buf->codec.h265.mv_col_buf_dma,
+			       DMA_ATTR_NO_KERNEL_MAPPING);
+}
+
 struct cedrus_dec_ops cedrus_dec_ops_h265 = {
 	.irq_clear	= cedrus_h265_irq_clear,
 	.irq_disable	= cedrus_h265_irq_disable,
@@ -824,4 +833,5 @@ struct cedrus_dec_ops cedrus_dec_ops_h265 = {
 	.start		= cedrus_h265_start,
 	.stop		= cedrus_h265_stop,
 	.trigger	= cedrus_h265_trigger,
+	.buf_cleanup	= cedrus_h265_buf_cleanup,
 };
-- 
2.35.3

