2021-04-12 18:21:40

by Dikshita Agarwal

[permalink] [raw]
Subject: [PATCH] media: venus: Enable low power setting for encoder

Set the FW to run in low power for encoder
to accommodate more session without losing much on quality.

Signed-off-by: Dikshita Agarwal <[email protected]>
---
drivers/media/platform/qcom/venus/core.h | 6 ++
drivers/media/platform/qcom/venus/helpers.c | 2 +
drivers/media/platform/qcom/venus/hfi_helper.h | 10 +-
drivers/media/platform/qcom/venus/hfi_platform.c | 16 +++
drivers/media/platform/qcom/venus/hfi_platform.h | 4 +
.../media/platform/qcom/venus/hfi_platform_v4.c | 28 ++++--
.../media/platform/qcom/venus/hfi_platform_v6.c | 28 ++++--
drivers/media/platform/qcom/venus/pm_helpers.c | 108 ++++++++++++++++++---
8 files changed, 167 insertions(+), 35 deletions(-)

diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h
index 9451e54..9b5031f 100644
--- a/drivers/media/platform/qcom/venus/core.h
+++ b/drivers/media/platform/qcom/venus/core.h
@@ -257,6 +257,7 @@ struct clock_data {
unsigned long freq;
unsigned long vpp_freq;
unsigned long vsp_freq;
+ unsigned long low_power_freq;
};

#define to_venus_buffer(ptr) container_of(ptr, struct venus_buffer, vb)
@@ -280,6 +281,10 @@ struct venus_ts_metadata {
struct v4l2_timecode tc;
};

