msm: camera: cpas: Add support to control camnoc axi clk

Based on consolidated uncompressed bw from all clients,
calculate corresponding camnoc axi clk frequency and
set the freq using clk api.

Change-Id: Ia458e75687f9ea5063f3b9590a8d057c7f98f2e1
Signed-off-by: Pavan Kumar Chilamkurthi <pchilamk@codeaurora.org>
diff --git a/Documentation/devicetree/bindings/media/video/msm-cam-cpas.txt b/Documentation/devicetree/bindings/media/video/msm-cam-cpas.txt
index 4b16103..86d49e3 100644
--- a/Documentation/devicetree/bindings/media/video/msm-cam-cpas.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-cam-cpas.txt
@@ -108,6 +108,23 @@
   Definition: List of strings corresponds clock-rates levels.
   Supported strings: minsvs, lowsvs, svs, svs_l1, nominal, turbo.
 
+- control-camnoc-axi-clk
+  Usage: optional
+  Value type: <empty>
+  Definition: Bool property specifying whether to control camnoc axi
+              clock from cpas driver.
+
+- camnoc-bus-width
+  Usage: required if control-camnoc-axi-clk is enabled
+  Value type: <u32>
+  Definition: camnoc bus width.
+
+- camnoc-axi-clk-bw-margin-perc
+  Usage: optional
+  Value type: <u32>
+  Definition: Percentage value to be added to camnoc bw while calculating
+              camnoc axi clock frequency.
+
 - qcom,msm-bus,name
 - qcom,msm-bus,num-cases
 - qcom,msm-bus,num-paths
@@ -204,6 +221,9 @@
 		src-clock-name = "slow_ahb_clk_src";
 		clock-rates = <0 0 0 0 80000000 0>;
 		clock-cntl-level = "turbo";
+		control-camnoc-axi-clk;
+		camnoc-bus-width = <32>;
+		camnoc-axi-clk-bw-margin-perc = <10>;
 		qcom,msm-bus,name = "cam_ahb";
 		qcom,msm-bus,num-cases = <4>;
 		qcom,msm-bus,num-paths = <1>;
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.c b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.c
index b04bc23..e7de207 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.c
+++ b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.c
@@ -543,10 +543,71 @@
 	return rc;
 }
 
