Explorar o código

msm: camera: cpas: Add support for camnoc based voting

For some targets, it is needed to vote for camnoc clock
as there is no CAMNOC AXI clock source in camera domain.
Voting is done with the bus drivers and it calcultates
the clock rate.
This change adds support for camnoc based voting.

CRs-Fixed: 2571273
Change-Id: I38a4fa8d40892b6dfe7e925b6368eb259132615d
Signed-off-by: Gaurav Jindal <[email protected]>
Gaurav Jindal %!s(int64=5) %!d(string=hai) anos
pai
achega
1ed310412a

+ 142 - 24
drivers/cam_cpas/cam_cpas_hw.c

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
  */
 
 #include <linux/device.h>
@@ -177,6 +177,12 @@ static int cam_cpas_util_axi_cleanup(struct cam_cpas *cpas_core,
 		return -EINVAL;
 	}
 
+	if (cpas_core->num_camnoc_axi_ports > CAM_CPAS_MAX_AXI_PORTS) {
+		CAM_ERR(CAM_CPAS, "Invalid num_camnoc_axi_ports: %d",
+			cpas_core->num_camnoc_axi_ports);
+		return -EINVAL;
+	}
+
 	for (i = 0; i < cpas_core->num_axi_ports; i++) {
 		cam_cpas_util_unregister_bus_client(
 			&cpas_core->axi_port[i].bus_client);
@@ -184,6 +190,13 @@ static int cam_cpas_util_axi_cleanup(struct cam_cpas *cpas_core,
 		cpas_core->axi_port[i].axi_port_node = NULL;
 	}
 
+	for (i = 0; i < cpas_core->num_camnoc_axi_ports; i++) {
+		cam_cpas_util_unregister_bus_client(
+			&cpas_core->camnoc_axi_port[i].bus_client);
+		of_node_put(cpas_core->camnoc_axi_port[i].axi_port_node);
+		cpas_core->camnoc_axi_port[i].axi_port_node = NULL;
+	}
+
 	return 0;
 }
 
