[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250930040348.3702923-30-h.dewangan@samsung.com>
Date: Tue, 30 Sep 2025 09:33:48 +0530
From: Himanshu Dewangan <h.dewangan@...sung.com>
To: mchehab@...nel.org, robh@...nel.org, krzk+dt@...nel.org,
conor+dt@...nel.org, sumit.semwal@...aro.org, christian.koenig@....com,
alim.akhtar@...sung.com, manjun@...sung.com, nagaraju.s@...sung.com,
ih0206.lee@...sung.com, jehyung.lee@...sung.com
Cc: linux-arm-kernel@...ts.infradead.org, linux-media@...r.kernel.org,
devicetree@...r.kernel.org, linux-samsung-soc@...r.kernel.org,
linux-kernel@...r.kernel.org, dri-devel@...ts.freedesktop.org,
linaro-mm-sig@...ts.linaro.org, Himanshu Dewangan <h.dewangan@...sung.com>
Subject: [PATCH 29/29] media: mfc: Hardware‑accelerated encoding support
From: Nagaraju Siddineni <nagaraju.s@...sung.com>
- Add for MPEG‑4, H.263, VP8/VP9, HEVC codec support with extended
contexts and buffer allocation.
- Implement LCU sizing, motion‑estimation buffers, and align
scratch buffers.
- Unify ROI buffer handling across all encoders.
- Register additional pixel formats (`mfc_format.h`).
- Enhance QoS weighting and B‑frame/reference‑count logic.
- Introduce hierarchical‑coding
controls (layer count, per‑layer bitrate)
and SVC for HEVC, VP8, VP9.
- Update timestamp handling for H.263 and improved VP8 buffer‑exhaustion
error reporting.
Signed-off-by: Nagaraju Siddineni <nagaraju.s@...sung.com>
Signed-off-by: Himanshu Dewangan <h.dewangan@...sung.com>
---
.../samsung/exynos-mfc/base/mfc_buf.c | 71 ++++++++++++++++++-
.../samsung/exynos-mfc/base/mfc_format.h | 40 +++++++++++
.../samsung/exynos-mfc/base/mfc_qos.c | 23 +++++-
.../samsung/exynos-mfc/base/mfc_utils.h | 10 ++-
.../samsung/exynos-mfc/mfc_core_buf_ctrl.c | 59 ++++++++++++++-
.../samsung/exynos-mfc/mfc_core_isr.c | 2 +
6 files changed, 198 insertions(+), 7 deletions(-)
diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.c b/drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.c
index 0186fe3327f1..164852e83e7d 100644
--- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.c
+++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.c
@@ -133,10 +133,17 @@ int mfc_alloc_instance_context(struct mfc_core_ctx *core_ctx)
core_ctx->instance_ctx_buf.size = buf_size->other_dec_ctx;
break;
case MFC_REG_CODEC_H264_ENC:
+ case MFC_REG_CODEC_AV1_DEC:
core_ctx->instance_ctx_buf.size = buf_size->h264_enc_ctx;
break;
- case MFC_REG_CODEC_AV1_DEC:
- core_ctx->instance_ctx_buf.size = buf_size->av1_dec_ctx;
+ case MFC_REG_CODEC_HEVC_ENC:
+ core_ctx->instance_ctx_buf.size = buf_size->hevc_enc_ctx;
+ break;
+ case MFC_REG_CODEC_MPEG4_ENC:
+ case MFC_REG_CODEC_H263_ENC:
+ case MFC_REG_CODEC_VP8_ENC:
+ case MFC_REG_CODEC_VP9_ENC:
+ core_ctx->instance_ctx_buf.size = buf_size->other_enc_ctx;
break;
default:
core_ctx->instance_ctx_buf.size = 0;
@@ -256,6 +263,7 @@ static void __mfc_enc_calc_codec_buffer_size(struct mfc_core_ctx *core_ctx)
struct mfc_ctx *ctx = core_ctx->ctx;
struct mfc_enc *enc;
unsigned int mb_width, mb_height;
+ unsigned int lcu_width = 0, lcu_height = 0;
enc = ctx->enc_priv;
enc->tmv_buffer_size = 0;
@@ -263,6 +271,9 @@ static void __mfc_enc_calc_codec_buffer_size(struct mfc_core_ctx *core_ctx)
mb_width = WIDTH_MB(ctx->crop_width);
mb_height = HEIGHT_MB(ctx->crop_height);
+ lcu_width = ENC_LCU_WIDTH(ctx->crop_width);
+ lcu_height = ENC_LCU_HEIGHT(ctx->crop_height);
+
/* default recon buffer size, it can be changed in case of 422, 10bit */
enc->luma_dpb_size =
ALIGN(ENC_LUMA_DPB_SIZE(ctx->crop_width, ctx->crop_height), SZ_64);
@@ -293,6 +304,47 @@ static void __mfc_enc_calc_codec_buffer_size(struct mfc_core_ctx *core_ctx)
(ctx->dpb_count * (enc->luma_dpb_size +
enc->chroma_dpb_size + enc->me_buffer_size));
break;
+ case MFC_REG_CODEC_MPEG4_ENC:
+ case MFC_REG_CODEC_H263_ENC:
+ enc->me_buffer_size =
+ ALIGN(ENC_V100_MPEG4_ME_SIZE(mb_width, mb_height), SZ_256);
+
+ ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, SZ_256);
+ core_ctx->codec_buf.size =
+ ctx->scratch_buf_size + enc->tmv_buffer_size +
+ (ctx->dpb_count * (enc->luma_dpb_size +
+ enc->chroma_dpb_size + enc->me_buffer_size));
+ break;
+ case MFC_REG_CODEC_VP8_ENC:
+ enc->me_buffer_size =
+ ALIGN(ENC_V100_VP8_ME_SIZE(mb_width, mb_height), SZ_256);
+
+ ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, SZ_256);
+ core_ctx->codec_buf.size =
+ ctx->scratch_buf_size + enc->tmv_buffer_size +
+ (ctx->dpb_count * (enc->luma_dpb_size +
+ enc->chroma_dpb_size + enc->me_buffer_size));
+ break;
+ case MFC_REG_CODEC_VP9_ENC:
+ enc->me_buffer_size =
+ ALIGN(ENC_V100_VP9_ME_SIZE(lcu_width, lcu_height), SZ_256);
+
+ ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, SZ_256);
+ core_ctx->codec_buf.size =
+ ctx->scratch_buf_size + enc->tmv_buffer_size +
+ (ctx->dpb_count * (enc->luma_dpb_size +
+ enc->chroma_dpb_size + enc->me_buffer_size));
+ break;
+ case MFC_REG_CODEC_HEVC_ENC:
+ enc->me_buffer_size =
+ ALIGN(ENC_V100_HEVC_ME_SIZE(lcu_width, lcu_height), SZ_256);
+
+ ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, SZ_256);
+ core_ctx->codec_buf.size =
+ ctx->scratch_buf_size + enc->tmv_buffer_size +
+ (ctx->dpb_count * (enc->luma_dpb_size +
+ enc->chroma_dpb_size + enc->me_buffer_size));
+ break;
default:
core_ctx->codec_buf.size = 0;
mfc_err("invalid codec type: %d\n", ctx->codec_mode);
@@ -493,6 +545,7 @@ int mfc_alloc_enc_roi_buffer(struct mfc_core_ctx *core_ctx)
struct mfc_ctx *ctx = core_ctx->ctx;
struct mfc_enc *enc = ctx->enc_priv;
unsigned int mb_width, mb_height;
+ unsigned int lcu_width = 0, lcu_height = 0;
size_t size;
int i;
@@ -503,6 +556,20 @@ int mfc_alloc_enc_roi_buffer(struct mfc_core_ctx *core_ctx)
case MFC_REG_CODEC_H264_ENC:
size = ((((mb_width * (mb_height + 1) / 2) + 15) / 16) * 16) * 2;
break;
+ case MFC_REG_CODEC_MPEG4_ENC:
+ case MFC_REG_CODEC_VP8_ENC:
+ size = mb_width * mb_height;
+ break;
+ case MFC_REG_CODEC_VP9_ENC:
+ lcu_width = (ctx->crop_width + 63) / 64;
+ lcu_height = (ctx->crop_height + 63) / 64;
+ size = lcu_width * lcu_height * 4;
+ break;
+ case MFC_REG_CODEC_HEVC_ENC:
+ lcu_width = (ctx->crop_width + 31) / 32;
+ lcu_height = (ctx->crop_height + 31) / 32;
+ size = lcu_width * lcu_height;
+ break;
default:
mfc_debug(2,
"ROI not supported codec type(%d). Allocate with default size\n",
diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_format.h b/drivers/media/platform/samsung/exynos-mfc/base/mfc_format.h
index e8573d6b6005..070c669e1d82 100644
--- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_format.h
+++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_format.h
@@ -271,6 +271,46 @@ static struct mfc_fmt mfc_formats[] = {
.num_planes = 1,
.mem_planes = 1,
},
+ {
+ .name = "ENC MPEG4",
+ .fourcc = V4L2_PIX_FMT_MPEG4,
+ .codec_mode = MFC_REG_CODEC_MPEG4_ENC,
+ .type = MFC_FMT_STREAM | MFC_FMT_ENC,
+ .num_planes = 1,
+ .mem_planes = 1,
+ },
+ {
+ .name = "ENC H263",
+ .fourcc = V4L2_PIX_FMT_H263,
+ .codec_mode = MFC_REG_CODEC_H263_ENC,
+ .type = MFC_FMT_STREAM | MFC_FMT_ENC,
+ .num_planes = 1,
+ .mem_planes = 1,
+ },
+ {
+ .name = "ENC VP8",
+ .fourcc = V4L2_PIX_FMT_VP8,
+ .codec_mode = MFC_REG_CODEC_VP8_ENC,
+ .type = MFC_FMT_STREAM | MFC_FMT_ENC,
+ .num_planes = 1,
+ .mem_planes = 1,
+ },
+ {
+ .name = "ENC VP9",
+ .fourcc = V4L2_PIX_FMT_VP9,
+ .codec_mode = MFC_REG_CODEC_VP9_ENC,
+ .type = MFC_FMT_STREAM | MFC_FMT_ENC,
+ .num_planes = 1,
+ .mem_planes = 1,
+ },
+ {
+ .name = "ENC HEVC",
+ .fourcc = V4L2_PIX_FMT_HEVC,
+ .codec_mode = MFC_REG_CODEC_HEVC_ENC,
+ .type = MFC_FMT_STREAM | MFC_FMT_ENC,
+ .num_planes = 1,
+ .mem_planes = 1,
+ },
};
#endif /* __MFC_FORMAT_H */
diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_qos.c b/drivers/media/platform/samsung/exynos-mfc/base/mfc_qos.c
index 40541e2d626f..73058ace1fd6 100644
--- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_qos.c
+++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_qos.c
@@ -58,6 +58,7 @@ static inline unsigned long __mfc_qos_add_weight(struct mfc_ctx *ctx, unsigned l
break;
case MFC_REG_CODEC_VP8_DEC:
+ case MFC_REG_CODEC_VP8_ENC:
weight = (weight * 100) / qos_weight->weight_vp8_vp9;
mfc_ctx_debug(3, "[QoS] vp8, vp9 codec, weight: %d\n", weight / 10);
if (num_planes == 3) {
@@ -80,12 +81,26 @@ static inline unsigned long __mfc_qos_add_weight(struct mfc_ctx *ctx, unsigned l
}
break;
+ case MFC_REG_CODEC_HEVC_ENC:
+ weight = (weight * 100) / qos_weight->weight_h264_hevc;
+ mfc_ctx_debug(3, "[QoS] h264, hevc codec, weight: %d\n", weight / 10);
+ if (num_planes == 3) {
+ weight = (weight * 100) / qos_weight->weight_3plane;
+ mfc_ctx_debug(3, "[QoS] 3 plane, weight: %d\n", weight / 10);
+ }
+ if (ctx->is_422) {
+ weight = (weight * 100) / qos_weight->weight_422;
+ mfc_ctx_debug(3, "[QoS] 422foramt, weight: %d\n", weight / 10);
+ }
+ break;
+
case MFC_REG_CODEC_AV1_DEC:
weight = (weight * 100) / qos_weight->weight_av1;
mfc_ctx_debug(3, "[QoS] av1 codec, weight: %d\n", weight / 10);
break;
case MFC_REG_CODEC_VP9_DEC:
+ case MFC_REG_CODEC_VP9_ENC:
weight = (weight * 100) / qos_weight->weight_vp8_vp9;
mfc_ctx_debug(3, "[QoS] vp8, vp9 codec, weight: %d\n", weight / 10);
@@ -109,6 +124,8 @@ static inline unsigned long __mfc_qos_add_weight(struct mfc_ctx *ctx, unsigned l
case MFC_REG_CODEC_VC1_RCV_DEC:
case MFC_REG_CODEC_VC1_DEC:
case MFC_REG_CODEC_MPEG2_DEC:
+ case MFC_REG_CODEC_MPEG4_ENC:
+ case MFC_REG_CODEC_H263_ENC:
weight = (weight * 100) / qos_weight->weight_other_codec;
mfc_ctx_debug(3, "[QoS] other codec, weight: %d\n", weight / 10);
break;
@@ -122,9 +139,13 @@ static inline unsigned long __mfc_qos_add_weight(struct mfc_ctx *ctx, unsigned l
if (mfc_is_enc_bframe(ctx)) {
weight = (weight * 100) / qos_weight->weight_bframe;
mfc_ctx_debug(3, "[QoS] B frame encoding, weight: %d\n", weight / 10);
- } else if (IS_H264_ENC(ctx) && (p->num_refs_for_p >= 2)) {
+ } else if ((IS_H264_ENC(ctx) || IS_HEVC_ENC(ctx) || IS_VP8_ENC(ctx) ||
+ IS_VP9_ENC(ctx)) && (p->num_refs_for_p >= 2)) {
weight = (weight * 100) / qos_weight->weight_num_of_ref;
mfc_ctx_debug(3, "[QoS] num of ref >= 2, weight: %d\n", weight / 10);
+ } else if (IS_HEVC_ENC(ctx) && p->codec.hevc.general_pb_enable) {
+ weight = (weight * 100) / qos_weight->weight_gpb;
+ mfc_ctx_debug(3, "[QoS] Genaral PB, weight: %d\n", weight / 10);
}
}
if (dec) {
diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_utils.h b/drivers/media/platform/samsung/exynos-mfc/base/mfc_utils.h
index a127f330fe16..8526f5676761 100644
--- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_utils.h
+++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_utils.h
@@ -229,7 +229,11 @@ static inline int mfc_is_enc_bframe(struct mfc_ctx *ctx)
if (IS_H264_ENC(ctx)) {
num_hier_layer = p->codec.h264.num_hier_layer;
hier_qp_type = (int)p->codec.h264.hier_qp_type;
+ } else if (IS_HEVC_ENC(ctx)) {
+ num_hier_layer = p->codec.hevc.num_hier_layer;
+ hier_qp_type = (int)p->codec.hevc.hier_qp_type;
}
+
if (enc->params.num_b_frame ||
(num_hier_layer >= 2 &&
hier_qp_type == V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_B))
@@ -402,7 +406,11 @@ static inline int mfc_enc_get_ts_delta(struct mfc_ctx *ctx)
* so thie is also divided into pre-calculated 100.
* (Preventing both overflow and calculation duplication)
*/
- ts_delta = ctx->src_ts.ts_last_interval / 100;
+ if (IS_H263_ENC(ctx))
+ ts_delta = ctx->src_ts.ts_last_interval *
+ p->rc_framerate_res / USEC_PER_SEC;
+ else
+ ts_delta = ctx->src_ts.ts_last_interval / 100;
}
return ts_delta;
}
diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_buf_ctrl.c b/drivers/media/platform/samsung/exynos-mfc/mfc_core_buf_ctrl.c
index cc0a20bea33a..11e9c2622242 100644
--- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_buf_ctrl.c
+++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_buf_ctrl.c
@@ -11,6 +11,25 @@
#include "mfc_core_reg_api.h"
+static int __mfc_enc_check_adaptive_temporal_svc(int id,
+ struct mfc_enc_params *p,
+ struct temporal_layer_info
+ *temporal_LC)
+{
+ unsigned int new_num_layer = temporal_LC->temporal_layer_count;
+ unsigned int old_num_layer, ref_type;
+
+ if (id == V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_CH) {
+ old_num_layer = p->codec.hevc.num_hier_layer;
+ ref_type = p->codec.hevc.hier_ref_type;
+ if (ref_type == 0 && old_num_layer != new_num_layer)
+ if (new_num_layer == 1 || old_num_layer == 1)
+ return 1;
+ }
+
+ return 0;
+}
+
static void __mfc_enc_store_buf_ctrls_temporal_svc(int id,
struct mfc_enc_params *p,
struct temporal_layer_info
@@ -26,6 +45,24 @@ static void __mfc_enc_store_buf_ctrls_temporal_svc(int id,
p->codec.h264.hier_bit_layer[i] =
temporal_LC->temporal_layer_bitrate[i];
break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_CH:
+ p->codec.hevc.num_hier_layer = num_layer & 0x7;
+ for (i = 0; i < (num_layer & 0x7); i++)
+ p->codec.hevc.hier_bit_layer[i] =
+ temporal_LC->temporal_layer_bitrate[i];
+ break;
+ case V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_CH:
+ p->codec.vp8.num_hier_layer = num_layer & 0x7;
+ for (i = 0; i < (num_layer & 0x7); i++)
+ p->codec.vp8.hier_bit_layer[i] =
+ temporal_LC->temporal_layer_bitrate[i];
+ break;
+ case V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_CH:
+ p->codec.vp9.num_hier_layer = num_layer & 0x7;
+ for (i = 0; i < (num_layer & 0x7); i++)
+ p->codec.vp9.hier_bit_layer[i] =
+ temporal_LC->temporal_layer_bitrate[i];
+ break;
default:
break;
}
@@ -43,12 +80,21 @@ static void __mfc_core_enc_set_buf_ctrls_temporal_svc(struct mfc_core *core,
struct mfc_enc_params *p = &enc->params;
if (buf_ctrl->id
- == V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_CH) {
+ == V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_CH ||
+ buf_ctrl->id
+ == V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_CH ||
+ buf_ctrl->id
+ == V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_CH ||
+ buf_ctrl->id
+ == V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_CH) {
memcpy(&temporal_LC,
enc->sh_handle_svc.vaddr,
sizeof(struct temporal_layer_info));
- if ((temporal_LC.temporal_layer_count & 0x7) < 1) {
+ if (((temporal_LC.temporal_layer_count & 0x7) < 1) ||
+ (temporal_LC.temporal_layer_count > 3 && IS_VP8_ENC(ctx)) ||
+ (temporal_LC.temporal_layer_count > 3 &&
+ IS_VP9_ENC(ctx))) {
/* clear NUM_T_LAYER_CHANGE */
value = MFC_CORE_READL(buf_ctrl->flag_addr);
value &= ~BIT(10);
@@ -61,7 +107,14 @@ static void __mfc_core_enc_set_buf_ctrls_temporal_svc(struct mfc_core *core,
value = MFC_CORE_READL(buf_ctrl->flag_addr);
value &= ~(0x3 << 21);
-
+ /* Adaptive temporal SVC(layer count 1: IPPP, others: H-B) */
+ if (__mfc_enc_check_adaptive_temporal_svc
+ (buf_ctrl->id, p, &temporal_LC)) {
+ if (temporal_LC.temporal_layer_count > 1)
+ value |= BIT(21);
+ else
+ value |= (0x2 << 21);
+ }
MFC_CORE_WRITEL(value, buf_ctrl->flag_addr);
/* Store temporal layer information */
diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_isr.c b/drivers/media/platform/samsung/exynos-mfc/mfc_core_isr.c
index 1a3cf7e76e29..9c5be84fcbc6 100644
--- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_isr.c
+++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_isr.c
@@ -1883,6 +1883,8 @@ static inline void __mfc_handle_nal_abort(struct mfc_core *core,
if (ctx->type == MFCINST_ENCODER) {
mfc_change_state(core_ctx, MFCINST_RUNNING_BUF_FULL);
enc->buf_full = 0;
+ if (IS_VP8_ENC(ctx))
+ mfc_err("stream buffer size isn't enough\n");
__mfc_handle_stream(core, ctx, reason);
} else {
mfc_change_state(core_ctx, MFCINST_ABORT);
--
2.34.1
Powered by blists - more mailing lists