[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250930040348.3702923-13-h.dewangan@samsung.com>
Date: Tue, 30 Sep 2025 09:33:31 +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 12/29] media: mfc: Introduce QoS support and instance
context handling
From: Nagaraju Siddineni <nagaraju.s@...sung.com>
- 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 <nagaraju.s@...sung.com>
Signed-off-by: Himanshu Dewangan <h.dewangan@...sung.com>
---
.../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, <nagaraju.s@...sung.com>
+ * Himanshu Dewangan, <h.dewangan@...sung.com>
+ */
+
+#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, <nagaraju.s@...sung.com>
+ * Himanshu Dewangan, <h.dewangan@...sung.com>
+ */
+
+#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
Powered by blists - more mailing lists