video: driver: add clock residency calculation support

Add logic to derive clock residency values for each
frequency levels.

Change-Id: Iadad29d2733cb083fce627999a31dd96475b73c1
Signed-off-by: Govindaraj Rajagopal <quic_grajagop@quicinc.com>
This commit is contained in:
Govindaraj Rajagopal
2022-12-23 20:54:53 +05:30
parent 4f297753f7
commit e15f3afabd
5 changed files with 281 additions and 12 deletions

View File

@@ -439,6 +439,8 @@ int msm_vidc_change_core_sub_state(struct msm_vidc_core *core,
int msm_vidc_core_init(struct msm_vidc_core *core); int msm_vidc_core_init(struct msm_vidc_core *core);
int msm_vidc_core_init_wait(struct msm_vidc_core *core); int msm_vidc_core_init_wait(struct msm_vidc_core *core);
int msm_vidc_core_deinit(struct msm_vidc_core *core, bool force); int msm_vidc_core_deinit(struct msm_vidc_core *core, bool force);
int msm_vidc_print_residency_stats(struct msm_vidc_core *core);
int msm_vidc_reset_residency_stats(struct msm_vidc_core *core);
int msm_vidc_core_deinit_locked(struct msm_vidc_core *core, bool force); int msm_vidc_core_deinit_locked(struct msm_vidc_core *core, bool force);
int msm_vidc_inst_timeout(struct msm_vidc_inst *inst); int msm_vidc_inst_timeout(struct msm_vidc_inst *inst);
int msm_vidc_print_buffer_info(struct msm_vidc_inst *inst); int msm_vidc_print_buffer_info(struct msm_vidc_inst *inst);

View File

@@ -117,6 +117,13 @@ struct regulator_set {
u32 count; u32 count;
}; };
struct clock_residency {
struct list_head list;
u64 rate;
u64 start_time_us;
u64 total_time_us;
};
struct clock_info { struct clock_info {
struct clk *clk; struct clk *clk;
const char *name; const char *name;
@@ -126,6 +133,7 @@ struct clock_info {
#ifdef CONFIG_MSM_MMRM #ifdef CONFIG_MSM_MMRM
struct mmrm_client *mmrm_client; struct mmrm_client *mmrm_client;
#endif #endif
struct list_head residency_list; /* list of struct clock_residency */
}; };
struct clock_set { struct clock_set {
@@ -227,6 +235,8 @@ struct msm_vidc_resources_ops {
int (*clk_enable)(struct msm_vidc_core *core, const char *name); int (*clk_enable)(struct msm_vidc_core *core, const char *name);
int (*clk_set_flag)(struct msm_vidc_core *core, int (*clk_set_flag)(struct msm_vidc_core *core,
const char *name, enum branch_mem_flags flag); const char *name, enum branch_mem_flags flag);
int (*clk_print_residency_stats)(struct msm_vidc_core *core);
int (*clk_reset_residency_stats)(struct msm_vidc_core *core);
}; };
const struct msm_vidc_resources_ops *get_resources_ops(void); const struct msm_vidc_resources_ops *get_resources_ops(void);

View File

@@ -992,6 +992,10 @@ void *msm_vidc_open(void *vidc_core, u32 session_type)
i_vpr_e(inst, "%s: failed to get session id\n", __func__); i_vpr_e(inst, "%s: failed to get session id\n", __func__);
goto error; goto error;
} }
/* reset clock residency stats */
msm_vidc_reset_residency_stats(core);
msm_vidc_scale_power(inst, true); msm_vidc_scale_power(inst, true);
rc = msm_vidc_session_open(inst); rc = msm_vidc_session_open(inst);
@@ -1031,6 +1035,7 @@ int msm_vidc_close(void *instance)
inst_lock(inst, __func__); inst_lock(inst, __func__);
/* print final stats */ /* print final stats */
msm_vidc_print_stats(inst); msm_vidc_print_stats(inst);
msm_vidc_print_residency_stats(core);
msm_vidc_session_close(inst); msm_vidc_session_close(inst);
msm_vidc_event_queue_deinit(inst); msm_vidc_event_queue_deinit(inst);
msm_vidc_vb2_queue_deinit(inst); msm_vidc_vb2_queue_deinit(inst);

View File

