Browse Source

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 <[email protected]>
Govindaraj Rajagopal 2 years ago
parent
commit
e15f3afabd

+ 2 - 0
driver/vidc/inc/msm_vidc_driver.h

@@ -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_wait(struct msm_vidc_core *core);
 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_inst_timeout(struct msm_vidc_inst *inst);
 int msm_vidc_print_buffer_info(struct msm_vidc_inst *inst);

+ 10 - 0
driver/vidc/inc/resources.h

@@ -117,6 +117,13 @@ struct regulator_set {
 	u32                        count;
 };
 
+struct clock_residency {
+	struct list_head           list;
+	u64                        rate;
+	u64                        start_time_us;
+	u64                        total_time_us;
+};
+
 struct clock_info {
 	struct clk                *clk;
 	const char                *name;
@@ -126,6 +133,7 @@ struct clock_info {
 #ifdef CONFIG_MSM_MMRM
 	struct mmrm_client        *mmrm_client;
 #endif
+	struct list_head           residency_list;  /* list of struct clock_residency */
 };
 
 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_set_flag)(struct msm_vidc_core *core,
 		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);

+ 5 - 0
driver/vidc/src/msm_vidc.c

@@ -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__);
 		goto error;
 	}
+
+	/* reset clock residency stats */
+	msm_vidc_reset_residency_stats(core);
+
 	msm_vidc_scale_power(inst, true);
 
 	rc = msm_vidc_session_open(inst);
@@ -1031,6 +1035,7 @@ int msm_vidc_close(void *instance)
 	inst_lock(inst, __func__);
 	/* print final stats */
 	msm_vidc_print_stats(inst);
+	msm_vidc_print_residency_stats(core);
 	msm_vidc_session_close(inst);
 	msm_vidc_event_queue_deinit(inst);
 	msm_vidc_vb2_queue_deinit(inst);

+ 28 - 0
driver/vidc/src/msm_vidc_driver.c

@@ -4726,6 +4726,34 @@ unlock:
 	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 rc = 0;

+ 236 - 12
driver/vidc/src/resources.c

@@ -288,11 +288,13 @@ static int __init_regulators(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;
+	struct freq_table *freq_tbl;
 	struct clock_set *clocks;
 	struct clock_info *cinfo = NULL;
-	u32 clk_count = 0, cnt = 0;
-	int rc = 0;
+	u32 clk_count = 0, freq_count = 0;
+	int fcnt = 0, cnt = 0, rc = 0;
 
 	if (!core || !core->resource || !core->platform) {
 		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;
 	}
 
+	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 */
 	venus_hfi_for_each_clock(core, cinfo) {
 		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);
 }
 
+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
 static int __set_clk_rate(struct msm_vidc_core *core, struct clock_info *cl,
 			  u64 rate)
@@ -1073,6 +1238,7 @@ static int __set_clk_rate(struct msm_vidc_core *core, struct clock_info *cl,
 	int rc = 0;
 	struct mmrm_client_data client_data;
 	struct mmrm_client *client;
+	u64 srate;
 
 	/* not registered */
 	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;
 	}
 
+	/* update clock residency stats */
+	update_residency_stats(core, cl, rate);
+
 	/*
 	 * This conversion is necessary since we are scaling clock values based on
 	 * the branch clock. However, mmrm driver expects source clock to be registered
 	 * and used for scaling.
 	 * 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 */
 	if (rate == cl->prev)
 		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)) {
 		/* set clock rate to mmrm driver */
 		client = cl->mmrm_client;
 		memset(&client_data, 0, sizeof(client_data));
 		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) {
 			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;
 		}
 	} else {
 		/* set clock rate to clock driver */
-		rc = clk_set_rate(cl->clk, rate);
+		rc = clk_set_rate(cl->clk, srate);
 		if (rc) {
 			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;
 		}
 	}
@@ -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,
 			  u64 rate)
 {
+	u64 srate;
 	int rc = 0;
 
 	/* not registered */
@@ -1134,24 +1305,28 @@ static int __set_clk_rate(struct msm_vidc_core *core, struct clock_info *cl,
 		return -EINVAL;
 	}
 
+	/* update clock residency stats */
+	update_residency_stats(core, cl, rate);
+
 	/*
 	 * This conversion is necessary since we are scaling clock values based on
 	 * the branch clock. However, mmrm driver expects source clock to be registered
 	 * and used for scaling.
 	 * 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 */
 	if (rate == cl->prev)
 		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) {
 		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;
 	}
 
@@ -1241,6 +1416,9 @@ static int __prepare_enable_clock(struct msm_vidc_core *core,
 		 * it to the lowest frequency possible
 		 */
 		if (cl->has_scaling) {
+			/* reset clk residency stats */
+			reset_residency_stats(core, cl);
+
 			rate = clk_round_rate(cl->clk, 0);
 			/**
 			 * 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;
 }
 
+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 = {
 	.init = __init_resources,
 	.reset_bridge = __reset_ahb2axi_bridge,
@@ -1460,6 +1682,8 @@ static const struct msm_vidc_resources_ops res_ops = {
 	.clk_enable = __prepare_enable_clock,
 	.clk_disable = __disable_unprepare_clock,
 	.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)