+enum venus_inst_modes {
+ VENUS_LOW_POWER = BIT(0),
+};
+
/**
* struct venus_inst - holds per instance parameters
*
@@ -400,6 +405,7 @@ struct venus_inst {
unsigned int pic_struct;
bool next_buf_last;
bool drain_active;
+ enum venus_inst_modes flags;
};

#define IS_V1(core) ((core)->res->hfi_version == HFI_VERSION_1XX)
diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c
index 76ece2f..c6b6a30 100644
--- a/drivers/media/platform/qcom/venus/helpers.c
+++ b/drivers/media/platform/qcom/venus/helpers.c
@@ -1566,6 +1566,8 @@ int venus_helper_session_init(struct venus_inst *inst)
session_type);
inst->clk_data.vsp_freq = hfi_platform_get_codec_vsp_freq(version, codec,
session_type);
+ inst->clk_data.low_power_freq = hfi_platform_get_codec_lp_freq(version, codec,
+ session_type);

return 0;
}
diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h
index 6b524c7..5621cdb 100644
--- a/drivers/media/platform/qcom/venus/hfi_helper.h
+++ b/drivers/media/platform/qcom/venus/hfi_helper.h
@@ -412,9 +412,6 @@
#define HFI_BUFFER_MODE_RING 0x1000002
#define HFI_BUFFER_MODE_DYNAMIC 0x1000003

-#define HFI_VENC_PERFMODE_MAX_QUALITY 0x1
-#define HFI_VENC_PERFMODE_POWER_SAVE 0x2
-
/*
* HFI_PROPERTY_SYS_COMMON_START
* HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + 0x0000
@@ -815,6 +812,13 @@ struct hfi_framesize {
u32 height;
};

+#define HFI_VENC_PERFMODE_MAX_QUALITY 0x1
+#define HFI_VENC_PERFMODE_POWER_SAVE 0x2
+
+struct hfi_perf_mode {
+ u32 video_perf_mode;
+};
+
#define VIDC_CORE_ID_DEFAULT 0
#define VIDC_CORE_ID_1 1
#define VIDC_CORE_ID_2 2
diff --git a/drivers/media/platform/qcom/venus/hfi_platform.c b/drivers/media/platform/qcom/venus/hfi_platform.c
index 8f47804..f5b4e1f 100644
--- a/drivers/media/platform/qcom/venus/hfi_platform.c
+++ b/drivers/media/platform/qcom/venus/hfi_platform.c
@@ -50,6 +50,22 @@ hfi_platform_get_codec_vsp_freq(enum hfi_version version, u32 codec, u32 session
return freq;
}

+unsigned long
+hfi_platform_get_codec_lp_freq(enum hfi_version version, u32 codec, u32 session_type)
+{
+ const struct hfi_platform *plat;
+ unsigned long freq = 0;
+
+ plat = hfi_platform_get(version);
+ if (!plat)
+ return 0;
+
+ if (plat->codec_lp_freq)
+ freq = plat->codec_lp_freq(session_type, codec);
+
+ return freq;
+}
+
u8 hfi_platform_num_vpp_pipes(enum hfi_version version)
{
const struct hfi_platform *plat;
diff --git a/drivers/media/platform/qcom/venus/hfi_platform.h b/drivers/media/platform/qcom/venus/hfi_platform.h
index 3819bb2..2dbe608 100644
--- a/drivers/media/platform/qcom/venus/hfi_platform.h
+++ b/drivers/media/platform/qcom/venus/hfi_platform.h
@@ -43,11 +43,13 @@ struct hfi_platform_codec_freq_data {
u32 session_type;
unsigned long vpp_freq;
unsigned long vsp_freq;
+ unsigned long low_power_freq;
};

struct hfi_platform {
unsigned long (*codec_vpp_freq)(u32 session_type, u32 codec);
unsigned long (*codec_vsp_freq)(u32 session_type, u32 codec);
+ unsigned long (*codec_lp_freq)(u32 session_type, u32 codec);
void (*codecs)(u32 *enc_codecs, u32 *dec_codecs, u32 *count);
const struct hfi_plat_caps *(*capabilities)(unsigned int *entries);
u8 (*num_vpp_pipes)(void);
@@ -63,5 +65,7 @@ unsigned long hfi_platform_get_codec_vpp_freq(enum hfi_version version, u32 code
u32 session_type);
unsigned long hfi_platform_get_codec_vsp_freq(enum hfi_version version, u32 codec,
u32 session_type);
+unsigned long hfi_platform_get_codec_lp_freq(enum hfi_version version, u32 codec,
+ u32 session_type);
u8 hfi_platform_num_vpp_pipes(enum hfi_version version);
#endif
diff --git a/drivers/media/platform/qcom/venus/hfi_platform_v4.c b/drivers/media/platform/qcom/venus/hfi_platform_v4.c
index 3848bb6..3f7f527 100644
--- a/drivers/media/platform/qcom/venus/hfi_platform_v4.c
+++ b/drivers/media/platform/qcom/venus/hfi_platform_v4.c
@@ -262,14 +262,14 @@ static void get_codecs(u32 *enc_codecs, u32 *dec_codecs, u32 *count)
}

static const struct hfi_platform_codec_freq_data codec_freq_data[] = {
- { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_ENC, 675, 10 },
- { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_ENC, 675, 10 },
- { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_ENC, 675, 10 },
- { V4L2_PIX_FMT_MPEG2, VIDC_SESSION_TYPE_DEC, 200, 10 },
- { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_DEC, 200, 10 },
- { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_DEC, 200, 10 },
- { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_DEC, 200, 10 },
- { V4L2_PIX_FMT_VP9, VIDC_SESSION_TYPE_DEC, 200, 10 },
+ { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_ENC, 675, 10, 320 },
+ { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_ENC, 675, 10, 320 },
+ { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_ENC, 675, 10, 320 },
+ { V4L2_PIX_FMT_MPEG2, VIDC_SESSION_TYPE_DEC, 200, 10, 200 },
+ { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_DEC, 200, 10, 200 },
+ { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_DEC, 200, 10, 200 },
+ { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_DEC, 200, 10, 200 },
+ { V4L2_PIX_FMT_VP9, VIDC_SESSION_TYPE_DEC, 200, 10, 200 },
};

static const struct hfi_platform_codec_freq_data *
@@ -311,9 +311,21 @@ static unsigned long codec_vsp_freq(u32 session_type, u32 codec)
return 0;
}

+static unsigned long codec_lp_freq(u32 session_type, u32 codec)
+{
+ const struct hfi_platform_codec_freq_data *data;
+
+ data = get_codec_freq_data(session_type, codec);
+ if (data)
+ return data->low_power_freq;
+
+ return 0;
+}
+
const struct hfi_platform hfi_plat_v4 = {
.codec_vpp_freq = codec_vpp_freq,
.codec_vsp_freq = codec_vsp_freq,
+ .codec_lp_freq = codec_lp_freq,
.codecs = get_codecs,
.capabilities = get_capabilities,
};
diff --git a/drivers/media/platform/qcom/venus/hfi_platform_v6.c b/drivers/media/platform/qcom/venus/hfi_platform_v6.c
index 2278be1..15d0dc8 100644
--- a/drivers/media/platform/qcom/venus/hfi_platform_v6.c
+++ b/drivers/media/platform/qcom/venus/hfi_platform_v6.c
@@ -262,14 +262,14 @@ static void get_codecs(u32 *enc_codecs, u32 *dec_codecs, u32 *count)
}

static const struct hfi_platform_codec_freq_data codec_freq_data[] = {
- { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_ENC, 675, 25 },
- { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_ENC, 675, 25 },
- { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_ENC, 675, 60 },
- { V4L2_PIX_FMT_MPEG2, VIDC_SESSION_TYPE_DEC, 200, 25 },
- { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_DEC, 200, 25 },
- { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_DEC, 200, 25 },
- { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_DEC, 200, 60 },
- { V4L2_PIX_FMT_VP9, VIDC_SESSION_TYPE_DEC, 200, 60 },
+ { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_ENC, 675, 25, 320 },
+ { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_ENC, 675, 25, 320 },
+ { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_ENC, 675, 60, 320 },
+ { V4L2_PIX_FMT_MPEG2, VIDC_SESSION_TYPE_DEC, 200, 25, 200 },
+ { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_DEC, 200, 25, 200 },
+ { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_DEC, 200, 25, 200 },
+ { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_DEC, 200, 60, 200 },
+ { V4L2_PIX_FMT_VP9, VIDC_SESSION_TYPE_DEC, 200, 60, 200 },
};

static const struct hfi_platform_codec_freq_data *
@@ -311,6 +311,17 @@ static unsigned long codec_vsp_freq(u32 session_type, u32 codec)
return 0;
}

+static unsigned long codec_lp_freq(u32 session_type, u32 codec)
+{
+ const struct hfi_platform_codec_freq_data *data;
+
+ data = get_codec_freq_data(session_type, codec);
+ if (data)
+ return data->low_power_freq;
+
+ return 0;
+}
+
static u8 num_vpp_pipes(void)
{
return 4;
@@ -319,6 +330,7 @@ static u8 num_vpp_pipes(void)
const struct hfi_platform hfi_plat_v6 = {
.codec_vpp_freq = codec_vpp_freq,
.codec_vsp_freq = codec_vsp_freq,
+ .codec_lp_freq = codec_lp_freq,
.codecs = get_codecs,
.capabilities = get_capabilities,
.num_vpp_pipes = num_vpp_pipes,
diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c
index 43c4e3d..a3f3e31 100644
--- a/drivers/media/platform/qcom/venus/pm_helpers.c
+++ b/drivers/media/platform/qcom/venus/pm_helpers.c
@@ -492,8 +492,50 @@ static int poweron_coreid(struct venus_core *core, unsigned int coreid_mask)
return 0;
}

+static inline int power_save_mode_enable(struct venus_inst *inst,
+ bool enable)
+{
+ struct venc_controls *enc_ctr = &inst->controls.enc;
+ const u32 ptype = HFI_PROPERTY_CONFIG_VENC_PERF_MODE;
+ u32 venc_mode;
+ int ret = 0;
+
+ if (inst->session_type != VIDC_SESSION_TYPE_ENC)
+ return 0;
+
+ if (enc_ctr->bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CQ)
+ enable = false;
+
+ venc_mode = enable ? HFI_VENC_PERFMODE_POWER_SAVE :
+ HFI_VENC_PERFMODE_MAX_QUALITY;
+
+ ret = hfi_session_set_property(inst, ptype, &venc_mode);
+ if (ret)
+ return ret;
+
+ inst->flags = enable ? inst->flags | VENUS_LOW_POWER :
+ inst->flags & ~VENUS_LOW_POWER;
+
+ return ret;
+}
+
+static int move_core_to_power_save_mode(struct venus_core *core,
+ u32 core_id)
+{
+ struct venus_inst *inst = NULL;
+
+ mutex_lock(&core->lock);
+ list_for_each_entry(inst, &core->instances, list) {
+ if (inst->clk_data.core_id == core_id &&
+ inst->session_type == VIDC_SESSION_TYPE_ENC)
+ power_save_mode_enable(inst, true);
+ }
+ mutex_unlock(&core->lock);
+ return 0;
+}
+
static void
-min_loaded_core(struct venus_inst *inst, u32 *min_coreid, u32 *min_load)
+min_loaded_core(struct venus_inst *inst, u32 *min_coreid, u32 *min_load, bool low_power)
{
u32 mbs_per_sec, load, core1_load = 0, core2_load = 0;
u32 cores_max = core_num_max(inst);
@@ -511,7 +553,14 @@ min_loaded_core(struct venus_inst *inst, u32 *min_coreid, u32 *min_load)
if (inst_pos->state != INST_START)
continue;

- vpp_freq = inst_pos->clk_data.vpp_freq;
+ if (inst->session_type == VIDC_SESSION_TYPE_DEC)
+ vpp_freq = inst_pos->clk_data.vpp_freq;
+ else if (inst->session_type == VIDC_SESSION_TYPE_ENC)
+ vpp_freq = low_power ? inst_pos->clk_data.vpp_freq :
+ inst_pos->clk_data.low_power_freq;
+ else
+ continue;
+
coreid = inst_pos->clk_data.core_id;

mbs_per_sec = load_per_instance(inst_pos);
@@ -543,9 +592,11 @@ static int decide_core(struct venus_inst *inst)
{
const u32 ptype = HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE;
struct venus_core *core = inst->core;
- u32 min_coreid, min_load, inst_load;
+ u32 min_coreid, min_load, cur_inst_load;
+ u32 min_lp_coreid, min_lp_load, cur_inst_lp_load;
struct hfi_videocores_usage_type cu;
unsigned long max_freq;
+ int ret = 0;

if (legacy_binding) {
if (inst->session_type == VIDC_SESSION_TYPE_DEC)
@@ -559,23 +610,43 @@ static int decide_core(struct venus_inst *inst)
if (inst->clk_data.core_id != VIDC_CORE_ID_DEFAULT)
return 0;

- inst_load = load_per_instance(inst);
- inst_load *= inst->clk_data.vpp_freq;
- max_freq = core->res->freq_tbl[0].freq;
+ cur_inst_load = load_per_instance(inst);
+ cur_inst_load *= inst->clk_data.vpp_freq;
+ /*TODO : divide this inst->load by work_route */