@@ -4726,6 +4726,34 @@ unlock:
return rc; return rc;
} }
int msm_vidc_print_residency_stats(struct msm_vidc_core *core)
{
int rc = 0;
core_lock(core, __func__);
rc = call_res_op(core, clk_print_residency_stats, core);
if (rc)
goto unlock;
unlock:
core_unlock(core, __func__);
return rc;
}
int msm_vidc_reset_residency_stats(struct msm_vidc_core *core)
{
int rc = 0;
core_lock(core, __func__);
rc = call_res_op(core, clk_reset_residency_stats, core);
if (rc)
goto unlock;
unlock:
core_unlock(core, __func__);
return rc;
}
int msm_vidc_inst_timeout(struct msm_vidc_inst *inst) int msm_vidc_inst_timeout(struct msm_vidc_inst *inst)
{ {
int rc = 0; int rc = 0;

View File

@@ -288,11 +288,13 @@ static int __init_regulators(struct msm_vidc_core *core)
static int __init_clocks(struct msm_vidc_core *core) static int __init_clocks(struct msm_vidc_core *core)
{ {
struct clock_residency *residency = NULL;
const struct clk_table *clk_tbl; const struct clk_table *clk_tbl;
struct freq_table *freq_tbl;
struct clock_set *clocks; struct clock_set *clocks;
struct clock_info *cinfo = NULL; struct clock_info *cinfo = NULL;
u32 clk_count = 0, cnt = 0; u32 clk_count = 0, freq_count = 0;
int rc = 0; int fcnt = 0, cnt = 0, rc = 0;
if (!core || !core->resource || !core->platform) { if (!core || !core->resource || !core->platform) {
d_vpr_e("%s: invalid params\n", __func__); d_vpr_e("%s: invalid params\n", __func__);
@@ -325,6 +327,42 @@ static int __init_clocks(struct msm_vidc_core *core)
clocks->clock_tbl[cnt].has_scaling = clk_tbl[cnt].scaling; clocks->clock_tbl[cnt].has_scaling = clk_tbl[cnt].scaling;
} }
freq_tbl = core->platform->data.freq_tbl;
freq_count = core->platform->data.freq_tbl_size;
/* populate clk residency stats table */
for (cnt = 0; cnt < clocks->count; cnt++) {
/* initialize residency_list */
INIT_LIST_HEAD(&clocks->clock_tbl[cnt].residency_list);
/* skip if scaling not supported */
if (!clocks->clock_tbl[cnt].has_scaling)
continue;
for (fcnt = 0; fcnt < freq_count; fcnt++) {
residency = devm_kzalloc(&core->pdev->dev,
sizeof(struct clock_residency), GFP_KERNEL);
if (!residency) {
d_vpr_e("%s: failed to alloc clk residency stat node\n", __func__);
return -ENOMEM;
}
if (!freq_tbl) {
d_vpr_e("%s: invalid freq tbl %#x\n", __func__, freq_tbl);
return -EINVAL;
}
/* update residency node */
residency->rate = freq_tbl[fcnt].freq;
residency->start_time_us = 0;
residency->total_time_us = 0;
INIT_LIST_HEAD(&residency->list);
/* add entry into residency_list */
list_add_tail(&residency->list, &clocks->clock_tbl[cnt].residency_list);
}
}
/* print clock fields */ /* print clock fields */
venus_hfi_for_each_clock(core, cinfo) { venus_hfi_for_each_clock(core, cinfo) {
d_vpr_h("%s: clock name %s clock id %#x scaling %d\n", d_vpr_h("%s: clock name %s clock id %#x scaling %d\n",
@@ -1066,6 +1104,133 @@ static int set_bw(struct msm_vidc_core *core, unsigned long bw_ddr,
return __vote_buses(core, bw_ddr, bw_llcc); return __vote_buses(core, bw_ddr, bw_llcc);
} }
static int print_residency_stats(struct msm_vidc_core *core, struct clock_info *cl)
{
struct clock_residency *residency = NULL;
u64 total_time_us = 0;
int rc = 0;
if (!core || !cl) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
/* skip if scaling not supported */
if (!cl->has_scaling)
return 0;
/* grand total residency time */
list_for_each_entry(residency, &cl->residency_list, list)
total_time_us += residency->total_time_us;
/* sanity check to avoid divide by 0 */
total_time_us = (total_time_us > 0) ? total_time_us : 1;
/* print residency percent for each clock */
list_for_each_entry(residency, &cl->residency_list, list) {
d_vpr_h("%s: %s clock rate [%d] total %lluus residency %u%%\n",
__func__, cl->name, residency->rate, residency->total_time_us,
residency->total_time_us * 100 / total_time_us);
}
return rc;
}
static int reset_residency_stats(struct msm_vidc_core *core, struct clock_info *cl)
{
struct clock_residency *residency = NULL;
int rc = 0;
if (!core || !cl) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
/* skip if scaling not supported */
if (!cl->has_scaling)
return 0;
d_vpr_h("%s: reset %s residency stats\n", __func__, cl->name);
/* reset clock residency stats */
list_for_each_entry(residency, &cl->residency_list, list) {
residency->start_time_us = 0;
residency->total_time_us = 0;
}
return rc;
}
static struct clock_residency *get_residency_stats(struct clock_info *cl, u64 rate)
{
struct clock_residency *residency = NULL;
bool found = false;
if (!cl) {
d_vpr_e("%s: invalid params\n", __func__);
return NULL;
}
list_for_each_entry(residency, &cl->residency_list, list) {
if (residency->rate == rate) {
found = true;
break;
}
}
return found ? residency : NULL;
}
static int update_residency_stats(
struct msm_vidc_core *core, struct clock_info *cl, u64 rate)
{
struct clock_residency *cur_residency = NULL, *prev_residency = NULL;
u64 cur_time_us = 0;
int rc = 0;
if (!core || !cl) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
/* skip update if scaling not supported */
if (!cl->has_scaling)
return 0;
/* skip update if rate not changed */
if (rate == cl->prev)
return 0;
/* get current time in ns */
cur_time_us = ktime_get_ns() / 1000;
/* update previous rate residency end or total time */
prev_residency = get_residency_stats(cl, cl->prev);
if (prev_residency) {
if (prev_residency->start_time_us)
prev_residency->total_time_us = cur_time_us - prev_residency->start_time_us;
/* reset start time us */
prev_residency->start_time_us = 0;
}
/* clk disable case - no need to update new entry */
if (rate == 0)
return 0;
/* check if rate entry is present */
cur_residency = get_residency_stats(cl, rate);
if (!cur_residency) {
d_vpr_e("%s: entry not found. rate %llu\n", __func__, rate);
return -EINVAL;
}
/* update residency start time for current rate/freq */
cur_residency->start_time_us = cur_time_us;
return rc;
}
#ifdef CONFIG_MSM_MMRM #ifdef CONFIG_MSM_MMRM
static int __set_clk_rate(struct msm_vidc_core *core, struct clock_info *cl, static int __set_clk_rate(struct msm_vidc_core *core, struct clock_info *cl,
u64 rate) u64 rate)
@@ -1073,6 +1238,7 @@ static int __set_clk_rate(struct msm_vidc_core *core, struct clock_info *cl,
int rc = 0; int rc = 0;
struct mmrm_client_data client_data; struct mmrm_client_data client_data;
struct mmrm_client *client; struct mmrm_client *client;
u64 srate;
/* not registered */ /* not registered */
if (!core || !cl || !core->platform) { if (!core || !cl || !core->platform) {
@@ -1085,37 +1251,41 @@ static int __set_clk_rate(struct msm_vidc_core *core, struct clock_info *cl,
return -EINVAL; return -EINVAL;
} }
/* update clock residency stats */
update_residency_stats(core, cl, rate);
/* /*
* This conversion is necessary since we are scaling clock values based on * This conversion is necessary since we are scaling clock values based on
* the branch clock. However, mmrm driver expects source clock to be registered * the branch clock. However, mmrm driver expects source clock to be registered
* and used for scaling. * and used for scaling.
* TODO: Remove this scaling if using source clock instead of branch clock. * TODO: Remove this scaling if using source clock instead of branch clock.
*/ */
rate = rate * MSM_VIDC_CLOCK_SOURCE_SCALING_RATIO; srate = rate * MSM_VIDC_CLOCK_SOURCE_SCALING_RATIO;
/* bail early if requested clk rate is not changed */ /* bail early if requested clk rate is not changed */
if (rate == cl->prev) if (rate == cl->prev)
return 0; return 0;
d_vpr_p("Scaling clock %s to %llu, prev %llu\n", cl->name, rate, cl->prev); d_vpr_p("Scaling clock %s to %llu, prev %llu\n",
cl->name, srate, cl->prev * MSM_VIDC_CLOCK_SOURCE_SCALING_RATIO);
if (is_mmrm_supported(core)) { if (is_mmrm_supported(core)) {
/* set clock rate to mmrm driver */ /* set clock rate to mmrm driver */
client = cl->mmrm_client; client = cl->mmrm_client;
memset(&client_data, 0, sizeof(client_data)); memset(&client_data, 0, sizeof(client_data));
client_data.num_hw_blocks = 1; client_data.num_hw_blocks = 1;
rc = mmrm_client_set_value(client, &client_data, rate); rc = mmrm_client_set_value(client, &client_data, srate);
if (rc) { if (rc) {
d_vpr_e("%s: Failed to set mmrm clock rate %llu %s: %d\n", d_vpr_e("%s: Failed to set mmrm clock rate %llu %s: %d\n",
__func__, rate, cl->name, rc); __func__, srate, cl->name, rc);
return rc; return rc;
} }
} else { } else {
/* set clock rate to clock driver */ /* set clock rate to clock driver */
rc = clk_set_rate(cl->clk, rate); rc = clk_set_rate(cl->clk, srate);
if (rc) { if (rc) {
d_vpr_e("%s: Failed to set clock rate %llu %s: %d\n", d_vpr_e("%s: Failed to set clock rate %llu %s: %d\n",
__func__, rate, cl->name, rc); __func__, srate, cl->name, rc);
return rc; return rc;
} }
} }
@@ -1126,6 +1296,7 @@ static int __set_clk_rate(struct msm_vidc_core *core, struct clock_info *cl,
static int __set_clk_rate(struct msm_vidc_core *core, struct clock_info *cl, static int __set_clk_rate(struct msm_vidc_core *core, struct clock_info *cl,
u64 rate) u64 rate)
{ {
u64 srate;
int rc = 0; int rc = 0;
/* not registered */ /* not registered */
@@ -1134,24 +1305,28 @@ static int __set_clk_rate(struct msm_vidc_core *core, struct clock_info *cl,
return -EINVAL; return -EINVAL;
} }
/* update clock residency stats */
update_residency_stats(core, cl, rate);
/* /*
* This conversion is necessary since we are scaling clock values based on * This conversion is necessary since we are scaling clock values based on
* the branch clock. However, mmrm driver expects source clock to be registered * the branch clock. However, mmrm driver expects source clock to be registered
* and used for scaling. * and used for scaling.
* TODO: Remove this scaling if using source clock instead of branch clock. * TODO: Remove this scaling if using source clock instead of branch clock.
*/ */
rate = rate * MSM_VIDC_CLOCK_SOURCE_SCALING_RATIO; srate = rate * MSM_VIDC_CLOCK_SOURCE_SCALING_RATIO;
/* bail early if requested clk rate is not changed */ /* bail early if requested clk rate is not changed */
if (rate == cl->prev) if (rate == cl->prev)
return 0; return 0;
d_vpr_p("Scaling clock %s to %llu, prev %llu\n", cl->name, rate, cl->prev); d_vpr_p("Scaling clock %s to %llu, prev %llu\n",
cl->name, srate, cl->prev * MSM_VIDC_CLOCK_SOURCE_SCALING_RATIO);
rc = clk_set_rate(cl->clk, rate); rc = clk_set_rate(cl->clk, srate);
if (rc) { if (rc) {
d_vpr_e("%s: Failed to set clock rate %llu %s: %d\n", d_vpr_e("%s: Failed to set clock rate %llu %s: %d\n",
__func__, rate, cl->name, rc); __func__, srate, cl->name, rc);
return rc; return rc;
} }
@@ -1241,6 +1416,9 @@ static int __prepare_enable_clock(struct msm_vidc_core *core,
* it to the lowest frequency possible * it to the lowest frequency possible
*/ */
if (cl->has_scaling) { if (cl->has_scaling) {
/* reset clk residency stats */
reset_residency_stats(core, cl);
rate = clk_round_rate(cl->clk, 0); rate = clk_round_rate(cl->clk, 0);
/** /**
* source clock is already multipled with scaling ratio and __set_clk_rate * source clock is already multipled with scaling ratio and __set_clk_rate
@@ -1445,6 +1623,50 @@ static int __reset_ahb2axi_bridge(struct msm_vidc_core *core)
return rc; return rc;
} }
static int __print_clock_residency_stats(struct msm_vidc_core *core)
{
struct clock_info *cl;
int rc = 0;
if (!core) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
venus_hfi_for_each_clock(core, cl) {
/* skip if scaling not supported */
if (!cl->has_scaling)
continue;
/* print clock residency stats */
print_residency_stats(core, cl);
}
return rc;
}
static int __reset_clock_residency_stats(struct msm_vidc_core *core)
{
struct clock_info *cl;
int rc = 0;
if (!core) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
venus_hfi_for_each_clock(core, cl) {
/* skip if scaling not supported */
if (!cl->has_scaling)
continue;
/* reset clock residency stats */
reset_residency_stats(core, cl);
}
return rc;
}
static const struct msm_vidc_resources_ops res_ops = { static const struct msm_vidc_resources_ops res_ops = {
.init = __init_resources, .init = __init_resources,
.reset_bridge = __reset_ahb2axi_bridge, .reset_bridge = __reset_ahb2axi_bridge,
@@ -1460,6 +1682,8 @@ static const struct msm_vidc_resources_ops res_ops = {
.clk_enable = __prepare_enable_clock, .clk_enable = __prepare_enable_clock,
.clk_disable = __disable_unprepare_clock, .clk_disable = __disable_unprepare_clock,
.clk_set_flag = __clock_set_flag, .clk_set_flag = __clock_set_flag,
.clk_print_residency_stats = __print_clock_residency_stats,
.clk_reset_residency_stats = __reset_clock_residency_stats,
}; };
const struct msm_vidc_resources_ops *get_resources_ops(void) const struct msm_vidc_resources_ops *get_resources_ops(void)