@@ -192,6 +205,7 @@ static int cam_cpas_util_axi_setup(struct cam_cpas *cpas_core,
 {
 	int i = 0, rc = 0;
 	struct device_node *axi_port_mnoc_node = NULL;
+	struct device_node *axi_port_camnoc_node = NULL;
 
 	if (cpas_core->num_axi_ports > CAM_CPAS_MAX_AXI_PORTS) {
 		CAM_ERR(CAM_CPAS, "Invalid num_axi_ports: %d",
@@ -206,6 +220,15 @@ static int cam_cpas_util_axi_setup(struct cam_cpas *cpas_core,
 		if (rc)
 			goto bus_register_fail;
 	}
+	for (i = 0; i < cpas_core->num_camnoc_axi_ports; i++) {
+		axi_port_camnoc_node =
+			cpas_core->camnoc_axi_port[i].axi_port_node;
+		rc = cam_cpas_util_register_bus_client(soc_info,
+			axi_port_camnoc_node,
+			&cpas_core->camnoc_axi_port[i].bus_client);
+		if (rc)
+			goto bus_register_fail;
+	}
 
 	return 0;
 bus_register_fail:
@@ -543,6 +566,99 @@ static int cam_cpas_axi_consolidate_path_votes(
 	return rc;
 }
 
+static int cam_cpas_update_axi_vote_bw(
+	struct cam_hw_info *cpas_hw,
+	struct cam_cpas_tree_node *cpas_tree_node,
+	bool   *mnoc_axi_port_updated,
+	bool   *camnoc_axi_port_updated)
+{
+	struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info;
+	struct cam_cpas_private_soc *soc_private =
+		(struct cam_cpas_private_soc *) cpas_hw->soc_info.soc_private;
+
+	if (cpas_tree_node->axi_port_idx >= CAM_CPAS_MAX_AXI_PORTS) {
+		CAM_ERR(CAM_CPAS, "Invalid axi_port_idx: %d",
+			cpas_tree_node->axi_port_idx);
+		return -EINVAL;
+	}
+
+	cpas_core->axi_port[cpas_tree_node->axi_port_idx].ab_bw =
+		cpas_tree_node->mnoc_ab_bw;
+	cpas_core->axi_port[cpas_tree_node->axi_port_idx].ib_bw =
+		cpas_tree_node->mnoc_ib_bw;
+	mnoc_axi_port_updated[cpas_tree_node->axi_port_idx] = true;
+
+	if (soc_private->control_camnoc_axi_clk)
+		return 0;
+
+	cpas_core->camnoc_axi_port[cpas_tree_node->axi_port_idx].camnoc_bw =
+		cpas_tree_node->camnoc_bw;
+	camnoc_axi_port_updated[cpas_tree_node->camnoc_axi_port_idx] = true;
+	return 0;
+}
+
+static int cam_cpas_camnoc_set_vote_axi_clk_rate(
+	struct cam_hw_info *cpas_hw,
+	bool   *camnoc_axi_port_updated)
+{
+	struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info;
+	struct cam_cpas_private_soc *soc_private =
+		(struct cam_cpas_private_soc *) cpas_hw->soc_info.soc_private;
+	int i;
+	int rc = 0;
+	struct cam_cpas_axi_port *camnoc_axi_port = NULL;
+	uint64_t camnoc_bw;
+
+	if (soc_private->control_camnoc_axi_clk) {
+		rc = cam_cpas_util_set_camnoc_axi_clk_rate(cpas_hw);
+		if (rc)
+			CAM_ERR(CAM_CPAS,
+				"Failed in setting axi clk rate rc=%d", rc);
+		return rc;
+	}
+
+	/* Below code is executed if we just vote and do not set the clk rate
+	 * for camnoc
+	 */
+
+	if (cpas_core->num_camnoc_axi_ports > CAM_CPAS_MAX_AXI_PORTS) {
+		CAM_ERR(CAM_CPAS, "Invalid num_camnoc_axi_ports: %d",
+			cpas_core->num_camnoc_axi_ports);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < cpas_core->num_camnoc_axi_ports; i++) {
+		if (camnoc_axi_port_updated[i])
+			camnoc_axi_port = &cpas_core->camnoc_axi_port[i];
+		else
+			continue;
+
+		CAM_DBG(CAM_PERF, "Port[%s] : camnoc_bw=%lld",
+			camnoc_axi_port->axi_port_name,
+			camnoc_axi_port->camnoc_bw);
+
+		if (camnoc_axi_port->camnoc_bw)
+			camnoc_bw = camnoc_axi_port->camnoc_bw;
+		else
+			camnoc_bw = camnoc_axi_port->additional_bw;
+
+		rc = cam_cpas_util_vote_bus_client_bw(
+			&camnoc_axi_port->bus_client,
+			0, camnoc_bw, true);
+
+		CAM_DBG(CAM_CPAS,
+			"camnoc vote camnoc_bw[%llu] rc=%d %s",
+			camnoc_bw, rc, camnoc_axi_port->axi_port_name);
+		if (rc) {
+			CAM_ERR(CAM_CPAS,
+				"Failed in camnoc vote camnoc_bw[%llu] rc=%d",
+				camnoc_bw, rc);
+			break;
+		}
+	}
+	return rc;
+}
+
 static int cam_cpas_util_apply_client_axi_vote(
 	struct cam_hw_info *cpas_hw,
 	struct cam_cpas_client *cpas_client,
@@ -550,12 +666,13 @@ static int cam_cpas_util_apply_client_axi_vote(
 {
 	struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info;
 	struct cam_axi_vote *con_axi_vote = NULL;
-	struct cam_cpas_axi_port *axi_port = NULL;
+	struct cam_cpas_axi_port *mnoc_axi_port = NULL;
 	struct cam_cpas_tree_node *curr_tree_node = NULL;
 	struct cam_cpas_tree_node *par_tree_node = NULL;
 	uint32_t transac_type;
 	uint32_t path_data_type;
-	bool axi_port_updated[CAM_CPAS_MAX_AXI_PORTS] = {false};
+	bool mnoc_axi_port_updated[CAM_CPAS_MAX_AXI_PORTS] = {false};
+	bool camnoc_axi_port_updated[CAM_CPAS_MAX_AXI_PORTS] = {false};
 	uint64_t mnoc_ab_bw = 0, mnoc_ib_bw = 0,
 		curr_camnoc_old = 0, curr_mnoc_ab_old = 0, curr_mnoc_ib_old = 0,
 		par_camnoc_old = 0, par_mnoc_ab_old = 0, par_mnoc_ib_old = 0;
@@ -578,7 +695,7 @@ static int cam_cpas_util_apply_client_axi_vote(
 				cpas_core->axi_port[i].additional_bw -=
 					CAM_CPAS_DEFAULT_AXI_BW;
 			}
-			axi_port_updated[i] = true;
+			mnoc_axi_port_updated[i] = true;
 		}
 		goto vote_start_clients;
 	}
@@ -668,15 +785,15 @@ static int cam_cpas_util_apply_client_axi_vote(
 					rc = -EINVAL;
 					goto unlock_tree;
 				}
-
-				cpas_core->axi_port
-				[par_tree_node->axi_port_idx].ab_bw =
-				par_tree_node->mnoc_ab_bw;
-				cpas_core->axi_port
-				[par_tree_node->axi_port_idx].ib_bw =
-				par_tree_node->mnoc_ib_bw;
-				axi_port_updated[par_tree_node->axi_port_idx] =
-					true;
+				rc = cam_cpas_update_axi_vote_bw(cpas_hw,
+					par_tree_node,
+					mnoc_axi_port_updated,
+					camnoc_axi_port_updated);
+				if (rc) {
+					CAM_ERR(CAM_CPAS,
+						"Update Vote failed");
+					goto unlock_tree;
+				}
 			}
 
 			curr_tree_node = par_tree_node;
@@ -694,26 +811,27 @@ static int cam_cpas_util_apply_client_axi_vote(
 
 vote_start_clients:
 	for (i = 0; i < cpas_core->num_axi_ports; i++) {
-		if (axi_port_updated[i])
-			axi_port = &cpas_core->axi_port[i];
+		if (mnoc_axi_port_updated[i])
+			mnoc_axi_port = &cpas_core->axi_port[i];
 		else
 			continue;
 
 		CAM_DBG(CAM_PERF, "Port[%s] : ab=%lld ib=%lld additional=%lld",
-			axi_port->bus_client.common_data.name, axi_port->ab_bw,
-			axi_port->ib_bw, axi_port->additional_bw);
+			mnoc_axi_port->axi_port_name, mnoc_axi_port->ab_bw,
+			mnoc_axi_port->ib_bw, mnoc_axi_port->additional_bw);
 
-		if (axi_port->ab_bw)
-			mnoc_ab_bw = axi_port->ab_bw;
+		if (mnoc_axi_port->ab_bw)
+			mnoc_ab_bw = mnoc_axi_port->ab_bw;
 		else
-			mnoc_ab_bw = axi_port->additional_bw;
+			mnoc_ab_bw = mnoc_axi_port->additional_bw;
 
 		if (cpas_core->axi_port[i].ib_bw_voting_needed)
-			mnoc_ib_bw = axi_port->ib_bw;
+			mnoc_ib_bw = mnoc_axi_port->ib_bw;
 		else
 			mnoc_ib_bw = 0;
 
-		rc = cam_cpas_util_vote_bus_client_bw(&axi_port->bus_client,
+		rc = cam_cpas_util_vote_bus_client_bw(
+			&mnoc_axi_port->bus_client,
 			mnoc_ab_bw, mnoc_ib_bw, false);
 		if (rc) {
 			CAM_ERR(CAM_CPAS,
@@ -722,8 +840,8 @@ vote_start_clients:
 			goto unlock_tree;
 		}
 	}
-
-	rc = cam_cpas_util_set_camnoc_axi_clk_rate(cpas_hw);
+	rc = cam_cpas_camnoc_set_vote_axi_clk_rate(
+		cpas_hw, camnoc_axi_port_updated);
 	if (rc)
 		CAM_ERR(CAM_CPAS, "Failed in setting axi clk rate rc=%d", rc);
 

+ 9 - 1
drivers/cam_cpas/cam_cpas_hw.h

@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
  */
 
 #ifndef _CAM_CPAS_HW_H_
@@ -136,19 +136,23 @@ struct cam_cpas_bus_client {
 /**
  * struct cam_cpas_axi_port : AXI port information
  *
+ * @axi_port_name: Name of this AXI port
  * @bus_client: bus client info for this port
  * @ib_bw_voting_needed: if this port can update ib bw dynamically
  * @axi_port_node: Node representing AXI Port info in device tree
  * @ab_bw: AB bw value for this port
  * @ib_bw: IB bw value for this port
+ * @camnoc_bw: CAMNOC bw value for this port
  * @additional_bw: Additional bandwidth to cover non-hw cpas clients
  */
 struct cam_cpas_axi_port {
+	const char *axi_port_name;
 	struct cam_cpas_bus_client bus_client;
 	bool ib_bw_voting_needed;
 	struct device_node *axi_port_node;
 	uint64_t ab_bw;
 	uint64_t ib_bw;
+	uint64_t camnoc_bw;
 	uint64_t additional_bw;
 };
 
@@ -161,11 +165,13 @@ struct cam_cpas_axi_port {
  * @tree_lock: Mutex lock for accessing CPAS node tree
  * @num_clients: Total number of clients that CPAS supports
  * @num_axi_ports: Total number of axi ports found in device tree
+ * @num_camnoc_axi_ports: Total number of camnoc axi ports found in device tree
  * @registered_clients: Number of Clients registered currently
  * @streamon_clients: Number of Clients that are in start state currently
  * @regbase_index: Register base indices for CPAS register base IDs
  * @ahb_bus_client: AHB Bus client info
  * @axi_port: AXI port info for a specific axi index
+ * @camnoc_axi_port: CAMNOC AXI port info for a specific camnoc axi index
  * @internal_ops: CPAS HW internal ops
  * @work_queue: Work queue handle
  * @irq_count: atomic irq count
@@ -180,11 +186,13 @@ struct cam_cpas {
 	struct mutex tree_lock;
 	uint32_t num_clients;
 	uint32_t num_axi_ports;
+	uint32_t num_camnoc_axi_ports;
 	uint32_t registered_clients;
 	uint32_t streamon_clients;
 	int32_t regbase_index[CAM_CPAS_REG_MAX];
 	struct cam_cpas_bus_client ahb_bus_client;
 	struct cam_cpas_axi_port axi_port[CAM_CPAS_MAX_AXI_PORTS];
+	struct cam_cpas_axi_port camnoc_axi_port[CAM_CPAS_MAX_AXI_PORTS];
 	struct cam_cpas_internal_ops internal_ops;
 	struct workqueue_struct *work_queue;
 	atomic_t irq_count;

+ 54 - 2
drivers/cam_cpas/cam_cpas_soc.c

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
  */
 
 #include <linux/device.h>
@@ -134,6 +134,47 @@ static int cam_cpas_util_path_type_to_idx(uint32_t *path_data_type)
 	return 0;
 }
 
+static int cam_cpas_update_camnoc_node(struct cam_cpas *cpas_core,
+	struct device_node *curr_node,
+	struct cam_cpas_tree_node *cpas_node_ptr,
+	int *camnoc_idx)
+
+{
+	struct device_node *camnoc_node;
+	int rc;
+
+	camnoc_node = of_find_node_by_name(curr_node,
+			"qcom,axi-port-camnoc");
+	if (camnoc_node) {
+
+		if (*camnoc_idx >=
+			CAM_CPAS_MAX_AXI_PORTS) {
+			CAM_ERR(CAM_CPAS, "CAMNOC axi index overshoot %d",
+				*camnoc_idx);
+			return -EINVAL;
+		}
+
+		cpas_core->camnoc_axi_port[*camnoc_idx]
+			.axi_port_node = camnoc_node;
+		rc = of_property_read_string(
+			curr_node,
+			"qcom,axi-port-name",
+			&cpas_core->camnoc_axi_port[*camnoc_idx]
+			.axi_port_name);
+
+		if (rc) {
+			CAM_ERR(CAM_CPAS,
+				"fail to read camnoc-port-name rc=%d",
+				rc);
+			return rc;
+		}
+		cpas_node_ptr->camnoc_axi_port_idx = *camnoc_idx;
+		cpas_core->num_camnoc_axi_ports++;
+		(*camnoc_idx)++;
+	}
+	return 0;
+}
+
 static int cam_cpas_parse_node_tree(struct cam_cpas *cpas_core,
 	struct device_node *of_node, struct cam_cpas_private_soc *soc_private)
 {
@@ -142,7 +183,7 @@ static int cam_cpas_parse_node_tree(struct cam_cpas *cpas_core,
 	struct device_node *curr_node;
 	struct device_node *parent_node;
 	struct device_node *mnoc_node;
-	int mnoc_idx = 0;
+	int mnoc_idx = 0, camnoc_idx = 0;
 	uint32_t path_idx;
 	bool camnoc_max_needed = false;
 	struct cam_cpas_tree_node *curr_node_ptr = NULL;
@@ -311,6 +352,17 @@ static int cam_cpas_parse_node_tree(struct cam_cpas *cpas_core,
 				cpas_core->num_axi_ports++;
 			}
 
+			if (!soc_private->control_camnoc_axi_clk) {
+				rc = cam_cpas_update_camnoc_node(
+					cpas_core, curr_node, curr_node_ptr,
+					&camnoc_idx);
+				if (rc) {
+					CAM_ERR(CAM_CPAS,
+						"Parse Camnoc port fail");
+					return rc;
+				}
+			}
+
 			rc = of_property_read_string(curr_node,
 				"client-name", &client_name);
 			if (!rc) {

+ 3 - 1
drivers/cam_cpas/cam_cpas_soc.h

@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
  */
 
 #ifndef _CAM_CPAS_SOC_H_
@@ -30,6 +30,7 @@ struct cam_cpas_vdd_ahb_mapping {
  * @cell_idx: Index to identify node from device tree and its parent
  * @level_idx: Index to identify at what level the node is present
  * @axi_port_idx: Index to identify which axi port to vote the consolidated bw
+ * @camnoc_axi_port_idx: Index to find which axi port to vote consolidated bw
  * @path_data_type: Traffic type info from device tree (ife-vid, ife-disp etc)
  * @path_trans_type: Transaction type info from device tree (rd, wr)
  * @merge_type: Traffic merge type (calculation info) from device tree
@@ -53,6 +54,7 @@ struct cam_cpas_tree_node {
 	uint32_t cell_idx;
 	uint32_t level_idx;
 	int axi_port_idx;
+	int camnoc_axi_port_idx;
 	const char *node_name;
 	uint32_t path_data_type;
 	uint32_t path_trans_type;