Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e210ec8d authored by Pavan Kumar Chilamkurthi's avatar Pavan Kumar Chilamkurthi
Browse files

msm: camera: cpas: Add support for dynamic AHB voting using freq



Add support to facilite clients to vote for AHB using dynamic
frequency. Use this freq to determine corresponding vdd
corner level from dev's OPP table and determine corresponding
AHB level to vote.

CRs-Fixed: 2019539
Change-Id: I8ad646b406be54a0d77262470d98c2dddb470eac
Signed-off-by: default avatarPavan Kumar Chilamkurthi <pchilamk@codeaurora.org>
parent 823e0419
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -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>
+47 −8
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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:
@@ -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;
@@ -883,7 +922,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;
+56 −0
Original line number Diff line number Diff line
@@ -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)
{
@@ -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;
}

+17 −0
Original line number Diff line number Diff line
@@ -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
@@ -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 {
@@ -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,