Loading Documentation/devicetree/bindings/media/video/msm-cam-cpas.txt +11 −0 Original line number Diff line number Diff line Loading @@ -104,6 +104,17 @@ First Level Node - CAM CPAS device Please refer Documentation/devicetree/bindings/arm/msm/msm_bus.txt for the properties above. - vdd-corners Usage: required Value type: <u32> Definition: List of vdd corners to map for ahb level. - vdd-corner-ahb-mapping Usage: required Value type: <string> Definition: List of ahb level strings corresponds to vdd-corners. Supported strings: suspend, svs, nominal, turbo - client-id-based Usage: required Value type: <empty> Loading drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.c +47 −8 Original line number Diff line number Diff line Loading @@ -628,9 +628,46 @@ static int cam_cpas_hw_update_axi_vote(struct cam_hw_info *cpas_hw, return rc; } static int cam_cpas_util_apply_client_ahb_vote(struct cam_cpas *cpas_core, static int cam_cpas_util_get_ahb_level(struct cam_hw_info *cpas_hw, struct device *dev, unsigned long freq, enum cam_vote_level *req_level) { struct cam_cpas_private_soc *soc_private = (struct cam_cpas_private_soc *) cpas_hw->soc_info.soc_private; struct dev_pm_opp *opp; unsigned int corner; enum cam_vote_level level = CAM_SVS_VOTE; unsigned long corner_freq = freq; int i; if (!dev || !req_level) { pr_err("Invalid params %pK, %pK\n", dev, req_level); return -EINVAL; } opp = dev_pm_opp_find_freq_ceil(dev, &corner_freq); if (IS_ERR(opp)) { pr_err("Error on OPP freq :%ld, %pK\n", corner_freq, opp); return -EINVAL; } corner = dev_pm_opp_get_voltage(opp); for (i = 0; i < soc_private->num_vdd_ahb_mapping; i++) if (corner == soc_private->vdd_ahb[i].vdd_corner) level = soc_private->vdd_ahb[i].ahb_level; CPAS_CDBG("From OPP table : freq=[%ld][%ld], corner=%d, level=%d\n", freq, corner_freq, corner, level); *req_level = level; return 0; } static int cam_cpas_util_apply_client_ahb_vote(struct cam_hw_info *cpas_hw, struct cam_cpas_client *cpas_client, struct cam_ahb_vote *ahb_vote) { struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info; struct cam_cpas_bus_client *ahb_bus_client = &cpas_core->ahb_bus_client; enum cam_vote_level required_level; enum cam_vote_level highest_level; Loading @@ -642,11 +679,13 @@ static int cam_cpas_util_apply_client_ahb_vote(struct cam_cpas *cpas_core, } if (ahb_vote->type == CAM_VOTE_DYNAMIC) { pr_err("Dynamic AHB vote not supported\n"); return -EINVAL; } rc = cam_cpas_util_get_ahb_level(cpas_hw, cpas_client->data.dev, ahb_vote->vote.freq, &required_level); if (rc) return rc; } else { required_level = ahb_vote->vote.level; } if (cpas_client->ahb_level == required_level) return 0; Loading Loading @@ -708,7 +747,7 @@ static int cam_cpas_hw_update_ahb_vote(struct cam_hw_info *cpas_hw, ahb_vote->vote.freq, cpas_core->cpas_client[client_indx]->ahb_level); rc = cam_cpas_util_apply_client_ahb_vote(cpas_core, rc = cam_cpas_util_apply_client_ahb_vote(cpas_hw, cpas_core->cpas_client[client_indx], ahb_vote); unlock_client: Loading Loading @@ -780,7 +819,7 @@ static int cam_cpas_hw_start(void *hw_priv, void *start_args, CPAS_CDBG("AHB :client[%d] type[%d], level[%d], applied[%d]\n", client_indx, ahb_vote->type, ahb_vote->vote.level, cpas_client->ahb_level); rc = cam_cpas_util_apply_client_ahb_vote(cpas_core, cpas_client, rc = cam_cpas_util_apply_client_ahb_vote(cpas_hw, cpas_client, ahb_vote); if (rc) goto done; Loading Loading @@ -892,7 +931,7 @@ static int cam_cpas_hw_stop(void *hw_priv, void *stop_args, ahb_vote.type = CAM_VOTE_ABSOLUTE; ahb_vote.vote.level = CAM_SUSPEND_VOTE; rc = cam_cpas_util_apply_client_ahb_vote(cpas_core, cpas_client, rc = cam_cpas_util_apply_client_ahb_vote(cpas_hw, cpas_client, &ahb_vote); if (rc) goto done; Loading drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.c +56 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,26 @@ #include "cam_cpas_hw.h" #include "cam_cpas_soc.h" static int cam_cpas_get_vote_level_from_string(const char *string, enum cam_vote_level *vote_level) { if (!vote_level || !string) return -EINVAL; if (strnstr("suspend", string, strlen(string))) *vote_level = CAM_SUSPEND_VOTE; else if (strnstr("svs", string, strlen(string))) *vote_level = CAM_SVS_VOTE; else if (strnstr("nominal", string, strlen(string))) *vote_level = CAM_NOMINAL_VOTE; else if (strnstr("turbo", string, strlen(string))) *vote_level = CAM_TURBO_VOTE; else *vote_level = CAM_SVS_VOTE; return 0; } int cam_cpas_get_custom_dt_info(struct platform_device *pdev, struct cam_cpas_private_soc *soc_private) { Loading Loading @@ -89,6 +109,42 @@ int cam_cpas_get_custom_dt_info(struct platform_device *pdev, soc_private->axi_camnoc_based = of_property_read_bool(of_node, "client-bus-camnoc-based"); 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") == count)) { const char *ahb_string; for (i = 0; i < count; i++) { rc = of_property_read_u32_index(of_node, "vdd-corners", i, &soc_private->vdd_ahb[i].vdd_corner); if (rc) { pr_err("vdd-corners failed at index=%d\n", i); return -ENODEV; } rc = of_property_read_string_index(of_node, "vdd-corner-ahb-mapping", i, &ahb_string); if (rc) { pr_err("no ahb-mapping at index=%d\n", i); return -ENODEV; } rc = cam_cpas_get_vote_level_from_string(ahb_string, &soc_private->vdd_ahb[i].ahb_level); if (rc) { pr_err("invalid ahb-string at index=%d\n", i); return -EINVAL; } CPAS_CDBG("Vdd-AHB mapping [%d] : [%d] [%s] [%d]\n", i, soc_private->vdd_ahb[i].vdd_corner, ahb_string, soc_private->vdd_ahb[i].ahb_level); } soc_private->num_vdd_ahb_mapping = count; } return 0; } Loading drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.h +17 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,19 @@ #include "cam_soc_util.h" #define CAM_CPAS_MAX_CLIENTS 20 #define CAM_REGULATOR_LEVEL_MAX 16 /** * struct cam_cpas_vdd_ahb_mapping : Voltage to ahb level mapping * * @vdd_corner : Voltage corner value * @ahb_level : AHB vote level corresponds to this vdd_corner * */ struct cam_cpas_vdd_ahb_mapping { unsigned int vdd_corner; enum cam_vote_level ahb_level; }; /** * struct cam_cpas_private_soc : CPAS private DT info Loading @@ -27,6 +40,8 @@ * @axi_camnoc_based: Whether AXi access is camnoc based * @client_axi_port_name: AXI Port name for each client * @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 * */ struct cam_cpas_private_soc { Loading @@ -37,6 +52,8 @@ struct cam_cpas_private_soc { bool axi_camnoc_based; const char *client_axi_port_name[CAM_CPAS_MAX_CLIENTS]; 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]; }; int cam_cpas_soc_init_resources(struct cam_hw_soc_info *soc_info, Loading Loading
Documentation/devicetree/bindings/media/video/msm-cam-cpas.txt +11 −0 Original line number Diff line number Diff line Loading @@ -104,6 +104,17 @@ First Level Node - CAM CPAS device Please refer Documentation/devicetree/bindings/arm/msm/msm_bus.txt for the properties above. - vdd-corners Usage: required Value type: <u32> Definition: List of vdd corners to map for ahb level. - vdd-corner-ahb-mapping Usage: required Value type: <string> Definition: List of ahb level strings corresponds to vdd-corners. Supported strings: suspend, svs, nominal, turbo - client-id-based Usage: required Value type: <empty> Loading
drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.c +47 −8 Original line number Diff line number Diff line Loading @@ -628,9 +628,46 @@ static int cam_cpas_hw_update_axi_vote(struct cam_hw_info *cpas_hw, return rc; } static int cam_cpas_util_apply_client_ahb_vote(struct cam_cpas *cpas_core, static int cam_cpas_util_get_ahb_level(struct cam_hw_info *cpas_hw, struct device *dev, unsigned long freq, enum cam_vote_level *req_level) { struct cam_cpas_private_soc *soc_private = (struct cam_cpas_private_soc *) cpas_hw->soc_info.soc_private; struct dev_pm_opp *opp; unsigned int corner; enum cam_vote_level level = CAM_SVS_VOTE; unsigned long corner_freq = freq; int i; if (!dev || !req_level) { pr_err("Invalid params %pK, %pK\n", dev, req_level); return -EINVAL; } opp = dev_pm_opp_find_freq_ceil(dev, &corner_freq); if (IS_ERR(opp)) { pr_err("Error on OPP freq :%ld, %pK\n", corner_freq, opp); return -EINVAL; } corner = dev_pm_opp_get_voltage(opp); for (i = 0; i < soc_private->num_vdd_ahb_mapping; i++) if (corner == soc_private->vdd_ahb[i].vdd_corner) level = soc_private->vdd_ahb[i].ahb_level; CPAS_CDBG("From OPP table : freq=[%ld][%ld], corner=%d, level=%d\n", freq, corner_freq, corner, level); *req_level = level; return 0; } static int cam_cpas_util_apply_client_ahb_vote(struct cam_hw_info *cpas_hw, struct cam_cpas_client *cpas_client, struct cam_ahb_vote *ahb_vote) { struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info; struct cam_cpas_bus_client *ahb_bus_client = &cpas_core->ahb_bus_client; enum cam_vote_level required_level; enum cam_vote_level highest_level; Loading @@ -642,11 +679,13 @@ static int cam_cpas_util_apply_client_ahb_vote(struct cam_cpas *cpas_core, } if (ahb_vote->type == CAM_VOTE_DYNAMIC) { pr_err("Dynamic AHB vote not supported\n"); return -EINVAL; } rc = cam_cpas_util_get_ahb_level(cpas_hw, cpas_client->data.dev, ahb_vote->vote.freq, &required_level); if (rc) return rc; } else { required_level = ahb_vote->vote.level; } if (cpas_client->ahb_level == required_level) return 0; Loading Loading @@ -708,7 +747,7 @@ static int cam_cpas_hw_update_ahb_vote(struct cam_hw_info *cpas_hw, ahb_vote->vote.freq, cpas_core->cpas_client[client_indx]->ahb_level); rc = cam_cpas_util_apply_client_ahb_vote(cpas_core, rc = cam_cpas_util_apply_client_ahb_vote(cpas_hw, cpas_core->cpas_client[client_indx], ahb_vote); unlock_client: Loading Loading @@ -780,7 +819,7 @@ static int cam_cpas_hw_start(void *hw_priv, void *start_args, CPAS_CDBG("AHB :client[%d] type[%d], level[%d], applied[%d]\n", client_indx, ahb_vote->type, ahb_vote->vote.level, cpas_client->ahb_level); rc = cam_cpas_util_apply_client_ahb_vote(cpas_core, cpas_client, rc = cam_cpas_util_apply_client_ahb_vote(cpas_hw, cpas_client, ahb_vote); if (rc) goto done; Loading Loading @@ -892,7 +931,7 @@ static int cam_cpas_hw_stop(void *hw_priv, void *stop_args, ahb_vote.type = CAM_VOTE_ABSOLUTE; ahb_vote.vote.level = CAM_SUSPEND_VOTE; rc = cam_cpas_util_apply_client_ahb_vote(cpas_core, cpas_client, rc = cam_cpas_util_apply_client_ahb_vote(cpas_hw, cpas_client, &ahb_vote); if (rc) goto done; Loading
drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.c +56 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,26 @@ #include "cam_cpas_hw.h" #include "cam_cpas_soc.h" static int cam_cpas_get_vote_level_from_string(const char *string, enum cam_vote_level *vote_level) { if (!vote_level || !string) return -EINVAL; if (strnstr("suspend", string, strlen(string))) *vote_level = CAM_SUSPEND_VOTE; else if (strnstr("svs", string, strlen(string))) *vote_level = CAM_SVS_VOTE; else if (strnstr("nominal", string, strlen(string))) *vote_level = CAM_NOMINAL_VOTE; else if (strnstr("turbo", string, strlen(string))) *vote_level = CAM_TURBO_VOTE; else *vote_level = CAM_SVS_VOTE; return 0; } int cam_cpas_get_custom_dt_info(struct platform_device *pdev, struct cam_cpas_private_soc *soc_private) { Loading Loading @@ -89,6 +109,42 @@ int cam_cpas_get_custom_dt_info(struct platform_device *pdev, soc_private->axi_camnoc_based = of_property_read_bool(of_node, "client-bus-camnoc-based"); 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") == count)) { const char *ahb_string; for (i = 0; i < count; i++) { rc = of_property_read_u32_index(of_node, "vdd-corners", i, &soc_private->vdd_ahb[i].vdd_corner); if (rc) { pr_err("vdd-corners failed at index=%d\n", i); return -ENODEV; } rc = of_property_read_string_index(of_node, "vdd-corner-ahb-mapping", i, &ahb_string); if (rc) { pr_err("no ahb-mapping at index=%d\n", i); return -ENODEV; } rc = cam_cpas_get_vote_level_from_string(ahb_string, &soc_private->vdd_ahb[i].ahb_level); if (rc) { pr_err("invalid ahb-string at index=%d\n", i); return -EINVAL; } CPAS_CDBG("Vdd-AHB mapping [%d] : [%d] [%s] [%d]\n", i, soc_private->vdd_ahb[i].vdd_corner, ahb_string, soc_private->vdd_ahb[i].ahb_level); } soc_private->num_vdd_ahb_mapping = count; } return 0; } Loading
drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.h +17 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,19 @@ #include "cam_soc_util.h" #define CAM_CPAS_MAX_CLIENTS 20 #define CAM_REGULATOR_LEVEL_MAX 16 /** * struct cam_cpas_vdd_ahb_mapping : Voltage to ahb level mapping * * @vdd_corner : Voltage corner value * @ahb_level : AHB vote level corresponds to this vdd_corner * */ struct cam_cpas_vdd_ahb_mapping { unsigned int vdd_corner; enum cam_vote_level ahb_level; }; /** * struct cam_cpas_private_soc : CPAS private DT info Loading @@ -27,6 +40,8 @@ * @axi_camnoc_based: Whether AXi access is camnoc based * @client_axi_port_name: AXI Port name for each client * @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 * */ struct cam_cpas_private_soc { Loading @@ -37,6 +52,8 @@ struct cam_cpas_private_soc { bool axi_camnoc_based; const char *client_axi_port_name[CAM_CPAS_MAX_CLIENTS]; 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]; }; int cam_cpas_soc_init_resources(struct cam_hw_soc_info *soc_info, Loading