- min_loaded_core(inst, &min_coreid, &min_load);
+ cur_inst_lp_load = load_per_instance(inst);
+ cur_inst_lp_load *= inst->clk_data.low_power_freq;
+ /*TODO : divide this inst->load by work_route */

- if ((inst_load + min_load) > max_freq) {
- dev_warn(core->dev, "HW is overloaded, needed: %u max: %lu\n",
- inst_load, max_freq);
+ max_freq = core->res->freq_tbl[0].freq;
+
+ min_loaded_core(inst, &min_coreid, &min_load, false);
+ min_loaded_core(inst, &min_lp_coreid, &min_lp_load, true);
+
+ if (cur_inst_load + min_load <= max_freq) {
+ inst->clk_data.core_id = min_coreid;
+ cu.video_core_enable_mask = min_coreid;
+ } else if (cur_inst_lp_load + min_load <= max_freq) {
+ /* Move current instance to LP and return */
+ inst->clk_data.core_id = min_coreid;
+ cu.video_core_enable_mask = min_coreid;
+ power_save_mode_enable(inst, true);
+ } else if (cur_inst_lp_load + min_lp_load <= max_freq) {
+ /* Move all instances to LP mode and return */
+ inst->clk_data.core_id = min_lp_coreid;
+ cu.video_core_enable_mask = min_lp_coreid;
+ move_core_to_power_save_mode(core, min_lp_coreid);
+ } else {
+ dev_warn(core->dev, "HW can't support this load");
return -EINVAL;
}

- inst->clk_data.core_id = min_coreid;
- cu.video_core_enable_mask = min_coreid;
-
done:
- return hfi_session_set_property(inst, ptype, &cu);
+ ret = hfi_session_set_property(inst, ptype, &cu);
+ if (ret)
+ return ret;
+
+ return ret;
}

