media: venus: Enable low power setting for encoder
authorDikshita Agarwal <dikshita@codeaurora.org>
Mon, 12 Apr 2021 06:58:43 +0000 (08:58 +0200)
committerMauro Carvalho Chehab <mchehab+huawei@kernel.org>
Wed, 2 Jun 2021 10:28:17 +0000 (12:28 +0200)
Set the FW to run in low power for encoder
to accommodate more session without losing much on quality.

Signed-off-by: Dikshita Agarwal <dikshita@codeaurora.org>
Signed-off-by: Stanimir Varbanov <stanimir.varbanov@linaro.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
drivers/media/platform/qcom/venus/core.h
drivers/media/platform/qcom/venus/helpers.c
drivers/media/platform/qcom/venus/hfi_helper.h
drivers/media/platform/qcom/venus/hfi_platform.c
drivers/media/platform/qcom/venus/hfi_platform.h
drivers/media/platform/qcom/venus/hfi_platform_v4.c
drivers/media/platform/qcom/venus/hfi_platform_v6.c
drivers/media/platform/qcom/venus/pm_helpers.c

index 56054c3..8df2d49 100644 (file)
@@ -292,6 +292,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)
@@ -315,6 +316,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
  *
@@ -444,6 +449,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)
index b813d6d..b691215 100644 (file)
@@ -1627,6 +1627,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;
 }
index 63cd347..b0a9beb 100644 (file)
 #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
@@ -848,6 +845,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
index 8f47804..f5b4e1f 100644 (file)
@@ -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;
index 3819bb2..2dbe608 100644 (file)
@@ -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
index 3848bb6..3f7f527 100644 (file)
@@ -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,
 };
index dd1a039..d8243b2 100644 (file)
@@ -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,
index fc204e4..3e2345e 100644 (file)
@@ -523,8 +523,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);
@@ -542,7 +584,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);
@@ -574,9 +623,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)
@@ -590,23 +641,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)
@@ -1005,7 +1076,7 @@ static int core_power_v4(struct venus_core *core, 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;
 
@@ -1014,7 +1085,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;