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

Commit 65a7bd54 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

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

parents ff4670a9 e210ec8d
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;
@@ -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;
+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,