static int acquire_core(struct venus_inst *inst)
@@ -936,7 +1007,7 @@ static int core_power_v4(struct device *dev, int on)
static unsigned long calculate_inst_freq(struct venus_inst *inst,
unsigned long filled_len)
{
- unsigned long vpp_freq = 0, vsp_freq = 0;
+ unsigned long vpp_freq_per_mb = 0, vpp_freq = 0, vsp_freq = 0;
u32 fps = (u32)inst->fps;
u32 mbs_per_sec;

@@ -945,7 +1016,12 @@ static unsigned long calculate_inst_freq(struct venus_inst *inst,
if (inst->state != INST_START)
return 0;

- vpp_freq = mbs_per_sec * inst->clk_data.vpp_freq;
+ if (inst->session_type == VIDC_SESSION_TYPE_ENC)
+ vpp_freq_per_mb = inst->flags & VENUS_LOW_POWER ?
+ inst->clk_data.low_power_freq :
+ inst->clk_data.vpp_freq;
+
+ vpp_freq = mbs_per_sec * vpp_freq_per_mb;
/* 21 / 20 is overhead factor */
vpp_freq += vpp_freq / 20;
vsp_freq = mbs_per_sec * inst->clk_data.vsp_freq;
--
2.7.4


2021-04-27 20:57:44

by Fritz Koenig

[permalink] [raw]
Subject: Re: [PATCH] media: venus: Enable low power setting for encoder

On Sun, Apr 11, 2021 at 11:59 PM Dikshita Agarwal
<[email protected]> wrote:
>
> Set the FW to run in low power for encoder
> to accommodate more session without losing much on quality.
>
> Signed-off-by: Dikshita Agarwal <[email protected]>
> ---
> drivers/media/platform/qcom/venus/core.h | 6 ++
> drivers/media/platform/qcom/venus/helpers.c | 2 +
> drivers/media/platform/qcom/venus/hfi_helper.h | 10 +-
> drivers/media/platform/qcom/venus/hfi_platform.c | 16 +++
> drivers/media/platform/qcom/venus/hfi_platform.h | 4 +
> .../media/platform/qcom/venus/hfi_platform_v4.c | 28 ++++--
> .../media/platform/qcom/venus/hfi_platform_v6.c | 28 ++++--
> drivers/media/platform/qcom/venus/pm_helpers.c | 108 ++++++++++++++++++---
> 8 files changed, 167 insertions(+), 35 deletions(-)
>
> diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h
> index 9451e54..9b5031f 100644
> --- a/drivers/media/platform/qcom/venus/core.h
> +++ b/drivers/media/platform/qcom/venus/core.h
> @@ -257,6 +257,7 @@ struct clock_data {
> unsigned long freq;
> unsigned long vpp_freq;
> unsigned long vsp_freq;
> + unsigned long low_power_freq;
> };
>
> #define to_venus_buffer(ptr) container_of(ptr, struct venus_buffer, vb)
> @@ -280,6 +281,10 @@ struct venus_ts_metadata {
> struct v4l2_timecode tc;
> };
>
> +enum venus_inst_modes {
> + VENUS_LOW_POWER = BIT(0),
> +};
> +
> /**
> * struct venus_inst - holds per instance parameters
> *
> @@ -400,6 +405,7 @@ struct venus_inst {
> unsigned int pic_struct;
> bool next_buf_last;
> bool drain_active;
> + enum venus_inst_modes flags;
> };
>
> #define IS_V1(core) ((core)->res->hfi_version == HFI_VERSION_1XX)
> diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c
> index 76ece2f..c6b6a30 100644
> --- a/drivers/media/platform/qcom/venus/helpers.c
> +++ b/drivers/media/platform/qcom/venus/helpers.c
> @@ -1566,6 +1566,8 @@ int venus_helper_session_init(struct venus_inst *inst)
> session_type);
> inst->clk_data.vsp_freq = hfi_platform_get_codec_vsp_freq(version, codec,
> session_type);
> + inst->clk_data.low_power_freq = hfi_platform_get_codec_lp_freq(version, codec,
> + session_type);
>
> return 0;
> }
> diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h
> index 6b524c7..5621cdb 100644
> --- a/drivers/media/platform/qcom/venus/hfi_helper.h
> +++ b/drivers/media/platform/qcom/venus/hfi_helper.h
> @@ -412,9 +412,6 @@
> #define HFI_BUFFER_MODE_RING 0x1000002
> #define HFI_BUFFER_MODE_DYNAMIC 0x1000003
>
> -#define HFI_VENC_PERFMODE_MAX_QUALITY 0x1
> -#define HFI_VENC_PERFMODE_POWER_SAVE 0x2
> -
> /*
> * HFI_PROPERTY_SYS_COMMON_START
> * HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + 0x0000
> @@ -815,6 +812,13 @@ struct hfi_framesize {
> u32 height;
> };
>
> +#define HFI_VENC_PERFMODE_MAX_QUALITY 0x1
> +#define HFI_VENC_PERFMODE_POWER_SAVE 0x2
> +
> +struct hfi_perf_mode {
> + u32 video_perf_mode;
> +};
> +
> #define VIDC_CORE_ID_DEFAULT 0
> #define VIDC_CORE_ID_1 1
> #define VIDC_CORE_ID_2 2
> diff --git a/drivers/media/platform/qcom/venus/hfi_platform.c b/drivers/media/platform/qcom/venus/hfi_platform.c
> index 8f47804..f5b4e1f 100644
> --- a/drivers/media/platform/qcom/venus/hfi_platform.c
> +++ b/drivers/media/platform/qcom/venus/hfi_platform.c
> @@ -50,6 +50,22 @@ hfi_platform_get_codec_vsp_freq(enum hfi_version version, u32 codec, u32 session
> return freq;
> }
>
> +unsigned long
> +hfi_platform_get_codec_lp_freq(enum hfi_version version, u32 codec, u32 session_type)
> +{
> + const struct hfi_platform *plat;
> + unsigned long freq = 0;
> +
> + plat = hfi_platform_get(version);
> + if (!plat)
> + return 0;
> +
> + if (plat->codec_lp_freq)
> + freq = plat->codec_lp_freq(session_type, codec);
> +
> + return freq;
> +}
> +
> u8 hfi_platform_num_vpp_pipes(enum hfi_version version)
> {
> const struct hfi_platform *plat;
> diff --git a/drivers/media/platform/qcom/venus/hfi_platform.h b/drivers/media/platform/qcom/venus/hfi_platform.h
> index 3819bb2..2dbe608 100644
> --- a/drivers/media/platform/qcom/venus/hfi_platform.h
> +++ b/drivers/media/platform/qcom/venus/hfi_platform.h
> @@ -43,11 +43,13 @@ struct hfi_platform_codec_freq_data {
> u32 session_type;
> unsigned long vpp_freq;
> unsigned long vsp_freq;
> + unsigned long low_power_freq;
> };
>
> struct hfi_platform {
> unsigned long (*codec_vpp_freq)(u32 session_type, u32 codec);
> unsigned long (*codec_vsp_freq)(u32 session_type, u32 codec);
> + unsigned long (*codec_lp_freq)(u32 session_type, u32 codec);
> void (*codecs)(u32 *enc_codecs, u32 *dec_codecs, u32 *count);
> const struct hfi_plat_caps *(*capabilities)(unsigned int *entries);
> u8 (*num_vpp_pipes)(void);
> @@ -63,5 +65,7 @@ unsigned long hfi_platform_get_codec_vpp_freq(enum hfi_version version, u32 code
> u32 session_type);
> unsigned long hfi_platform_get_codec_vsp_freq(enum hfi_version version, u32 codec,
> u32 session_type);
> +unsigned long hfi_platform_get_codec_lp_freq(enum hfi_version version, u32 codec,
> + u32 session_type);
> u8 hfi_platform_num_vpp_pipes(enum hfi_version version);
> #endif
> diff --git a/drivers/media/platform/qcom/venus/hfi_platform_v4.c b/drivers/media/platform/qcom/venus/hfi_platform_v4.c
> index 3848bb6..3f7f527 100644
> --- a/drivers/media/platform/qcom/venus/hfi_platform_v4.c
> +++ b/drivers/media/platform/qcom/venus/hfi_platform_v4.c
> @@ -262,14 +262,14 @@ static void get_codecs(u32 *enc_codecs, u32 *dec_codecs, u32 *count)
> }
>
> static const struct hfi_platform_codec_freq_data codec_freq_data[] = {
> - { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_ENC, 675, 10 },
> - { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_ENC, 675, 10 },
> - { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_ENC, 675, 10 },
> - { V4L2_PIX_FMT_MPEG2, VIDC_SESSION_TYPE_DEC, 200, 10 },
> - { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_DEC, 200, 10 },
> - { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_DEC, 200, 10 },
> - { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_DEC, 200, 10 },
> - { V4L2_PIX_FMT_VP9, VIDC_SESSION_TYPE_DEC, 200, 10 },
> + { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_ENC, 675, 10, 320 },
> + { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_ENC, 675, 10, 320 },
> + { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_ENC, 675, 10, 320 },
> + { V4L2_PIX_FMT_MPEG2, VIDC_SESSION_TYPE_DEC, 200, 10, 200 },
> + { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_DEC, 200, 10, 200 },
> + { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_DEC, 200, 10, 200 },
> + { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_DEC, 200, 10, 200 },
> + { V4L2_PIX_FMT_VP9, VIDC_SESSION_TYPE_DEC, 200, 10, 200 },
> };
>
> static const struct hfi_platform_codec_freq_data *
> @@ -311,9 +311,21 @@ static unsigned long codec_vsp_freq(u32 session_type, u32 codec)
> return 0;
> }
>
> +static unsigned long codec_lp_freq(u32 session_type, u32 codec)
> +{
> + const struct hfi_platform_codec_freq_data *data;
> +
> + data = get_codec_freq_data(session_type, codec);
> + if (data)
> + return data->low_power_freq;
> +
> + return 0;
> +}
> +
> const struct hfi_platform hfi_plat_v4 = {
> .codec_vpp_freq = codec_vpp_freq,
> .codec_vsp_freq = codec_vsp_freq,
> + .codec_lp_freq = codec_lp_freq,
> .codecs = get_codecs,
> .capabilities = get_capabilities,
> };
> diff --git a/drivers/media/platform/qcom/venus/hfi_platform_v6.c b/drivers/media/platform/qcom/venus/hfi_platform_v6.c
> index 2278be1..15d0dc8 100644
> --- a/drivers/media/platform/qcom/venus/hfi_platform_v6.c
> +++ b/drivers/media/platform/qcom/venus/hfi_platform_v6.c
> @@ -262,14 +262,14 @@ static void get_codecs(u32 *enc_codecs, u32 *dec_codecs, u32 *count)
> }
>
> static const struct hfi_platform_codec_freq_data codec_freq_data[] = {
> - { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_ENC, 675, 25 },
> - { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_ENC, 675, 25 },
> - { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_ENC, 675, 60 },
> - { V4L2_PIX_FMT_MPEG2, VIDC_SESSION_TYPE_DEC, 200, 25 },
> - { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_DEC, 200, 25 },
> - { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_DEC, 200, 25 },
> - { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_DEC, 200, 60 },
> - { V4L2_PIX_FMT_VP9, VIDC_SESSION_TYPE_DEC, 200, 60 },
> + { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_ENC, 675, 25, 320 },
> + { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_ENC, 675, 25, 320 },
> + { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_ENC, 675, 60, 320 },
> + { V4L2_PIX_FMT_MPEG2, VIDC_SESSION_TYPE_DEC, 200, 25, 200 },
> + { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_DEC, 200, 25, 200 },
> + { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_DEC, 200, 25, 200 },
> + { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_DEC, 200, 60, 200 },
> + { V4L2_PIX_FMT_VP9, VIDC_SESSION_TYPE_DEC, 200, 60, 200 },
> };
>
> static const struct hfi_platform_codec_freq_data *
> @@ -311,6 +311,17 @@ static unsigned long codec_vsp_freq(u32 session_type, u32 codec)
> return 0;
> }
>
> +static unsigned long codec_lp_freq(u32 session_type, u32 codec)
> +{
> + const struct hfi_platform_codec_freq_data *data;
> +
> + data = get_codec_freq_data(session_type, codec);
> + if (data)
> + return data->low_power_freq;
> +
> + return 0;
> +}
> +
> static u8 num_vpp_pipes(void)
> {
> return 4;
> @@ -319,6 +330,7 @@ static u8 num_vpp_pipes(void)
> const struct hfi_platform hfi_plat_v6 = {
> .codec_vpp_freq = codec_vpp_freq,
> .codec_vsp_freq = codec_vsp_freq,
> + .codec_lp_freq = codec_lp_freq,
> .codecs = get_codecs,
> .capabilities = get_capabilities,
> .num_vpp_pipes = num_vpp_pipes,
> diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c
> index 43c4e3d..a3f3e31 100644
> --- a/drivers/media/platform/qcom/venus/pm_helpers.c
> +++ b/drivers/media/platform/qcom/venus/pm_helpers.c
> @@ -492,8 +492,50 @@ static int poweron_coreid(struct venus_core *core, unsigned int coreid_mask)
> return 0;
> }
>
> +static inline int power_save_mode_enable(struct venus_inst *inst,
> + bool enable)
> +{
> + struct venc_controls *enc_ctr = &inst->controls.enc;
> + const u32 ptype = HFI_PROPERTY_CONFIG_VENC_PERF_MODE;
> + u32 venc_mode;
> + int ret = 0;
> +
> + if (inst->session_type != VIDC_SESSION_TYPE_ENC)
> + return 0;
> +
> + if (enc_ctr->bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CQ)
> + enable = false;
> +
> + venc_mode = enable ? HFI_VENC_PERFMODE_POWER_SAVE :
> + HFI_VENC_PERFMODE_MAX_QUALITY;
> +
> + ret = hfi_session_set_property(inst, ptype, &venc_mode);
> + if (ret)
> + return ret;
> +
> + inst->flags = enable ? inst->flags | VENUS_LOW_POWER :
> + inst->flags & ~VENUS_LOW_POWER;
> +
> + return ret;
> +}
> +
> +static int move_core_to_power_save_mode(struct venus_core *core,
> + u32 core_id)
> +{
> + struct venus_inst *inst = NULL;
> +
> + mutex_lock(&core->lock);
> + list_for_each_entry(inst, &core->instances, list) {
> + if (inst->clk_data.core_id == core_id &&
> + inst->session_type == VIDC_SESSION_TYPE_ENC)
> + power_save_mode_enable(inst, true);
> + }
> + mutex_unlock(&core->lock);
> + return 0;
> +}
> +
> static void
> -min_loaded_core(struct venus_inst *inst, u32 *min_coreid, u32 *min_load)
> +min_loaded_core(struct venus_inst *inst, u32 *min_coreid, u32 *min_load, bool low_power)
> {
> u32 mbs_per_sec, load, core1_load = 0, core2_load = 0;
> u32 cores_max = core_num_max(inst);
> @@ -511,7 +553,14 @@ min_loaded_core(struct venus_inst *inst, u32 *min_coreid, u32 *min_load)
> if (inst_pos->state != INST_START)
> continue;
>
> - vpp_freq = inst_pos->clk_data.vpp_freq;
> + if (inst->session_type == VIDC_SESSION_TYPE_DEC)
> + vpp_freq = inst_pos->clk_data.vpp_freq;
> + else if (inst->session_type == VIDC_SESSION_TYPE_ENC)
> + vpp_freq = low_power ? inst_pos->clk_data.vpp_freq :
> + inst_pos->clk_data.low_power_freq;
> + else
> + continue;
> +
> coreid = inst_pos->clk_data.core_id;
>
> mbs_per_sec = load_per_instance(inst_pos);
> @@ -543,9 +592,11 @@ static int decide_core(struct venus_inst *inst)
> {
> const u32 ptype = HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE;
> struct venus_core *core = inst->core;
> - u32 min_coreid, min_load, inst_load;
> + u32 min_coreid, min_load, cur_inst_load;
> + u32 min_lp_coreid, min_lp_load, cur_inst_lp_load;
> struct hfi_videocores_usage_type cu;
> unsigned long max_freq;
> + int ret = 0;
>
> if (legacy_binding) {
> if (inst->session_type == VIDC_SESSION_TYPE_DEC)
> @@ -559,23 +610,43 @@ static int decide_core(struct venus_inst *inst)
> if (inst->clk_data.core_id != VIDC_CORE_ID_DEFAULT)
> return 0;
>
> - inst_load = load_per_instance(inst);
> - inst_load *= inst->clk_data.vpp_freq;
> - max_freq = core->res->freq_tbl[0].freq;
> + cur_inst_load = load_per_instance(inst);
> + cur_inst_load *= inst->clk_data.vpp_freq;
> + /*TODO : divide this inst->load by work_route */
>
> - min_loaded_core(inst, &min_coreid, &min_load);
> + cur_inst_lp_load = load_per_instance(inst);
> + cur_inst_lp_load *= inst->clk_data.low_power_freq;
> + /*TODO : divide this inst->load by work_route */
>
> - if ((inst_load + min_load) > max_freq) {
> - dev_warn(core->dev, "HW is overloaded, needed: %u max: %lu\n",
> - inst_load, max_freq);
> + max_freq = core->res->freq_tbl[0].freq;
> +
> + min_loaded_core(inst, &min_coreid, &min_load, false);
> + min_loaded_core(inst, &min_lp_coreid, &min_lp_load, true);
> +
> + if (cur_inst_load + min_load <= max_freq) {
> + inst->clk_data.core_id = min_coreid;
> + cu.video_core_enable_mask = min_coreid;
> + } else if (cur_inst_lp_load + min_load <= max_freq) {
> + /* Move current instance to LP and return */
> + inst->clk_data.core_id = min_coreid;
> + cu.video_core_enable_mask = min_coreid;
> + power_save_mode_enable(inst, true);
> + } else if (cur_inst_lp_load + min_lp_load <= max_freq) {
> + /* Move all instances to LP mode and return */
> + inst->clk_data.core_id = min_lp_coreid;
> + cu.video_core_enable_mask = min_lp_coreid;
> + move_core_to_power_save_mode(core, min_lp_coreid);
> + } else {
> + dev_warn(core->dev, "HW can't support this load");
> return -EINVAL;
> }
>
> - inst->clk_data.core_id = min_coreid;
> - cu.video_core_enable_mask = min_coreid;
> -
> done:
> - return hfi_session_set_property(inst, ptype, &cu);
> + ret = hfi_session_set_property(inst, ptype, &cu);
> + if (ret)
> + return ret;
> +
> + return ret;
> }
>
> static int acquire_core(struct venus_inst *inst)
> @@ -936,7 +1007,7 @@ static int core_power_v4(struct device *dev, int on)
> static unsigned long calculate_inst_freq(struct venus_inst *inst,
> unsigned long filled_len)
> {
> - unsigned long vpp_freq = 0, vsp_freq = 0;
> + unsigned long vpp_freq_per_mb = 0, vpp_freq = 0, vsp_freq = 0;
> u32 fps = (u32)inst->fps;
> u32 mbs_per_sec;
>
> @@ -945,7 +1016,12 @@ static unsigned long calculate_inst_freq(struct venus_inst *inst,
> if (inst->state != INST_START)
> return 0;
>
> - vpp_freq = mbs_per_sec * inst->clk_data.vpp_freq;
> + if (inst->session_type == VIDC_SESSION_TYPE_ENC)
> + vpp_freq_per_mb = inst->flags & VENUS_LOW_POWER ?
> + inst->clk_data.low_power_freq :
> + inst->clk_data.vpp_freq;
> +
> + vpp_freq = mbs_per_sec * vpp_freq_per_mb;
> /* 21 / 20 is overhead factor */
> vpp_freq += vpp_freq / 20;
> vsp_freq = mbs_per_sec * inst->clk_data.vsp_freq;
> --
> 2.7.4
>

Tested-by: Fritz Koenig <[email protected]>