-static int cam_cpas_util_apply_client_axi_vote(
-	struct cam_cpas *cpas_core, struct cam_cpas_private_soc *soc_private,
-	struct cam_cpas_client *cpas_client, struct cam_axi_vote *axi_vote)
+static int cam_cpas_util_set_camnoc_axi_clk_rate(
+	struct cam_hw_info *cpas_hw)
 {
+	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 rc = 0;
+
+	CAM_DBG(CAM_CPAS, "control_camnoc_axi_clk=%d",
+		soc_private->control_camnoc_axi_clk);
+
+	if (soc_private->control_camnoc_axi_clk) {
+		struct cam_hw_soc_info *soc_info = &cpas_hw->soc_info;
+		struct cam_cpas_axi_port *curr_axi_port = NULL;
+		struct cam_cpas_axi_port *temp_axi_port = NULL;
+		uint64_t required_camnoc_bw = 0;
+		int32_t clk_rate = 0;
+
+		list_for_each_entry_safe(curr_axi_port, temp_axi_port,
+			&cpas_core->axi_ports_list_head, sibling_port) {
+
+			if (curr_axi_port->consolidated_axi_vote.uncompressed_bw
+				> required_camnoc_bw)
+				required_camnoc_bw = curr_axi_port->
+					consolidated_axi_vote.uncompressed_bw;
+
+			CAM_DBG(CAM_CPAS, "[%s] : curr=%llu, overal=%llu",
+				curr_axi_port->axi_port_name,
+				curr_axi_port->consolidated_axi_vote.
+				uncompressed_bw,
+				required_camnoc_bw);
+		}
+
+		required_camnoc_bw += (required_camnoc_bw *
+			soc_private->camnoc_axi_clk_bw_margin) / 100;
+
+		if ((required_camnoc_bw > 0) &&
+			(required_camnoc_bw < CAM_CPAS_AXI_MIN_CAMNOC_IB_BW))
+			required_camnoc_bw = CAM_CPAS_AXI_MIN_CAMNOC_IB_BW;
+
+		clk_rate = required_camnoc_bw / soc_private->camnoc_bus_width;
+
+		CAM_DBG(CAM_CPAS, "Setting camnoc axi clk rate : %llu %d",
+			required_camnoc_bw, clk_rate);
+
+		rc = cam_soc_util_set_clk_rate(
+			soc_info->clk[soc_info->src_clk_idx],
+			soc_info->clk_name[soc_info->src_clk_idx],
+			clk_rate);
+		if (!rc)
+			CAM_ERR(CAM_CPAS,
+				"Failed in setting camnoc axi clk %llu %d %d",
+				required_camnoc_bw, clk_rate, rc);
+	}
+
+	return rc;
+}
+
+static int cam_cpas_util_apply_client_axi_vote(
+	struct cam_hw_info *cpas_hw,
+	struct cam_cpas_client *cpas_client,
+	struct cam_axi_vote *axi_vote)
+{
+	struct cam_cpas_private_soc *soc_private =
+		(struct cam_cpas_private_soc *) cpas_hw->soc_info.soc_private;
 	struct cam_cpas_client *curr_client;
 	struct cam_cpas_client *temp_client;
 	struct cam_axi_vote req_axi_vote = *axi_vote;
@@ -587,6 +648,9 @@
 	if ((!soc_private->axi_camnoc_based) && (mnoc_bw < camnoc_bw))
 		mnoc_bw = camnoc_bw;
 
+	axi_port->consolidated_axi_vote.compressed_bw = mnoc_bw;
+	axi_port->consolidated_axi_vote.uncompressed_bw = camnoc_bw;
+
 	CAM_DBG(CAM_CPAS,
 		"axi[(%d, %d),(%d, %d)] : camnoc_bw[%llu], mnoc_bw[%llu]",
 		axi_port->mnoc_bus.src, axi_port->mnoc_bus.dst,
@@ -613,6 +677,14 @@
 		}
 	}
 
+	mutex_unlock(&axi_port->lock);
+
+	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;
+
 unlock_axi_port:
 	mutex_unlock(&axi_port->lock);
 	return rc;
@@ -645,6 +717,7 @@
 	if (!CAM_CPAS_CLIENT_VALID(client_indx))
 		return -EINVAL;
 
+	mutex_lock(&cpas_hw->hw_mutex);
 	mutex_lock(&cpas_core->client_mutex[client_indx]);
 
 	if (!CAM_CPAS_CLIENT_STARTED(cpas_core, client_indx)) {
@@ -658,12 +731,12 @@
 		client_indx, axi_vote.compressed_bw,
 		axi_vote.uncompressed_bw);
 
-	rc = cam_cpas_util_apply_client_axi_vote(cpas_core,
-		cpas_hw->soc_info.soc_private,
+	rc = cam_cpas_util_apply_client_axi_vote(cpas_hw,
 		cpas_core->cpas_client[client_indx], &axi_vote);
 
 unlock_client:
 	mutex_unlock(&cpas_core->client_mutex[client_indx]);
+	mutex_unlock(&cpas_hw->hw_mutex);
 	return rc;
 }
 
@@ -897,8 +970,8 @@
 		"AXI client[%d] compressed_bw[%llu], uncompressed_bw[%llu]",
 		client_indx, axi_vote->compressed_bw,
 		axi_vote->uncompressed_bw);
-	rc = cam_cpas_util_apply_client_axi_vote(cpas_core,
-		cpas_hw->soc_info.soc_private, cpas_client, axi_vote);
+	rc = cam_cpas_util_apply_client_axi_vote(cpas_hw,
+		cpas_client, axi_vote);
 	if (rc)
 		goto done;
 
@@ -1040,8 +1113,8 @@
 
 	axi_vote.uncompressed_bw = 0;
 	axi_vote.compressed_bw = 0;
