Эх сурвалжийг харах

msm: camera: cpas: Fix drv based bw voting and enhance debugging

Currently, we vote the drv bw before enabling clocks, which results
in channel switch based issues when ife is the first stream on client.
Also, the same needs to be taken care during stream off. In addition
to that, the stop bw vote for drv scenario is not propagated through
the camnoc tree correctly, causing non-zero bw to be voted during
stream off. Fix bw apply order and make sure drv bw is propagated
correctly during stop. Also, add a check to ensure all mnoc bws are
zero when the final cpas client issues stop. Enahnce cpas dump to
include debugging inside the camnoc tree.

CRs-Fixed: 3065551
Change-Id: I411870140036bfb33bb4555259f173d5f0c639d8
Signed-off-by: Mukund Madhusudan Atre <[email protected]>
Mukund Madhusudan Atre 2 жил өмнө
parent
commit
20e8ba235d

+ 89 - 43
drivers/cam_cpas/cam_cpas_hw.c

@@ -1310,7 +1310,7 @@ static int cam_cpas_util_apply_client_axi_vote(
 	int rc = 0, i = 0, drv_voting_idx;
 	bool apply_smart_qos = false;
 	bool rt_bw_updated = false;
-	bool camnoc_unchanged = false;
+	bool camnoc_unchanged;
 
 	mutex_lock(&cpas_core->tree_lock);
 	if (!cpas_client->tree_node_valid) {
@@ -1365,6 +1365,7 @@ static int cam_cpas_util_apply_client_axi_vote(
 
 	/* Traverse through node tree and update bw vote values */
 	for (i = 0; i < con_axi_vote->num_paths; i++) {
+		camnoc_unchanged = false;
 		path_data_type = con_axi_vote->axi_path[i].path_data_type;
 		transac_type = con_axi_vote->axi_path[i].transac_type;
 		curr_tree_node = cpas_client->tree_node[path_data_type][transac_type];
@@ -1377,13 +1378,15 @@ static int cam_cpas_util_apply_client_axi_vote(
 			camnoc_unchanged = true;
 
 		curr_camnoc_old = curr_tree_node->camnoc_bw;
-		curr_tree_node->camnoc_bw = con_axi_vote->axi_path[i].camnoc_bw;
 		memcpy(&curr_mnoc_old, &curr_tree_node->bw_info[drv_voting_idx],
 			sizeof(struct cam_cpas_axi_bw_info));
 
+		cam_cpas_dump_tree_vote_info(curr_tree_node, "Level0 before update",
+			drv_voting_idx);
+
 		if (soc_private->enable_cam_ddr_drv && (con_axi_vote->axi_path[i].vote_level ==
 			CAM_CPAS_VOTE_LEVEL_HIGH)) {
-			if (camnoc_unchanged &&
+			if ((apply_type != CAM_CPAS_APPLY_TYPE_STOP) && camnoc_unchanged &&
 				(curr_tree_node->bw_info[drv_voting_idx].drv_vote.high.ab ==
 				con_axi_vote->axi_path[i].mnoc_ab_bw) &&
 				(curr_tree_node->bw_info[drv_voting_idx].drv_vote.high.ib ==
@@ -1399,7 +1402,7 @@ static int cam_cpas_util_apply_client_axi_vote(
 		} else {
 			if ((drv_voting_idx > CAM_CPAS_PORT_HLOS_DRV) &&
 				!cpas_core->force_hlos_drv) {
-				if (camnoc_unchanged &&
+				if ((apply_type != CAM_CPAS_APPLY_TYPE_STOP) && camnoc_unchanged &&
 					(curr_tree_node->bw_info[drv_voting_idx].drv_vote.low.ab ==
 					con_axi_vote->axi_path[i].mnoc_ab_bw) &&
 					(curr_tree_node->bw_info[drv_voting_idx].drv_vote.low.ib ==
@@ -1427,11 +1430,16 @@ static int cam_cpas_util_apply_client_axi_vote(
 			}
 		}
 
+		curr_tree_node->camnoc_bw = con_axi_vote->axi_path[i].camnoc_bw;
+		cam_cpas_dump_tree_vote_info(curr_tree_node, "Level0 after update", drv_voting_idx);
+
 		while (curr_tree_node->parent_node) {
 			par_tree_node = curr_tree_node->parent_node;
 			par_camnoc_old = par_tree_node->camnoc_bw;
 			memcpy(&par_mnoc_old, &par_tree_node->bw_info[drv_voting_idx],
 				sizeof(struct cam_cpas_axi_bw_info));
+			cam_cpas_dump_tree_vote_info(par_tree_node, "Parent before update",
+				drv_voting_idx);
 
 			/*
 			 * Remove contribution of current node old bw from parent,
@@ -1480,6 +1488,9 @@ static int cam_cpas_util_apply_client_axi_vote(
 				goto unlock_tree;
 			}
 
+			cam_cpas_dump_tree_vote_info(par_tree_node, "Parent after update",
+				drv_voting_idx);
+
 			if (!par_tree_node->parent_node) {
 				rc = cam_cpas_update_axi_vote_bw(cpas_hw, par_tree_node,
 					drv_voting_idx, mnoc_axi_port_updated,
@@ -1981,8 +1992,7 @@ static int cam_cpas_util_create_vote_all_paths(
 	for (i = 0; i < CAM_CPAS_TRANSACTION_MAX; i++) {
 		for (j = 0; j < CAM_CPAS_PATH_DATA_MAX; j++) {
 			if (cpas_client->tree_node[j][i]) {
-				axi_path =
-				&axi_vote->axi_path[axi_vote->num_paths];
+				axi_path = &axi_vote->axi_path[axi_vote->num_paths];
 
 				axi_path->path_data_type = j;
 				axi_path->transac_type = i;
@@ -2118,11 +2128,6 @@ static int cam_cpas_hw_start(void *hw_priv, void *start_args,
 
 	cam_cpas_dump_axi_vote_info(cpas_client, "CPAS Start Translated Vote", &axi_vote);
 
-	rc = cam_cpas_util_apply_client_axi_vote(cpas_hw, cpas_client, &axi_vote,
-		CAM_CPAS_APPLY_TYPE_START);
-	if (rc)
-		goto remove_ahb_vote;
-
 	if (cpas_core->streamon_clients == 0) {
 		if (cpas_core->force_hlos_drv)
 			soc_private->enable_cam_ddr_drv = false;
@@ -2146,7 +2151,7 @@ static int cam_cpas_hw_start(void *hw_priv, void *start_args,
 		if (rc) {
 			atomic_set(&cpas_core->irq_count, 0);
 			CAM_ERR(CAM_CPAS, "enable_resorce failed, rc=%d", rc);
-			goto remove_axi_vote;
+			goto remove_ahb_vote;
 		}
 
 		if (cpas_core->internal_ops.qchannel_handshake) {
@@ -2176,7 +2181,7 @@ static int cam_cpas_hw_start(void *hw_priv, void *start_args,
 				CAM_ERR(CAM_CPAS,
 					"failed in power_on settings rc=%d",
 					rc);
-				goto remove_axi_vote;
+				goto remove_ahb_vote;
 			}
 		}
 		CAM_DBG(CAM_CPAS, "irq_count=%d\n",
@@ -2189,6 +2194,15 @@ static int cam_cpas_hw_start(void *hw_priv, void *start_args,
 		cpas_hw->hw_state = CAM_HW_STATE_POWER_UP;
 	}
 
+	/*
+	 * Need to apply axi vote after we enable clocks, since we need certain clocks enabled for
+	 * drv channel switch
+	 */
+	rc = cam_cpas_util_apply_client_axi_vote(cpas_hw, cpas_client, &axi_vote,
+		CAM_CPAS_APPLY_TYPE_START);
+	if (rc)
+		goto remove_ahb_vote;
+
 	cpas_client->started = true;
 	cpas_core->streamon_clients++;
 
@@ -2200,20 +2214,6 @@ static int cam_cpas_hw_start(void *hw_priv, void *start_args,
 	mutex_unlock(&cpas_hw->hw_mutex);
 	return rc;
 
-remove_axi_vote:
-	memset(&axi_vote, 0x0, sizeof(struct cam_axi_vote));
-	rc = cam_cpas_util_create_vote_all_paths(cpas_client, &axi_vote);
-	if (rc)
-		CAM_ERR(CAM_CPAS, "Unable to create per path votes rc: %d", rc);
-
-	cam_cpas_dump_axi_vote_info(cpas_client, "CPAS Start fail Vote",
-		&axi_vote);
-
-	rc = cam_cpas_util_apply_client_axi_vote(cpas_hw, cpas_client, &axi_vote,
-		CAM_CPAS_APPLY_TYPE_STOP);
-	if (rc)
-		CAM_ERR(CAM_CPAS, "Unable remove votes rc: %d", rc);
-
 remove_ahb_vote:
 	remove_ahb.type = CAM_VOTE_ABSOLUTE;
 	remove_ahb.vote.level = CAM_SUSPEND_VOTE;
@@ -2233,6 +2233,47 @@ static int _check_irq_count(struct cam_cpas *cpas_core)
 	return (atomic_read(&cpas_core->irq_count) > 0) ? 0 : 1;
 }
 
+static int cam_cpas_util_validate_stop_bw(struct cam_cpas_private_soc *soc_private,
+	struct cam_cpas *cpas_core)
+{
+	int i;
+
+	for (i = 0; i < cpas_core->num_axi_ports; i++) {
+		if (soc_private->enable_cam_ddr_drv &&
+			(cpas_core->axi_port[i].bus_client.common_data.is_drv_port)) {
+
+			if ((cpas_core->axi_port[i].applied_bw.drv_vote.high.ab) ||
+				(cpas_core->axi_port[i].applied_bw.drv_vote.high.ib) ||
+				(cpas_core->axi_port[i].applied_bw.drv_vote.low.ab) ||
+				(cpas_core->axi_port[i].applied_bw.drv_vote.low.ib)) {
+				CAM_ERR(CAM_CPAS,
+					"port:%s Non zero DRV applied BW high[%llu %llu] low[%llu %llu]",
+					cpas_core->axi_port[i].axi_port_name,
+					cpas_core->axi_port[i].applied_bw.drv_vote.high.ab,
+					cpas_core->axi_port[i].applied_bw.drv_vote.high.ib,
+					cpas_core->axi_port[i].applied_bw.drv_vote.low.ab,
+					cpas_core->axi_port[i].applied_bw.drv_vote.low.ib);
+				return -EINVAL;
+			}
+		} else {
+			if (cpas_core->axi_port[i].bus_client.common_data.is_drv_port)
+				continue;
+
+			if ((cpas_core->axi_port[i].applied_bw.hlos_vote.ab) ||
+				(cpas_core->axi_port[i].applied_bw.hlos_vote.ib)) {
+				CAM_ERR(CAM_CPAS,
+					"port:%s Non zero HLOS applied BW [%llu %llu]",
+					cpas_core->axi_port[i].axi_port_name,
+					cpas_core->axi_port[i].applied_bw.hlos_vote.ab,
+					cpas_core->axi_port[i].applied_bw.hlos_vote.ib);
+				return -EINVAL;
+			}
+		}
+	}
+
+	return 0;
+}
+
 static int cam_cpas_hw_stop(void *hw_priv, void *stop_args,
 	uint32_t arg_size)
 {
@@ -2286,6 +2327,19 @@ static int cam_cpas_hw_stop(void *hw_priv, void *stop_args,
 		goto done;
 	}
 
+	rc = cam_cpas_util_create_vote_all_paths(cpas_client, &axi_vote);
+	if (rc) {
+		CAM_ERR(CAM_CPAS, "Unable to create per path votes rc: %d", rc);
+		goto done;
+	}
+
+	cam_cpas_dump_axi_vote_info(cpas_client, "CPAS Stop Vote", &axi_vote);
+
+	rc = cam_cpas_util_apply_client_axi_vote(cpas_hw, cpas_client, &axi_vote,
+		CAM_CPAS_APPLY_TYPE_STOP);
+	if (rc)
+		goto done;
+
 	cpas_client->started = false;
 	cpas_core->streamon_clients--;
 
@@ -2350,6 +2404,14 @@ static int cam_cpas_hw_stop(void *hw_priv, void *stop_args,
 				client_indx, cpas_client->data.identifier,
 				cpas_client->data.cell_index, count);
 
+		rc = cam_cpas_util_apply_default_axi_vote(cpas_hw, false);
+		if (rc)
+			CAM_ERR(CAM_CPAS, "Failed in power off default vote rc: %d", rc);
+
+		rc = cam_cpas_util_validate_stop_bw(soc_private, cpas_core);
+		if (rc)
+			CAM_ERR(CAM_CPAS, "Invalid applied bw at stop rc: %d", rc);
+
 		cpas_hw->hw_state = CAM_HW_STATE_POWER_DOWN;
 	}
 
@@ -2360,22 +2422,6 @@ static int cam_cpas_hw_stop(void *hw_priv, void *stop_args,
 	if (rc)
 		goto done;
 
-	rc = cam_cpas_util_create_vote_all_paths(cpas_client, &axi_vote);
-	if (rc) {
-		CAM_ERR(CAM_CPAS, "Unable to create per path votes rc: %d", rc);
-		goto done;
-	}
-
-	cam_cpas_dump_axi_vote_info(cpas_client, "CPAS Stop Vote", &axi_vote);
-
-	rc = cam_cpas_util_apply_client_axi_vote(cpas_hw, cpas_client, &axi_vote,
-		CAM_CPAS_APPLY_TYPE_STOP);
-	if (rc)
-		goto done;
-
-	if (cpas_core->streamon_clients == 0)
-		rc = cam_cpas_util_apply_default_axi_vote(cpas_hw, false);
-
 done:
 	mutex_unlock(&cpas_core->client_mutex[client_indx]);
 	mutex_unlock(&cpas_hw->hw_mutex);

+ 23 - 0
drivers/cam_cpas/cam_cpas_soc.c

@@ -24,6 +24,29 @@ module_param(cpas_dump, uint, 0644);
 
 #define CAM_ICP_CLK_NAME "cam_icp_clk"
 
+void cam_cpas_dump_tree_vote_info(const struct cam_cpas_tree_node *tree_node,
+	const char *identifier, int drv_voting_idx)
+{
+	if (!cpas_dump)
+		return;
+
+	if (tree_node->bw_info[drv_voting_idx].vote_type == CAM_CPAS_VOTE_TYPE_DRV)
+		CAM_INFO(CAM_PERF,
+			"%s node:%s lvl:%d drv_idx:%d DRV BW camnoc[%llu] ab[%llu %llu] ib[%llu %llu]",
+			identifier, tree_node->node_name, tree_node->level_idx, drv_voting_idx,
+			tree_node->camnoc_bw, tree_node->bw_info[drv_voting_idx].drv_vote.high.ab,
+			tree_node->bw_info[drv_voting_idx].drv_vote.low.ab,
+			tree_node->bw_info[drv_voting_idx].drv_vote.high.ib,
+			tree_node->bw_info[drv_voting_idx].drv_vote.low.ib);
+	else
+		CAM_INFO(CAM_PERF,
+			"%s node:%s lvl:%d drv_idx:%d HLOS BW camnoc[%llu] ab[%llu] ib[%llu]",
+			identifier, tree_node->node_name, tree_node->level_idx, drv_voting_idx,
+			tree_node->camnoc_bw, tree_node->bw_info[drv_voting_idx].hlos_vote.ab,
+			tree_node->bw_info[drv_voting_idx].hlos_vote.ib);
+
+}
+
 void cam_cpas_dump_axi_vote_info(
 	const struct cam_cpas_client *cpas_client,
 	const char *identifier,

+ 3 - 0
drivers/cam_cpas/cam_cpas_soc.h

@@ -244,6 +244,9 @@ struct cam_cpas_private_soc {
 	struct cam_cpas_domain_id_support_clks *domain_id_clks;
 };
 
+void cam_cpas_dump_tree_vote_info(const struct cam_cpas_tree_node *tree_node,
+	const char *identifier, int drv_voting_idx);
+
 void cam_cpas_util_debug_parse_data(struct cam_cpas_private_soc *soc_private);
 void cam_cpas_dump_axi_vote_info(
 	const struct cam_cpas_client *cpas_client,