From: Nagaraju Siddineni <[email protected]>

- Implement QoS support (load estimation, dynamic level adjustment,
PM‑QoS & idle‑mode handling)
- Add mfc_qos.c/h and integrate it into the Makefile
- Introduce instance‑context allocation/release APIs
(mfc_alloc_instance_context, mfc_release_instance_context)
- Update driver code to use the new APIs and add debugging/trace hooks
- Provide documentation/comments for the new functionality

Signed-off-by: Nagaraju Siddineni <[email protected]>
Signed-off-by: Himanshu Dewangan <[email protected]>
---
 .../platform/samsung/exynos-mfc/Makefile      |   1 +
 .../samsung/exynos-mfc/base/mfc_buf.c         | 207 ++++
 .../samsung/exynos-mfc/base/mfc_buf.h         |   8 +
 .../samsung/exynos-mfc/base/mfc_qos.c         | 965 ++++++++++++++++++
 .../samsung/exynos-mfc/base/mfc_qos.h         |  99 ++
 5 files changed, 1280 insertions(+)
 create mode 100644 drivers/media/platform/samsung/exynos-mfc/base/mfc_qos.c
 create mode 100644 drivers/media/platform/samsung/exynos-mfc/base/mfc_qos.h

diff --git a/drivers/media/platform/samsung/exynos-mfc/Makefile 
b/drivers/media/platform/samsung/exynos-mfc/Makefile
index bd5f80953bab..9def2686cd4e 100644
--- a/drivers/media/platform/samsung/exynos-mfc/Makefile
+++ b/drivers/media/platform/samsung/exynos-mfc/Makefile
@@ -21,5 +21,6 @@ exynos_mfc-y += mfc_core_hw_reg_api.o mfc_core_reg_api.o
 #Common base layer
 exynos_mfc-y += base/mfc_rate_calculate.o base/mfc_queue.o base/mfc_utils.o
 exynos_mfc-y += base/mfc_buf.o base/mfc_mem.o
+exynos_mfc-y += base/mfc_qos.o
 #Tracing
 # exynos_mfc-y += trace/mfc_trace_points.o
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 b8b140824aab..bd1baf34e0b0 100644
--- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.c
+++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.c
@@ -88,6 +88,213 @@ int mfc_alloc_common_context(struct mfc_core *core)
        return ret;
 }
 