-	rc = cam_cpas_util_apply_client_axi_vote(cpas_core,
-		cpas_hw->soc_info.soc_private, cpas_client, &axi_vote);
+	rc = cam_cpas_util_apply_client_axi_vote(cpas_hw,
+		cpas_client, &axi_vote);
 
 done:
 	mutex_unlock(&cpas_core->client_mutex[client_indx]);
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.h b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.h
index 05840bb..2e660b1 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.h
+++ b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.h
@@ -145,6 +145,7 @@
  * @axi_port_node: Node representing this AXI Port
  * @axi_port_mnoc_node: Node representing mnoc in this AXI Port
  * @axi_port_camnoc_node: Node representing camnoc in this AXI Port
+ * @consolidated_axi_vote: Consolidated axi bw values for this AXI port
  *
  */
 struct cam_cpas_axi_port {
@@ -157,6 +158,7 @@
 	struct device_node *axi_port_node;
 	struct device_node *axi_port_mnoc_node;
 	struct device_node *axi_port_camnoc_node;
+	struct cam_axi_vote consolidated_axi_vote;
 };
 
 /**
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.c b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.c
index b18af0a..e30700d 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.c
+++ b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.c
@@ -93,6 +93,35 @@
 	soc_private->axi_camnoc_based = of_property_read_bool(of_node,
 		"client-bus-camnoc-based");
 
+	soc_private->control_camnoc_axi_clk = of_property_read_bool(of_node,
+		"control-camnoc-axi-clk");
+
+	if (soc_private->control_camnoc_axi_clk == true) {
+		rc = of_property_read_u32(of_node, "camnoc-bus-width",
+			&soc_private->camnoc_bus_width);
+		if (rc || (soc_private->camnoc_bus_width == 0)) {
+			CAM_ERR(CAM_CPAS, "Bus width not found rc=%d, %d",
+				rc, soc_private->camnoc_bus_width);
+			return rc;
+		}
+
+		rc = of_property_read_u32(of_node,
+			"camnoc-axi-clk-bw-margin-perc",
+			&soc_private->camnoc_axi_clk_bw_margin);
+
+		if (rc) {
+			/* this is not fatal, overwrite rc */
+			rc = 0;
+			soc_private->camnoc_axi_clk_bw_margin = 0;
+		}
+	}
+
+	CAM_DBG(CAM_CPAS,
+		"control_camnoc_axi_clk=%d, width=%d, margin=%d",
+		soc_private->control_camnoc_axi_clk,
+		soc_private->camnoc_bus_width,
+		soc_private->camnoc_axi_clk_bw_margin);
+
 	count = of_property_count_u32_elems(of_node, "vdd-corners");
 	if ((count > 0) && (count <= CAM_REGULATOR_LEVEL_MAX) &&
 		(of_property_count_strings(of_node, "vdd-corner-ahb-mapping") ==
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.h b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.h
index fe0187e..a0a9ae2 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.h
+++ b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.h
@@ -42,6 +42,10 @@
  * @axi_port_list_node : Node representing AXI Ports list
  * @num_vdd_ahb_mapping : Number of vdd to ahb level mapping supported
  * @vdd_ahb : AHB level mapping info for the supported vdd levels
+ * @control_camnoc_axi_clk : Whether CPAS driver need to set camnoc axi clk freq
+ * @camnoc_bus_width : CAMNOC Bus width
+ * @camnoc_axi_clk_bw_margin : BW Margin in percentage to add while calculating
+ *      camnoc axi clock
  *
  */
 struct cam_cpas_private_soc {
@@ -54,6 +58,9 @@
 	struct device_node *axi_port_list_node;
 	uint32_t num_vdd_ahb_mapping;
 	struct cam_cpas_vdd_ahb_mapping vdd_ahb[CAM_REGULATOR_LEVEL_MAX];
+	bool control_camnoc_axi_clk;
+	uint32_t camnoc_bus_width;
+	uint32_t camnoc_axi_clk_bw_margin;
 };
 
 int cam_cpas_soc_init_resources(struct cam_hw_soc_info *soc_info,