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,