+/* Release instance buffer */
+void mfc_release_instance_context(struct mfc_core_ctx *core_ctx)
+{
+       struct mfc_core *core = core_ctx->core;
+
+       mfc_debug_enter();
+
+       mfc_iova_pool_free(core->dev, &core_ctx->instance_ctx_buf);
+
+       mfc_mem_special_buf_free(core->dev, &core_ctx->instance_ctx_buf);
+
+       mfc_debug_leave();
+}
+
+/* Allocate memory for instance data buffer */
+int mfc_alloc_instance_context(struct mfc_core_ctx *core_ctx)
+{
+       struct mfc_ctx *ctx = core_ctx->ctx;
+       struct mfc_dev *dev = ctx->dev;
+       struct mfc_ctx_buf_size *buf_size;
+
+       mfc_debug_enter();
+
+       buf_size = dev->variant->buf_size->ctx_buf;
+
+       switch (ctx->codec_mode) {
+       case MFC_REG_CODEC_H264_DEC:
+       case MFC_REG_CODEC_H264_MVC_DEC:
+               core_ctx->instance_ctx_buf.size = buf_size->h264_dec_ctx;
+               break;
+       default:
+               core_ctx->instance_ctx_buf.size = 0;
+               mfc_err("Codec type(%d) should be checked!\n", ctx->codec_mode);
+               return -ENOMEM;
+       }
+
+       core_ctx->instance_ctx_buf.buftype = MFCBUF_NORMAL;
+
+       snprintf(core_ctx->instance_ctx_buf.name,
+                MFC_NUM_SPECIAL_BUF_NAME,
+                "MFC%d ctx%d instance",
+                core_ctx->core->id,
+                core_ctx->num);
+       if (mfc_mem_special_buf_alloc(dev, &core_ctx->instance_ctx_buf)) {
+               mfc_err("Allocating context buffer failed\n");
+               return -ENOMEM;
+       }
+
+       if (mfc_iova_pool_alloc(dev, &core_ctx->instance_ctx_buf)) {
+               mfc_err("[POOL] failed to get iova\n");
+               mfc_release_instance_context(core_ctx);
+               return -ENOMEM;
+       }
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+static void __mfc_dec_calc_codec_buffer_size(struct mfc_core_ctx *core_ctx)
+{
+       struct mfc_ctx *ctx = core_ctx->ctx;
+       struct mfc_dec *dec = ctx->dec_priv;
+
+       /* Codecs have different memory requirements */
+       switch (ctx->codec_mode) {
+       case MFC_REG_CODEC_H264_DEC:
+               ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, SZ_256);
+               core_ctx->codec_buf.size = ctx->scratch_buf_size;
+               ctx->mv_buf.size = dec->mv_count * ctx->mv_size;
+               break;
+       default:
+               core_ctx->codec_buf.size = 0;
+               mfc_err("invalid codec type: %d\n", ctx->codec_mode);
+               break;
+       }
+
+       mfc_debug(2,
+                 "[MEMINFO] scratch: %zu, MV: %zu x count %d\n",
+                 ctx->scratch_buf_size,
+                 ctx->mv_size,
+                 dec->mv_count);
+       if (dec->loop_filter_mpeg4)
+               mfc_debug(2,
+                         "[MEMINFO] (loopfilter luma: %zu, chroma: %zu) x 
count %d\n",
+                         ctx->loopfilter_luma_size,
+                         ctx->loopfilter_chroma_size,
+                         NUM_MPEG4_LF_BUF);
+}
+
+/* Allocate codec buffers */
+int mfc_alloc_codec_buffers(struct mfc_core_ctx *core_ctx)
+{
+       struct mfc_core *core = core_ctx->core;
+       struct mfc_dev *dev = core->dev;
+       struct mfc_ctx *ctx = core_ctx->ctx;
+
+       mfc_debug_enter();
+
+       if (ctx->type == MFCINST_DECODER) {
+               __mfc_dec_calc_codec_buffer_size(core_ctx);
+       } else {
+               mfc_err("invalid type: %d\n", ctx->type);
+               return -EINVAL;
+       }
+
+       core_ctx->codec_buf.buftype = MFCBUF_NORMAL;
+       ctx->mv_buf.buftype = MFCBUF_NORMAL;
+
+       if (core_ctx->codec_buf.size > 0) {
+               snprintf(core_ctx->codec_buf.name,
+                        MFC_NUM_SPECIAL_BUF_NAME,
+                        "MFC%d ctx%d codec",
+                        core->id,
+                        core_ctx->num);
+               if (mfc_mem_special_buf_alloc(dev, &core_ctx->codec_buf)) {
+                       mfc_err("Allocating codec buffer failed\n");
+                       return -ENOMEM;
+               }
+               core_ctx->codec_buffer_allocated = 1;
+       } else if (ctx->codec_mode == MFC_REG_CODEC_MPEG2_DEC) {
+               core_ctx->codec_buffer_allocated = 1;
+       }
+
+       if (!ctx->mv_buffer_allocated && ctx->mv_buf.size > 0) {
+               snprintf(ctx->mv_buf.name,
+                        MFC_NUM_SPECIAL_BUF_NAME,
+                        "MFC%d ctx%d MV",
+                        core->id,
+                        ctx->num);
+               if (mfc_mem_special_buf_alloc(dev, &ctx->mv_buf)) {
+                       mfc_err("Allocating MV buffer failed\n");
+                       return -ENOMEM;
+               }
+               ctx->mv_buffer_allocated = 1;
+       }
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+/* Release buffers allocated for codec */
+void mfc_release_codec_buffers(struct mfc_core_ctx *core_ctx)
+{
+       struct mfc_ctx *ctx = core_ctx->ctx;
+
+       if (core_ctx->codec_buffer_allocated) {
+               mfc_mem_special_buf_free(ctx->dev, &core_ctx->codec_buf);
+               core_ctx->codec_buffer_allocated = 0;
+       }
+
+       if (ctx->mv_buffer_allocated) {
+               mfc_mem_special_buf_free(ctx->dev, &ctx->mv_buf);
+               ctx->mv_buffer_allocated = 0;
+       }
+
+       mfc_release_scratch_buffer(core_ctx);
+}
+
+int mfc_alloc_scratch_buffer(struct mfc_core_ctx *core_ctx)
+{
+       struct mfc_core *core = core_ctx->core;
+       struct mfc_dev *dev = core->dev;
+       struct mfc_ctx *ctx = core_ctx->ctx;
+
+       mfc_debug_enter();
+
+       if (core_ctx->scratch_buffer_allocated) {
+               mfc_mem_special_buf_free(dev, &core_ctx->scratch_buf);
+               core_ctx->scratch_buffer_allocated = 0;
+               mfc_debug(2, "[MEMINFO] Release the scratch buffer ctx[%d]\n", 
core_ctx->num);
+       }
+
+       core_ctx->scratch_buf.buftype = MFCBUF_NORMAL;
+
+       core_ctx->scratch_buf.size =  ALIGN(ctx->scratch_buf_size, SZ_256);
+       if (core_ctx->scratch_buf.size > 0) {
+               snprintf(core_ctx->scratch_buf.name,
+                        MFC_NUM_SPECIAL_BUF_NAME,
+                        "MFC%d ctx%d scratch",
+                        core->id,
+                        core_ctx->num);
+               if (mfc_mem_special_buf_alloc(dev, &core_ctx->scratch_buf)) {
+                       mfc_err("Allocating scratch_buf buffer failed\n");
+                       return -ENOMEM;
+               }
+               core_ctx->scratch_buffer_allocated = 1;
+       }
+
+       mfc_debug_leave();
+
+       return 0;
+}
+
+void mfc_release_scratch_buffer(struct mfc_core_ctx *core_ctx)
+{
+       struct mfc_ctx *ctx = core_ctx->ctx;
+
+       mfc_debug_enter();
+       if (core_ctx->scratch_buffer_allocated) {
+               mfc_mem_special_buf_free(ctx->dev, &core_ctx->scratch_buf);
+               core_ctx->scratch_buffer_allocated = 0;
+       }
+       mfc_debug_leave();
+}
+
 /* Allocation buffer of debug infor memory for FW debugging */
 int mfc_alloc_dbg_info_buffer(struct mfc_core *core)
 {
diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.h 
b/drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.h
index 8291e043b81a..6907cf6ac775 100644
--- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.h
+++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.h
@@ -18,6 +18,14 @@
 void mfc_release_common_context(struct mfc_core *core);
 int mfc_alloc_common_context(struct mfc_core *core);
 
+void mfc_release_instance_context(struct mfc_core_ctx *core_ctx);
+int mfc_alloc_instance_context(struct mfc_core_ctx *core_ctx);
+
+int mfc_alloc_codec_buffers(struct mfc_core_ctx *core_ctx);
+void mfc_release_codec_buffers(struct mfc_core_ctx *core_ctx);
+int mfc_alloc_scratch_buffer(struct mfc_core_ctx *core_ctx);
+void mfc_release_scratch_buffer(struct mfc_core_ctx *core_ctx);
+
 int mfc_alloc_firmware(struct mfc_core *core);
 int mfc_load_firmware(struct mfc_core *core,
                      struct mfc_special_buf *fw_buf,
diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_qos.c 
b/drivers/media/platform/samsung/exynos-mfc/base/mfc_qos.c
new file mode 100644
index 000000000000..f6548543f07c
--- /dev/null
+++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_qos.c
@@ -0,0 +1,965 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com/
+ *
+ * mfc_qos.c file
+ *
+ * Nagaraju Siddineni, <[email protected]>
+ * Himanshu Dewangan, <[email protected]>
+ */
+
+#include <linux/err.h>
+#ifdef CONFIG_MFC_USE_BUS_DEVFREQ
+#include <soc/samsung/freq-qos-tracer.h>
+#endif
+
+#include "mfc_qos.h"
+#include "mfc_utils.h"
+#include "mfc_queue.h"
+
+static inline int __mfc_core_get_qos_steps(struct mfc_core *core, int 
table_type)
+{
+       return core->core_pdata->num_default_qos_steps;
+}
+
+static inline struct mfc_qos *__mfc_core_get_qos_table(struct mfc_core *core, 
int table_type)
+{
+       return core->core_pdata->default_qos_table;
+}
+
+static inline unsigned long __mfc_qos_add_weight(struct mfc_ctx *ctx, unsigned 
long mb)
+{
+       struct mfc_dec *dec = ctx->dec_priv;
+       struct mfc_qos_weight *qos_weight = &ctx->dev->pdata->qos_weight;
+       u32 num_planes = ctx->dst_fmt->num_planes;
+       int weight = 1000;
+       unsigned long weighted_mb;
+
+       switch (ctx->codec_mode) {
+       case MFC_REG_CODEC_H264_DEC:
+               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);
+               }
+               break;
+       default:
+               mfc_ctx_err("[QoS] wrong codec_mode (%d), no weight\n", 
ctx->codec_mode);
+       }
+
+       if (dec) {
+               if (dec->num_of_tile_over_4) {
+                       weight = (weight * 100) / 
qos_weight->weight_num_of_tile;
+                       mfc_ctx_debug(3, "[QoS] num of tile >= 4, weight: 
%d\n", weight / 10);
+               }
+               if (dec->is_mbaff) {
+                       weight = (weight * 100) / qos_weight->weight_mbaff;
+                       mfc_ctx_debug(3, "[QoS] MBAFF, weight: %d\n", weight / 
10);
+               }
+       }
+
+       weighted_mb = (mb * weight) / 1000;
+       mfc_ctx_debug(3, "%s %d, %s %d, %s %d, 422format: %d (mb: %ld)\n",
+                     "[QoS] weight:", weight / 10,
+                     "codec:", ctx->codec_mode,
+                     "num planes:", num_planes,
+                     ctx->is_422,
+                     weighted_mb);
+
+       return weighted_mb;
+}
+
+void mfc_qos_get_weighted_mb(struct mfc_ctx *ctx, enum mfc_real_time rt)
+{
+       unsigned long mb;
+       unsigned int max_mb = 
ctx->dev->core[MFC_DEC_DEFAULT_CORE]->core_pdata->max_mb;
+
+       ctx->mb_width = WIDTH_MB(ctx->img_width);
+       ctx->mb_height = HEIGHT_MB(ctx->img_height);
+       mb = ctx->mb_width * ctx->mb_height * (mfc_rate_get_rt_framerate(ctx, 
rt) / 1000);
+
+       /* Instance individual load regardless of operating in the multi core */
+       ctx->weighted_mb = __mfc_qos_add_weight(ctx, mb);
+       ctx->load = ctx->weighted_mb * 100 / max_mb;
+
+       mfc_ctx_debug(3, "[QoS] weighted_mb: %ld(load: %u)\n",
+                     ctx->weighted_mb, ctx->load);
+}
+
+#ifdef CONFIG_MFC_USE_BUS_DEVFREQ
+enum {
+       MFC_QOS_ADD = 0,
+       MFC_QOS_UPDATE,
+       MFC_QOS_REMOVE,
+       MFC_QOS_BW,
+};
+
+enum {
+       MFC_PERF_BOOST_DVFS     = BIT(0),
+       MFC_PERF_BOOST_MO       = BIT(1),
+       MFC_PERF_BOOST_CPU      = BIT(2),
+};
+
+void __mfc_qos_cpu_boost_enable(struct mfc_core *core)
+{
+       struct mfc_core_platdata *pdata = core->core_pdata;
+       struct mfc_qos_boost *qos_boost_table = pdata->qos_boost_table;
+       struct cpufreq_policy *policy;
+       int i;
+
+       for (i = 0; i < qos_boost_table->num_cluster; i++) {
+               policy = cpufreq_cpu_get(qos_boost_table->num_cpu[i]);
+               if (policy) {
+                       freq_qos_tracer_add_request(&policy->constraints,
+                                                   &core->qos_req_cluster[i], 
FREQ_QOS_MIN,
+                                                   
qos_boost_table->freq_cluster[i]);
+                       mfc_core_debug(2, "[QoS][BOOST] CPU cluster[%d]: %d\n",
+                                      i, qos_boost_table->freq_cluster[i]);
+               }
+       }
+
+       core->cpu_boost_enable = 1;
+}
+
+void __mfc_qos_cpu_boost_disable(struct mfc_core *core)
+{
+       struct mfc_core_platdata *pdata = core->core_pdata;
+       struct mfc_qos_boost *qos_boost_table = pdata->qos_boost_table;
+       int i;
+
+       for (i = 0; i < qos_boost_table->num_cluster; i++) {
+               freq_qos_tracer_remove_request(&core->qos_req_cluster[i]);
+               mfc_core_debug(2, "[QoS][BOOST] CPU cluster[%d] off\n", i);
+       }
+
+       core->cpu_boost_enable = 0;
+}
+
+void mfc_qos_set_portion(struct mfc_core *core, struct mfc_ctx *ctx)
+{
+       int idx;
+
+       /*
+        * When only it is single instance,
+        * there is an exact meaning in the qos portion.
+        */
+       if (!ctx->mfc_qos_portion || core->num_inst > 1)
+               return;
+
+       idx = atomic_read(&core->qos_req_cur) - 1;
+       if (idx == -1)
+               return;
+
+       ctx->mfc_qos_portion[idx]++;
+}
+
+void mfc_qos_get_portion(struct mfc_core *core, struct mfc_ctx *ctx)
+{
+       struct mfc_qos *qos_table;
+       int num_qos_steps;
+       int i, sum = 0;
+       int table_type;
+
+       if (!ctx->mfc_qos_portion)
+               return;
+
+       table_type = MFC_QOS_TABLE_TYPE_DEFAULT;
+
+       num_qos_steps = __mfc_core_get_qos_steps(core, table_type);
+       qos_table = __mfc_core_get_qos_table(core, table_type);
+
+       for (i = 0; i < num_qos_steps; i++) {
+               sum += ctx->mfc_qos_portion[i];
+               mfc_ctx_debug(2, "[QoS][portion] lv%d: %d frame, %d%% (type: 
%d, mfc: %d, int: %d, mif: %d, mo: %s)\n",
+                             i, ctx->mfc_qos_portion[i],
+                             ctx->mfc_qos_portion[i] * 100 / ctx->frame_cnt,
+                             core->last_table_type,
+                             qos_table[i].freq_mfc, qos_table[i].freq_int,
+                             qos_table[i].freq_mif, qos_table[i].name);
+       }
+       mfc_ctx_debug(2, "[QoS][portion] total %d frame (recorded %d)\n",
+                     ctx->frame_cnt, sum);
+}
+
+bool mfc_qos_mb_calculate(struct mfc_core *core, struct mfc_core_ctx *core_ctx,
+                         unsigned int processing_cycle, unsigned int 
frame_type)
+{
+       struct mfc_ctx *ctx = core_ctx->ctx;
+       struct list_head *head = &core_ctx->mb_list;
+       struct mfc_mb_control *temp_mb;
+       struct mfc_mb_control *new_mb;
+       unsigned int avg_fps, need_fps, total_fps = 0;
+       unsigned long frame_time, drv_time, hw_mb, need_mb, avg_mb, margin_mb, 
total_mb = 0;
+       int table_type, num_qos_steps, cur_qos, count = 0, level_num;
+       bool update = false;
+
+       if (!core->dev->pdata->dynamic_weight ||
+           (core->dev->debugfs.feature_option & MFC_OPTION_USE_FIXED_WEIGHT))
+               return update;
+
+       if (ctx->frame_cnt < (MFC_MIN_FPS / 1000) ||
+           ctx->framerate > MFC_MAX_FPS ||
+           core->dev->num_inst > 1) {
+               core_ctx->dynamic_weight_level = 0;
+               core_ctx->dynamic_weight_started = 0;
+               return update;
+       }
+
+       if (frame_type == 7) {
+               mfc_debug(4, "[QoS] Empty decoding\n");
+               return update;
+       }
+
+       mutex_lock(&core->qos_mutex);
+
+       if (!core_ctx->dynamic_weight_started) {
+               mfc_debug(4, "[QoS] Clear MB list\n");
+
+               while (!list_empty(head)) {
+                       temp_mb = list_entry(head->next, struct mfc_mb_control, 
list);
+                       list_del(&temp_mb->list);
+               }
+
+               core_ctx->mb_index = 0;
+               core_ctx->mb_is_full = 0;
+               core_ctx->mb_not_coded_time = 0;
+               core_ctx->dynamic_weight_level = 0;
+               core_ctx->dynamic_weight_started = 1;
+               core_ctx->mb_update_time = MFC_MAX_MB_TABLE;
+       }
+
+       new_mb = &core_ctx->mb_table[core_ctx->mb_index];
+
+       /* setup macroblock table list */
+       if (core_ctx->mb_is_full && !core_ctx->mb_not_coded_time) {
+               temp_mb = list_entry(head->next, struct mfc_mb_control, list);
+               list_del(&temp_mb->list);
+       }
+
+       hw_mb = ((ctx->crop_width + 15) / 16) * ((ctx->crop_height + 15) / 16);
+
+       if (IS_MULTI_STREAM(ctx))
+               drv_time = MFC_2CORE_DRV_TIME;
+       else if (ctx->type == MFCINST_DECODER)
+               drv_time = MFC_DRV_TIME;
+       else
+               drv_time = 0;
+
+       frame_time = processing_cycle / (core->last_mfc_freq / 1000) + drv_time;
+       if (core_ctx->mb_not_coded_time) {
+               mfc_debug(4, "[QoS] Add not coded time. %lu + %lu\n",
+                         frame_time, core_ctx->mb_not_coded_time);
+               frame_time += core_ctx->mb_not_coded_time;
+               core_ctx->mb_not_coded_time = 0;
+       } else {
+               list_add_tail(&new_mb->list, head);
+       }
+
+       if (frame_type == 0) {
+               mfc_debug(4, "[QoS] Not coded frame type. it accumulated to 
next frame\n");
+               if (frame_time)
+                       core_ctx->mb_not_coded_time = frame_time;
+               else
+                       core_ctx->mb_not_coded_time = 1;
+               goto qos_end;
+       }
+
+       if (frame_time) {
+               new_mb->mb_per_sec = (USEC_PER_SEC * hw_mb) / frame_time;
+               new_mb->fps = 1000000 / frame_time;
+       } else {
+               new_mb->mb_per_sec = 0;
+               new_mb->fps = 0;
+       }
+
+       mfc_debug(4, "[QoS] hw_mb: %ld, cycle: %d, t: %ld, mb: %ld, fps: %d, 
freq: %d\n",
+                 hw_mb, processing_cycle, frame_time, new_mb->mb_per_sec,
+                 new_mb->fps, core->last_mfc_freq);
+
+       mfc_debug(4, "[QoS] -------------- mb_table (MFC: %dKHz)\n", 
core->last_mfc_freq);
+       list_for_each_entry(temp_mb, head, list) {
+               mfc_debug(4, "[QoS][%d] %lu MB/sec, %u fps\n",
+                         count, temp_mb->mb_per_sec, temp_mb->fps);
+               total_mb += temp_mb->mb_per_sec;
+               total_fps += temp_mb->fps;
+               count++;
+       }
+
+       if (count == 0) {
+               mfc_err("[QoS] There is no list for MB\n");
+               goto qos_end;
+       }
+
+       core_ctx->mb_index++;
+       if (core_ctx->mb_index == MFC_MAX_MB_TABLE) {
+               core_ctx->mb_is_full = 1;
+               core_ctx->mb_index = 0;
+       }
+
+       /* Skip additional updates until the changed QoS is reflected */
+       if (core_ctx->mb_update_time)
+               core_ctx->mb_update_time--;
+
+       /* Calculate macroblock average */
+       if (ctx->disp_ratio)
+               need_fps = ((ctx->framerate / 1000) * ctx->disp_ratio) / 100;
+       else
+               need_fps = ctx->framerate / 1000;
+       if (IS_TWO_MODE2(ctx))
+               need_fps = need_fps / core->dev->num_core;
+       need_mb = hw_mb * need_fps;
+       avg_mb = total_mb / count;
+       avg_fps = total_fps / count;
+       core_ctx->avg_runtime = avg_fps ? (USEC_PER_SEC / avg_fps) : 0;
+
+       mfc_debug(2, "[QoS] MB/sec op: %lu, need: %lu, cur: %lu, fps op: %lu, 
need: %u cur: %u\n",
+                 hw_mb * ctx->framerate / 1000, need_mb, avg_mb,
+                 ctx->framerate / 1000, need_fps, avg_fps);
+
+       /* Calculate dynamic macroblock weight, it can be minus value */
+       if (ctx->type == MFCINST_DECODER)
+               margin_mb = MFC_DEC_MB_PER_TABLE;
+       else
+               margin_mb = MFC_ENC_MB_PER_TABLE;
+
+       if (need_mb < margin_mb)
+               margin_mb = need_mb / 2;
+       else if ((need_mb > core->core_pdata->max_mb) ||
+                (IS_MULTI_STREAM(ctx) && IS_MFC_HEAVY_PERF(ctx, need_fps)))
+               margin_mb = margin_mb * core->dev->num_core;
+
+       if (ctx->type == MFCINST_DECODER)
+               table_type = MFC_QOS_TABLE_TYPE_DEFAULT;
+
+       num_qos_steps = __mfc_core_get_qos_steps(core, table_type);
+
+       if (atomic_read(&core->qos_req_cur)) {
+               cur_qos = atomic_read(&core->qos_req_cur);
+               if (ctx->type == MFCINST_DECODER &&
+                   new_mb->mb_per_sec < need_mb &&
+                   cur_qos < num_qos_steps) {
+                       level_num = ((need_mb - new_mb->mb_per_sec) + 
MFC_DEC_MB_PER_TABLE - 1) /
+                               MFC_DEC_MB_PER_TABLE;
+                       if (cur_qos + level_num <= num_qos_steps)
+                               core_ctx->dynamic_weight_level =
+                                       core_ctx->dynamic_weight_level + 
level_num;
+                       else
+                               core_ctx->dynamic_weight_level = num_qos_steps 
- cur_qos;
+                       mfc_debug(2, "[QoS] dec per frame perf is insufficient 
(weight level %d)\n",
+                                 core_ctx->dynamic_weight_level);
+                       update = true;
+               } else if ((avg_mb <= need_mb) &&
+                          (cur_qos < num_qos_steps) &&
+                          !core_ctx->mb_update_time) {
+                       core_ctx->dynamic_weight_level++;
+                       mfc_debug(2, "[QoS] avg perf is insufficient (weight 
level %d)\n",
+                                 core_ctx->dynamic_weight_level);
+                       update = true;
+               } else if ((avg_mb > need_mb + margin_mb) &&
+                          (cur_qos > 1) &&
+                          !core_ctx->mb_update_time) {
+                       core_ctx->dynamic_weight_level--;
+                       mfc_debug(2, "[QoS] perf is enough (weight level %d)\n",
+                                 core_ctx->dynamic_weight_level);
+                       update = true;
+               } else if (!core_ctx->mb_update_time) {
+                       mfc_debug(2, "[QoS] perf is suitable\n");
+               }
+       }
+
+       if (update) {
+               while (!list_empty(head)) {
+                       temp_mb = list_entry(head->next, struct mfc_mb_control, 
list);
+                       list_del(&temp_mb->list);
+               }
+
+               core_ctx->mb_index = 0;
+               core_ctx->mb_is_full = 0;
+               core_ctx->mb_update_time = MFC_MAX_MB_TABLE;
+
+               mfc_debug(2, "[QoS] dynamic weight level: %d\n", 
core_ctx->dynamic_weight_level);
+       }
+
+qos_end:
+       mutex_unlock(&core->qos_mutex);
+
+       return update;
+}
+
+static void __mfc_qos_operate(struct mfc_core *core, int opr_type, int 
table_type, int idx)
+{
+       struct mfc_core_platdata *pdata = core->core_pdata;
+       struct mfc_qos *qos_table;
+       int freq_mfc;
+
+       qos_table = __mfc_core_get_qos_table(core, table_type);
+       /* When removing QoS, do not update because the table_type is not 
accurate. */
+       if (opr_type != MFC_QOS_REMOVE)
+               core->last_table_type = table_type;
+
+       if (core->mfc_freq_by_bps > qos_table[idx].freq_mfc)
+               freq_mfc = core->mfc_freq_by_bps;
+       else
+               freq_mfc = qos_table[idx].freq_mfc;
+
+       switch (opr_type) {
+       case MFC_QOS_ADD:
+               core->last_mfc_freq = freq_mfc;
+               if (pdata->mfc_freq_control)
+                       exynos_pm_qos_add_request(&core->qos_req_mfc, 
pdata->pm_qos_id,
+                                                 freq_mfc);
+               exynos_pm_qos_add_request(&core->qos_req_int, 
PM_QOS_DEVICE_THROUGHPUT,
+                                         qos_table[idx].freq_int);
+               exynos_pm_qos_add_request(&core->qos_req_mif, 
PM_QOS_BUS_THROUGHPUT,
+                                         qos_table[idx].freq_mif);
+
+               atomic_set(&core->qos_req_cur, idx + 1);
+               mfc_core_debug(3, "[QoS] qos_req_cur: %d\n", 
atomic_read(&core->qos_req_cur) - 1);
+               MFC_TRACE_CORE("QoS add[%d] - mfc:%d(%s), int:%d, mif:%d\n",
+                              idx, freq_mfc, pdata->mfc_freq_control ? "used" 
: "un-used",
+                              qos_table[idx].freq_int, 
qos_table[idx].freq_mif);
+               mfc_core_debug(2, "[QoS] QoS add[%d] - mfc:%d(%s), int:%d, 
mif:%d\n",
+                              idx, freq_mfc, pdata->mfc_freq_control ? "used" 
: "un-used",
+                              qos_table[idx].freq_int, 
qos_table[idx].freq_mif);
+               break;
+       case MFC_QOS_UPDATE:
+               core->last_mfc_freq = freq_mfc;
+               if (pdata->mfc_freq_control)
+                       exynos_pm_qos_update_request(&core->qos_req_mfc, 
freq_mfc);
+               exynos_pm_qos_update_request(&core->qos_req_int, 
qos_table[idx].freq_int);
+               exynos_pm_qos_update_request(&core->qos_req_mif, 
qos_table[idx].freq_mif);
+
+               atomic_set(&core->qos_req_cur, idx + 1);
+               mfc_core_debug(3, "[QoS] qos_req_cur: %d\n", 
atomic_read(&core->qos_req_cur) - 1);
+               MFC_TRACE_CORE("QoS update[%d] - mfc:%d(%s), int:%d, mif:%d\n",
+                              idx, freq_mfc, pdata->mfc_freq_control ? "used" 
: "un-used",
+                              qos_table[idx].freq_int, 
qos_table[idx].freq_mif);
+               mfc_core_debug(2, "[QoS] QoS update[%d] - mfc:%d(%s), int:%d, 
mif:%d\n",
+                              idx, freq_mfc, pdata->mfc_freq_control ? "used" 
: "un-used",
+                              qos_table[idx].freq_int, 
qos_table[idx].freq_mif);
+               break;
+       case MFC_QOS_REMOVE:
+               core->last_mfc_freq = 0;
+               if (atomic_read(&core->qos_req_cur) == 0) {
+                       MFC_TRACE_CORE("QoS already removed\n");
+                       mfc_core_debug(2, "[QoS] QoS already removed\n");
+                       break;
+               }
+
+               mutex_lock(&core->pm_qos_mutex);
+               if (pdata->mfc_freq_control)
+                       exynos_pm_qos_remove_request(&core->qos_req_mfc);
+               exynos_pm_qos_remove_request(&core->qos_req_int);
+               exynos_pm_qos_remove_request(&core->qos_req_mif);
+
+               atomic_set(&core->qos_req_cur, 0);
+               mfc_core_debug(3, "[QoS] qos_req_cur: %d\n", 
atomic_read(&core->qos_req_cur) - 1);
+               MFC_TRACE_CORE("QoS remove\n");
+               mfc_core_debug(2, "[QoS] QoS remove\n");
+               mutex_unlock(&core->pm_qos_mutex);
+               break;
+       case MFC_QOS_BW:
+               break;
+       default:
+               mfc_core_err("[QoS] Unknown request for opr [%d]\n", opr_type);
+               break;
+       }
+}
+
+static void __mfc_qos_set(struct mfc_core *core, struct mfc_ctx *ctx,
+                         int table_type, int i)
+{
+       struct mfc_core_platdata *pdata = core->core_pdata;
+       struct mfc_qos *qos_table;
+       int num_qos_steps;
+       int freq_mfc;
+
+       num_qos_steps = __mfc_core_get_qos_steps(core, table_type);
+       qos_table = __mfc_core_get_qos_table(core, table_type);
+
+       mfc_ctx_debug(2, "[QoS] %s table[%d] covered mb %d ~ %d (mfc: %d, 
int:%d, mif:%d)\n",
+                     table_type ? "enc" : "default", i, 
qos_table[i].threshold_mb,
+                     i == num_qos_steps - 1 ? pdata->max_mb : qos_table[i + 
1].threshold_mb,
+                     qos_table[i].freq_mfc, qos_table[i].freq_int,
+                     qos_table[i].freq_mif);
+
+       mfc_core_debug(3, "[QoS] qos_req_cur: %d\n", 
atomic_read(&core->qos_req_cur) - 1);
+       if (atomic_read(&core->qos_req_cur) == 0) {
+               __mfc_qos_operate(core, MFC_QOS_ADD, table_type, i);
+       } else {
+               /*
+                * 1) QoS level is changed
+                * 2) MFC freq should be high regardless of QoS level
+                */
+               if (atomic_read(&core->qos_req_cur) != (i + 1)) {
+                       __mfc_qos_operate(core, MFC_QOS_UPDATE, table_type, i);
+               } else {
+                       if (core->mfc_freq_by_bps > qos_table[i].freq_mfc)
+                               freq_mfc = core->mfc_freq_by_bps;
+                       else
+                               freq_mfc = qos_table[i].freq_mfc;
+                       if (freq_mfc != core->last_mfc_freq) {
+                               mfc_ctx_debug(2, "[QoS] mfc freq changed (last: 
%d, by bps: %d, QoS table: %d)\n",
+                                             core->last_mfc_freq,
+                                             core->mfc_freq_by_bps,
+                                             qos_table[i].freq_mfc);
+                               __mfc_qos_operate(core, MFC_QOS_UPDATE, 
table_type, i);
+                       }
+               }
+       }
+}
+
+static inline unsigned long __mfc_qos_get_mb_per_second(struct mfc_core *core,
+                                                       struct mfc_core_ctx 
*core_ctx,
+                                                       unsigned int max_mb)
+{
+       struct mfc_dev *dev = core->dev;
+       struct mfc_ctx *ctx = core_ctx->ctx;
+       unsigned long mb_width, mb_height, fps, frame_mb, mb, qos_weighted_mb;
+
+       mb_width = (ctx->crop_width + 15) / 16;
+       mb_height = (ctx->crop_height + 15) / 16;
+       frame_mb = mb_width * mb_height;
+       if (IS_MULTI_MODE(ctx))
+               fps = ctx->framerate / 1000 / dev->num_core;
+       else
+               fps = ctx->framerate / 1000;
+
+       /* If decoder resolution is larger than HD and smaller than FHD, apply 
FHD for perf */
+       if (ctx->type == MFCINST_DECODER && frame_mb > MFC_HD_RES_MB &&
+           frame_mb < MFC_FHD_RES_MB) {
+               mfc_debug(3, "[QoS] frame MB size is changed %lu -> %d 
(%dx%d)\n",
+                         frame_mb, MFC_FHD_RES_MB,
+                         ctx->crop_width, ctx->crop_height);
+               frame_mb = MFC_FHD_RES_MB;
+       }
+
+       mb = frame_mb * fps;
+       qos_weighted_mb = __mfc_qos_add_weight(ctx, mb);
+
+       mfc_debug(3, "[QoS] ctx[%d:%s] %d x %d @ %ld fps (mb: %ld), %dkbps\n",
+                 ctx->num, ctx->type == MFCINST_ENCODER ? "ENC" : "DEC",
+                 ctx->crop_width, ctx->crop_height, fps, mb, ctx->kbps);
+
+       if (ctx->update_framerate) {
+               core_ctx->dynamic_weight_level = 0;
+               core_ctx->dynamic_weight_started = 0;
+               mfc_debug(4, "[QoS] clear dynamic weight, update_framerate: 
%d\n",
+                         ctx->update_framerate);
+       }
+
+       mfc_debug(4, "[QoS] weight (hw_mb: %lu)\n", qos_weighted_mb);
+       return qos_weighted_mb;
+}
+
+void __mfc_qos_calculate(struct mfc_core *core, struct mfc_ctx *ctx, int 
delete)
+{
+       struct mfc_core_platdata *pdata = core->core_pdata;
+       struct mfc_core_ctx *core_ctx = core->core_ctx[ctx->num];
+       struct mfc_qos *qos_table;
+       struct mfc_ctx *qos_ctx;
+       struct mfc_core_ctx *qos_core_ctx;
+       unsigned long hw_mb = 0, total_mb = 0, total_fps = 0;
+       int total_bps = 0, mfc_freq_idx;
+       unsigned int fw_time, sw_time;
+       int i, qos_level, found = 0, dec_found = 0, heif_found = 0;
+       int table_type = MFC_QOS_TABLE_TYPE_DEFAULT, num_qos_steps;
+
+       /* get the hw macroblock */
+       list_for_each_entry(qos_core_ctx, &core->qos_queue, qos_list) {
+               if (delete && qos_core_ctx == core->core_ctx[ctx->num]) {
+                       found = 1;
+                       continue;
+               }
+
+               qos_ctx = qos_core_ctx->ctx;
+               if (qos_ctx->idle_mode == MFC_IDLE_MODE_IDLE) {
+                       mfc_ctx_debug(3, "[QoS][MFCIDLE] skip idle ctx [%d]\n", 
qos_ctx->num);
+                       continue;
+               }
+               if (qos_ctx->is_heif_mode)
+                       heif_found += 1;
+
+               if (qos_ctx->type == MFCINST_DECODER)
+                       dec_found += 1;
+               hw_mb += __mfc_qos_get_mb_per_second(core, qos_core_ctx, 
pdata->max_mb);
+               total_fps += (qos_ctx->framerate / 1000);
+               total_bps += qos_ctx->kbps;
+       }
+
+       if (found)
+               list_del(&core->core_ctx[ctx->num]->qos_list);
+
+       if (dec_found)
+               table_type = MFC_QOS_TABLE_TYPE_DEFAULT;
+
+       num_qos_steps = __mfc_core_get_qos_steps(core, table_type);
+       qos_table = __mfc_core_get_qos_table(core, table_type);
+
+       /* search the suitable qos table */
+       for (i = num_qos_steps - 1; i >= 0; i--) {
+               fw_time = qos_table[i].time_fw;
+               sw_time = (MFC_DRV_TIME + fw_time);
+
+               if ((total_fps * sw_time) >= 1000000)
+                       total_mb = pdata->max_mb;
+               else
+                       total_mb = ((1000000 * hw_mb) / (1000000 - (total_fps * 
sw_time)));
+
+               mfc_ctx_debug(4, "%s %s %s[%d] %s %dus, %s %ld, %s %d, %s %ld, 
%s %ld\n",
+                             "[QoS]", table_type ? "enc" : "default",
+                             "table", i,
+                             "fw_time:", fw_time,
+                             "hw_mb:", hw_mb,
+                             "sw_time:", sw_time,
+                             "total_fps:", total_fps,
+                             "total_mb:", total_mb);
+
+               if (total_mb > qos_table[i].threshold_mb || total_mb == 0 || i 
== 0)
+                       break;
+       }
+
+       if (total_mb > pdata->max_mb)
+               mfc_ctx_debug(4, "[QoS] overspec mb %ld > %d\n", total_mb, 
pdata->max_mb);
+
+       if (dec_found) {
+               /* search the suitable independent mfc freq using bps */
+               mfc_freq_idx = mfc_rate_get_bps_section_by_bps
+                       (core->dev, total_bps, core->dev->max_kbps);
+               core->mfc_freq_by_bps = 
core->dev->pdata->mfc_freqs[mfc_freq_idx];
+       } else {
+               core->mfc_freq_by_bps = 0;
+       }
+
+       if (delete && (list_empty(&core->qos_queue) || total_mb == 0)) {
+               if (core->cpu_boost_enable)
+                       __mfc_qos_cpu_boost_disable(core);
+               __mfc_qos_operate(core, MFC_QOS_REMOVE, table_type, 0);
+       } else {
+               if (heif_found) {
+                       qos_level = num_qos_steps - 1;
+                       mfc_ctx_debug(2, "[QoS][BOOST] use max level for 
HEIF\n");
+                       if (!core->cpu_boost_enable)
+                               __mfc_qos_cpu_boost_enable(core);
+               } else {
+                       qos_level = i;
+                       if (core_ctx->dynamic_weight_level) {
+                               qos_level += core_ctx->dynamic_weight_level;
+                               if (qos_level >= num_qos_steps)
+                                       qos_level = num_qos_steps - 1;
+                               else if (qos_level < 0)
+                                       qos_level = 0;
+                               mfc_ctx_debug(2, "[QoS] add dynamic weight 
level %d. table[%d]\n",
+                                             core_ctx->dynamic_weight_level, 
qos_level);
+                       }
+                       if (core->cpu_boost_enable)
+                               __mfc_qos_cpu_boost_disable(core);
+               }
+               __mfc_qos_set(core, ctx, table_type, qos_level);
+       }
+}
+
+void mfc_qos_on(struct mfc_core *core, struct mfc_ctx *ctx)
+{
+       struct mfc_core_ctx *qos_core_ctx;
+       int found = 0;
+
+       if (core->core_ctx[ctx->num] && core->core_ctx[ctx->num]->state == 
MFCINST_FREE) {
+               mfc_ctx_info("[QoS] instance not started yet\n");
+               return;
+       }
+
+       mutex_lock(&core->qos_mutex);
+       list_for_each_entry(qos_core_ctx, &core->qos_queue, qos_list)
+               if (qos_core_ctx == core->core_ctx[ctx->num])
+                       found = 1;
+
+       if (!found)
+               list_add_tail(&core->core_ctx[ctx->num]->qos_list,
+                             &core->qos_queue);
+
+       __mfc_qos_calculate(core, ctx, MFC_QOS_ADD);
+
+       mutex_unlock(&core->qos_mutex);
+}
+
+void mfc_qos_off(struct mfc_core *core, struct mfc_ctx *ctx)
+{
+       int table_type = MFC_QOS_TABLE_TYPE_DEFAULT;
+
+       mutex_lock(&core->qos_mutex);
+
+       if (list_empty(&core->qos_queue)) {
+               if (atomic_read(&core->qos_req_cur) != 0) {
+                       mfc_ctx_err("[QoS] MFC request count is wrong!\n");
+                       if (core->cpu_boost_enable)
+                               __mfc_qos_cpu_boost_disable(core);
+                       __mfc_qos_operate(core, MFC_QOS_REMOVE, table_type, 0);
+               }
+               goto out;
+       }
+
+       if (ON_RES_CHANGE(core->core_ctx[ctx->num]))
+               goto out;
+
+       __mfc_qos_calculate(core, ctx, MFC_QOS_REMOVE);
+
+out:
+       mutex_unlock(&core->qos_mutex);
+}
+
+void mfc_qos_update(struct mfc_core *core, int on)
+{
+       struct mfc_platdata *pdata = core->dev->pdata;
+       struct mfc_platdata *dev_pdata = core->dev->pdata;
+       unsigned int mfc_freq;
+       int qos, i;
+
+       if (core->dev->debugfs.feature_option & MFC_OPTION_DYNAMIC_QOS_DISABLE)
+               return;
+
+       mfc_core_debug_enter();
+
+       mutex_lock(&core->qos_mutex);
+
+       if ((atomic_read(&core->qos_req_cur) <= 1) ||
+           (atomic_read(&core->qos_req_cur) > (dev_pdata->qos_ctrl_level + 1)) 
||
+           core->last_table_type != MFC_QOS_TABLE_TYPE_DEFAULT) {
+               mutex_unlock(&core->qos_mutex);
+               return;
+       }
+
+       if (on) {
+               qos = atomic_read(&core->qos_req_cur) - 1;
+               mfc_freq = core->last_mfc_freq;
+               mfc_core_debug(3, "[QoS] ON: QoS update[%d], mfc freq %d\n",
+                              qos, mfc_freq);
+       } else {
+               qos = 0;
+               mfc_freq = pdata->mfc_freqs[0];
+               mfc_core_debug(3, "[QoS] OFF: QoS update[%d], mfc freq %d\n",
+                              qos, mfc_freq);
+       }
+
+       i = core->qos_ctrl_last_idx;
+       core->qos_ctrl[i].idx = qos;
+       core->qos_ctrl[i].table_type = core->last_table_type;
+       core->qos_ctrl[i].mfc_freq = mfc_freq;
+       core->qos_ctrl_last_idx++;
+       if (core->qos_ctrl_last_idx >= MAX_NUM_QOS_DYNAMIC)
+               core->qos_ctrl_last_idx = 0;
+
+       mutex_unlock(&core->qos_mutex);
+
+       queue_work(core->qos_ctrl_wq, &core->qos_ctrl_work);
+
+       mfc_core_debug_leave();
+}
+
+void mfc_qos_ctrl_worker(struct work_struct *work)
+{
+       struct mfc_core *core;
+       struct mfc_core_platdata *pdata;
+       struct mfc_platdata *dev_pdata;
+       struct mfc_qos *qos_table;
+       int idx, i;
+
+       core = container_of(work, struct mfc_core, qos_ctrl_work);
+       pdata = core->core_pdata;
+       dev_pdata = core->dev->pdata;
+
+       mutex_lock(&core->qos_mutex);
+
+       if (!core->qos_ctrl_last_idx) {
+               mutex_unlock(&core->qos_mutex);
+               return;
+       }
+
+       i = core->qos_ctrl_last_idx;
+       do {
+               core->qos_ctrl_last_idx = 0;
+
+               if ((atomic_read(&core->qos_req_cur) > 
(dev_pdata->qos_ctrl_level + 1)) ||
+                   atomic_read(&core->qos_req_cur) <= 1) {
+                       mutex_unlock(&core->qos_mutex);
+                       return;
+               }
+
+               i--;
+               idx = core->qos_ctrl[i].idx;
+               qos_table = __mfc_core_get_qos_table(core, 
core->qos_ctrl[i].table_type);
+
+               mutex_unlock(&core->qos_mutex);
+
+               /* use pm_qos_mutex to reduce pm_qos_update latency */
+               mutex_lock(&core->pm_qos_mutex);
+               if (atomic_read(&core->qos_req_cur) == 0) {
+                       mutex_unlock(&core->pm_qos_mutex);
+                       return;
+               }
+
+               if (pdata->mfc_freq_control)
+                       exynos_pm_qos_update_request
+                               (&core->qos_req_mfc, 
core->qos_ctrl[i].mfc_freq);
+
+               exynos_pm_qos_update_request(&core->qos_req_int, 
qos_table[idx].freq_int);
+               exynos_pm_qos_update_request(&core->qos_req_mif, 
qos_table[idx].freq_mif);
+               mfc_core_debug(3, "[QoS] WORKER: QoS update[%d], mfc freq %d\n",
+                              idx, core->qos_ctrl[i].mfc_freq);
+
+               mutex_unlock(&core->pm_qos_mutex);
+
+               mutex_lock(&core->qos_mutex);
+               i = core->qos_ctrl_last_idx;
+       } while (i);
+
+       mutex_unlock(&core->qos_mutex);
+}
+
+void __mfc_qos_on_idle(struct mfc_core *core)
+{
+       struct mfc_ctx *ctx;
+       int i;
+
+       mutex_lock(&core->dev->mfc_migrate_mutex);
+       if (!core->num_inst) {
+               mutex_unlock(&core->dev->mfc_migrate_mutex);
+               return;
+       }
+
+       for (i = 0; i < MFC_NUM_CONTEXTS; i++) {
+               if (core->core_ctx[i]) {
+                       ctx = core->core_ctx[i]->ctx;
+                       mfc_qos_on(core, ctx);
+                       break;
+               }
+       }
+       mutex_unlock(&core->dev->mfc_migrate_mutex);
+}
+
+void __mfc_qos_off_all(struct mfc_core *core)
+{
+       struct mfc_core_ctx *qos_core_ctx, *tmp_core_ctx;
+
+       mutex_lock(&core->qos_mutex);
+       if (list_empty(&core->qos_queue)) {
+               mfc_core_err("[QoS][MFCIDLE] MFC QoS list already empty (%d)\n",
+                            atomic_read(&core->qos_req_cur));
+               mutex_unlock(&core->qos_mutex);
+               return;
+       }
+
+       /* Delete all of QoS list */
+       list_for_each_entry_safe(qos_core_ctx, tmp_core_ctx, &core->qos_queue, 
qos_list)
+               list_del(&qos_core_ctx->qos_list);
+
+       /* Select the opend ctx structure for QoS remove */
+       if (core->cpu_boost_enable)
+               __mfc_qos_cpu_boost_disable(core);
+       __mfc_qos_operate(core, MFC_QOS_REMOVE, MFC_QOS_TABLE_TYPE_DEFAULT, 0);
+       mutex_unlock(&core->qos_mutex);
+}
+#else
+bool mfc_qos_mb_calculate(struct mfc_core *core, struct mfc_core_ctx *core_ctx,
+                         unsigned int processing_cycle, unsigned int 
frame_type)
+{
+       return false;
+}
+#endif
+
+void mfc_qos_idle_worker(struct work_struct *work)
+{
+       struct mfc_core *core;
+       struct mfc_core_ctx *core_ctx;
+       struct mfc_ctx *ctx;
+       int is_idle = 0, qos_num_inst = 0;
+
+       core = container_of(work, struct mfc_core, mfc_idle_work);
+
+       mutex_lock(&core->dev->mfc_mutex);
+
+       mutex_lock(&core->idle_qos_mutex);
+
+       /* Check idle mode for all context */
+       mutex_lock(&core->qos_mutex);
+       list_for_each_entry(core_ctx, &core->qos_queue, qos_list) {
+               ctx = core_ctx->ctx;
+               qos_num_inst++;
+               if (((atomic_read(&core->hw_run_bits) & BIT(ctx->num)) == 0) &&
+                   ((atomic_read(&core->dev->queued_bits) & BIT(ctx->num)) == 
0)) {
+                       mfc_ctx_change_idle_mode(ctx, MFC_IDLE_MODE_IDLE);
+                       mfc_debug(3, "[MFCIDLE] ctx[%d] is idle (hw %#x Q 
%#x)\n", ctx->num,
+                                 atomic_read(&core->hw_run_bits),
+                                 atomic_read(&core->dev->queued_bits));
+                       is_idle = 1;
+               } else {
+                       mfc_ctx_change_idle_mode(ctx, MFC_IDLE_MODE_NONE);
+               }
+       }
+       mutex_unlock(&core->qos_mutex);
+
+       if (core->idle_mode == MFC_IDLE_MODE_CANCEL) {
+               mfc_core_change_idle_mode(core, MFC_IDLE_MODE_NONE);
+               mfc_core_debug(2, "[QoS][MFCIDLE] idle mode is canceled\n");
+               goto ctx_idle;
+       } else if (core->idle_mode == MFC_IDLE_MODE_NONE) {
+               mfc_core_idle_checker_start_tick(core);
+               goto ctx_idle;
+       }
+
+#ifdef CONFIG_MFC_USE_BUS_DEVFREQ
+       __mfc_qos_off_all(core);
+#endif
+       if (qos_num_inst == 1) {
+               mfc_core_info("[QoS][MFCIDLE] go to idle mode (src %d(ready 
%d), dst %d, framecnt %d)\n",
+                             mfc_get_queue_count(&ctx->buf_queue_lock, 
&core_ctx->src_buf_queue),
+                             mfc_get_queue_count(&ctx->buf_queue_lock, 
&ctx->src_buf_ready_queue),
+                             mfc_get_queue_count(&ctx->buf_queue_lock, 
&ctx->dst_buf_queue),
+                             ctx->frame_cnt);
+       } else {
+               mfc_core_info("[QoS][MFCIDLE] go to idle mode\n");
+       }
+
+       mfc_core_change_idle_mode(core, MFC_IDLE_MODE_IDLE);
+       mutex_unlock(&core->idle_qos_mutex);
+       mutex_unlock(&core->dev->mfc_mutex);
+       return;
+
+ctx_idle:
+       if (is_idle) {
+               mfc_core_debug(2, "[QoS][MFCIDLE] idle mode is for ctx\n");
+#ifdef CONFIG_MFC_USE_BUS_DEVFREQ
+               __mfc_qos_on_idle(core);
+#endif
+       }
+
+       mutex_unlock(&core->idle_qos_mutex);
+       mutex_unlock(&core->dev->mfc_mutex);
+}
+
+bool mfc_qos_idle_trigger(struct mfc_core *core, struct mfc_ctx *ctx)
+{
+       bool update_idle = false;
+
+       mutex_lock(&core->idle_qos_mutex);
+       if (core->idle_mode == MFC_IDLE_MODE_IDLE) {
+               mfc_ctx_debug(2, "[QoS][MFCIDLE] restart QoS control\n");
+               mfc_core_change_idle_mode(core, MFC_IDLE_MODE_NONE);
+               update_idle = true;
+       } else if (core->idle_mode == MFC_IDLE_MODE_RUNNING) {
+               mfc_ctx_debug(2, "[QoS][MFCIDLE] restart QoS control, cancel 
idle\n");
+               mfc_core_change_idle_mode(core, MFC_IDLE_MODE_CANCEL);
+               update_idle = true;
+       }
+
+       if (ctx->idle_mode == MFC_IDLE_MODE_IDLE) {
+               mfc_ctx_debug(2, "[QoS][MFCIDLE] restart QoS control for 
ctx\n");
+               mfc_ctx_change_idle_mode(ctx, MFC_IDLE_MODE_NONE);
+               update_idle = true;
+       }
+       mutex_unlock(&core->idle_qos_mutex);
+
+       return update_idle;
+}
diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_qos.h 
b/drivers/media/platform/samsung/exynos-mfc/base/mfc_qos.h
new file mode 100644
index 000000000000..d9c0e2828bfa
--- /dev/null
+++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_qos.h
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
+ *              http://www.samsung.com
+ *
+ * mfc_qos.h file
+ *
+ * Nagaraju Siddineni, <[email protected]>
+ * Himanshu Dewangan, <[email protected]>
+ */
+
+#ifndef __MFC_QOS_H
+#define __MFC_QOS_H __FILE__
+
+#include "mfc_common.h"
+
+#define MB_COUNT_PER_UHD_FRAME         32400
+#define MAX_FPS_PER_UHD_FRAME          120
+#define MIN_BW_PER_SEC                 1
+
+#define MFC_2CORE_DRV_TIME                     1500
+#define MFC_DRV_TIME                   500
+#define MFC_DEC_MB_PER_TABLE           650000
+#define MFC_ENC_MB_PER_TABLE           400000
+
+/* It is the same value with ID_DEFAULT of exynos-bts.c */
+#define BTS_DEFAULT_SCEN_IDX   0
+
+enum {
+       MFC_QOS_TABLE_TYPE_DEFAULT      = 0,
+       MFC_QOS_TABLE_TYPE_ENCODER      = 1,
+};
+
+#ifdef CONFIG_MFC_USE_BUS_DEVFREQ
+void mfc_qos_on(struct mfc_core *core, struct mfc_ctx *ctx);
+void mfc_qos_off(struct mfc_core *core, struct mfc_ctx *ctx);
+void mfc_qos_update(struct mfc_core *core, int on);
+void mfc_qos_ctrl_worker(struct work_struct *work);
+void mfc_qos_set_portion(struct mfc_core *core, struct mfc_ctx *ctx);
+void mfc_qos_get_portion(struct mfc_core *core, struct mfc_ctx *ctx);
+#else
+
+#define mfc_qos_on(core, ctx)  ({              \
+       (void)core; /* Not used */              \
+       (void)ctx; /* Not used */               \
+       do {} while (0);                        \
+})
+
+#define mfc_qos_off(core, ctx) ({              \
+       (void)core; /* Not used */              \
+       (void)ctx; /* Not used */               \
+       do {} while (0);                        \
+})
+
+#define mfc_qos_update(core, on)  ({           \
+       (void)core; /* Not used */              \
+       (void)on; /* Not used */                \
+       do {} while (0);                        \
+})
+
+#define mfc_qos_ctrl_worker(work)  ({          \
+       (void)work; /* Not used */              \
+       do {} while (0);                        \
+})
+
+#define mfc_qos_set_portion(core, ctx)  ({      \
+       (void)core; /* Not used */              \
+       (void)ctx; /* Not used */               \
+       do {} while (0);                        \
+})
+
+#define mfc_qos_get_portion(core, ctx) ({      \
+       (void)core; /* Not used */              \
+       (void)ctx; /* Not used */               \
+       do {} while (0);                        \
+})
+#endif
+
+bool mfc_qos_mb_calculate(struct mfc_core *core,
+                         struct mfc_core_ctx *core_ctx,
+                         unsigned int processing_cycle,
+                         unsigned int frame_type);
+
+void mfc_qos_idle_worker(struct work_struct *work);
+bool mfc_qos_idle_trigger(struct mfc_core *core, struct mfc_ctx *ctx);
+void mfc_qos_get_weighted_mb(struct mfc_ctx *ctx, enum mfc_real_time rt);
+
+static inline void mfc_qos_get_disp_ratio(struct mfc_ctx *ctx, int dec_cnt, 
int disp_cnt)
+{
+       int delta;
+
+       delta = dec_cnt - disp_cnt;
+       ctx->disp_ratio = ((dec_cnt + delta) * 100) / disp_cnt;
+       if (ctx->disp_ratio > 100)
+               mfc_ctx_debug(2, "[QoS] need to more performance dec %d/disp 
%d, disp_ratio: x%d.%d\n",
+                             dec_cnt, disp_cnt,
+                             ctx->disp_ratio / 100, ctx->disp_ratio % 100);
+}
+#endif /* __MFC_QOS_H */
-- 
2.34.1

Reply via email to