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

Commit c37f48cf authored by Wyes Karny's avatar Wyes Karny Committed by Gerrit - the friendly Code Review server
Browse files

msm: camera: cpas: Add fuse MP limit support



If MP limit fuse feature is enabled in DTSI then
read the fuse register to get information about maximum
allowed resolution. According to that restrict CSID acquire.
Also if only DTSI based max_width is enabled then restrict acquire
with maximum allowed width.

CRs-Fixed: 2785460
Depends-on: 3349044
Change-Id: If861289aebe2921183276780a086dfa1b1d5f372
Signed-off-by: default avatarWyes Karny <wkarny@codeaurora.org>
parent 6f7608c0
Loading
Loading
Loading
Loading
+34 −6
Original line number Diff line number Diff line
@@ -119,28 +119,56 @@ const char *cam_cpas_axi_util_trans_type_to_string(
}
EXPORT_SYMBOL(cam_cpas_axi_util_trans_type_to_string);

int cam_cpas_is_feature_supported(uint32_t flag)
bool cam_cpas_is_feature_supported(uint32_t flag, uint32_t hw_map,
	uint32_t *fuse_val)
{
	struct cam_hw_info *cpas_hw = NULL;
	struct cam_cpas_private_soc *soc_private = NULL;
	uint32_t feature_mask;
	bool supported = true;
	int32_t i;

	if (!CAM_CPAS_INTF_INITIALIZED()) {
		CAM_ERR(CAM_CPAS, "cpas intf not initialized");
		return -ENODEV;
		return false;
	}

	cpas_hw = (struct cam_hw_info *) g_cpas_intf->hw_intf->hw_priv;
	soc_private =
		(struct cam_cpas_private_soc *)cpas_hw->soc_info.soc_private;
	feature_mask = soc_private->feature_mask;

	if (flag >= CAM_CPAS_FUSE_FEATURE_MAX) {
		CAM_ERR(CAM_CPAS, "Unknown feature flag %x", flag);
		return -EINVAL;
		return false;
	}

	for (i = 0; i < soc_private->num_feature_info; i++)
		if (soc_private->feature_info[i].feature == flag)
			break;

	if (i == soc_private->num_feature_info) {
		CAM_INFO(CAM_CPAS, "Feature not found, no of featues: %d",
			soc_private->num_feature_info);
		goto end;
	}

	if (soc_private->feature_info[i].type == CAM_CPAS_FEATURE_TYPE_DISABLE
		|| (soc_private->feature_info[i].type ==
		CAM_CPAS_FEATURE_TYPE_ENABLE)) {
		if ((soc_private->feature_info[i].hw_map & hw_map) == hw_map)
			supported = soc_private->feature_info[i].enable;
		else
			supported = !soc_private->feature_info[i].enable;
	} else {
		if (!fuse_val) {
			CAM_ERR(CAM_CPAS,
				"Invalid arg fuse_val");
		} else {
			*fuse_val = soc_private->feature_info[i].value;
		}
	}

	return feature_mask & flag ? 1 : 0;
end:
	return supported;
}
EXPORT_SYMBOL(cam_cpas_is_feature_supported);

+125 −59
Original line number Diff line number Diff line
@@ -466,87 +466,155 @@ static int cam_cpas_parse_node_tree(struct cam_cpas *cpas_core,
	return 0;
}

int cam_cpas_get_hw_fuse(struct platform_device *pdev,


int cam_cpas_get_hw_features(struct platform_device *pdev,
	struct cam_cpas_private_soc *soc_private)
{
	struct device_node *of_node;
	void *fuse;
	uint32_t fuse_addr, fuse_bit;
	uint32_t fuse_val = 0, feature_bit_pos;
	int count = 0, i = 0;

	memset(&soc_private->fuse_info, 0, sizeof(soc_private->fuse_info));
	uint32_t fuse_addr, fuse_mask, fuse_shift;
	uint32_t val = 0, fuse_val = 0, feature;
	uint32_t enable_type = 0, hw_map = 0;
	int count = 0, i = 0, j = 0,  num_feature = 0, num_fuse = 0;
	struct cam_cpas_feature_info *feature_info;

	of_node = pdev->dev.of_node;
	count = of_property_count_u32_elems(of_node, "cam_hw_fuse");

	CAM_DBG(CAM_CPAS, "fuse info elements count %d", count);

	if (count <= 0) {
		CAM_INFO(CAM_CPAS, "no or invalid fuse enrties %d", count);
		return 0;
	} else if (count%3 != 0) {
		CAM_INFO(CAM_CPAS, "fuse entries should be multiple of 3 %d",
		CAM_INFO(CAM_CPAS, "No or invalid fuse entries count: %d",
			count);
		return -EINVAL;
		goto end;
	} else if (count%5 != 0) {
		CAM_INFO(CAM_CPAS, "fuse entries should be multiple of 5 %d",
			count);
		goto end;
	}

	for (i = 0; i < count; i = i + 3) {
	for (i = 0; (i + 5) <= count; i = i + 5) {
		of_property_read_u32_index(of_node, "cam_hw_fuse", i,
				&feature_bit_pos);
				&feature);
		of_property_read_u32_index(of_node, "cam_hw_fuse", i + 1,
				&fuse_addr);
		of_property_read_u32_index(of_node, "cam_hw_fuse", i + 2,
				&fuse_bit);
		CAM_INFO(CAM_CPAS, "feature_bit 0x%x addr 0x%x, bit %d",
			feature_bit_pos, fuse_addr, fuse_bit);
				&fuse_mask);
		of_property_read_u32_index(of_node, "cam_hw_fuse", i + 3,
				&enable_type);
		of_property_read_u32_index(of_node, "cam_hw_fuse", i + 4,
				&hw_map);
		val = ffs(fuse_mask);
		if (val == 0) {
			CAM_ERR(CAM_CPAS, "fuse_mask not valid 0x%x",
				fuse_mask);
			fuse_shift = 0;
		} else {
			fuse_shift = val - 1;
		}
		CAM_INFO(CAM_CPAS,
			"feature 0x%x addr 0x%x, mask 0x%x, shift 0x%x type 0x%x hw_map 0x%x",
			feature, fuse_addr, fuse_mask, fuse_shift, enable_type,
			hw_map);

		fuse = ioremap(fuse_addr, 4);
		if (fuse) {
			fuse_val = cam_io_r(fuse);
			soc_private->fuse_info.fuse_val[i].fuse_id = fuse_addr;
			soc_private->fuse_info.fuse_val[i].fuse_val = fuse_val;
			for (j = 0; (j < num_fuse) && (j < CAM_CPAS_FUSES_MAX);
				j++) {
				if (soc_private->fuse_info.fuse_val[j].fuse_id
					== fuse_addr)
					break;
			}
		CAM_INFO(CAM_CPAS, "fuse_addr 0x%x, fuse_val %x",
			if (j >= CAM_CPAS_FUSES_MAX) {
				CAM_ERR(CAM_CPAS,
					"fuse_info array overflow! %d", j);
				goto end;
			}
			if (j == num_fuse) {
				soc_private->fuse_info.fuse_val[j].fuse_id =
					fuse_addr;
				soc_private->fuse_info.fuse_val[j].fuse_val =
					fuse_val;
				CAM_INFO(CAM_CPAS,
					"fuse_addr 0x%x, fuse_val %x",
					fuse_addr, fuse_val);
		soc_private->fuse_info.num_fuses++;
		iounmap(fuse);
				num_fuse++;
			}
		} else {
			/* if fuse ioremap is failed, disable the feature */
			CAM_ERR(CAM_CPAS,
				"fuse register io remap failed fuse_addr:0x%x feature0x%x ",
				fuse_addr, feature);

	return 0;
			if (enable_type == CAM_CPAS_FEATURE_TYPE_ENABLE ||
				enable_type == CAM_CPAS_FEATURE_TYPE_DISABLE)
				fuse_val = (enable_type) ? ~fuse_mask :
					fuse_mask;
			else
				fuse_val = 0;
		}

int cam_cpas_get_hw_features(struct platform_device *pdev,
	struct cam_cpas_private_soc *soc_private)
{
	struct device_node *of_node;
	void *fuse;
	uint32_t fuse_addr, fuse_bit;
	uint32_t fuse_val = 0, feature_bit_pos;
	int count = 0, i = 0;

	of_node = pdev->dev.of_node;
	count = of_property_count_u32_elems(of_node, "cam_hw_fuse");
		if (num_feature >= CAM_CPAS_MAX_FUSE_FEATURE) {
			CAM_ERR(CAM_CPAS, "feature_info array overflow %d",
				num_feature);
			goto end;
		}

	for (i = 0; (i + 3) <= count; i = i + 3) {
		of_property_read_u32_index(of_node, "cam_hw_fuse", i,
				&feature_bit_pos);
		of_property_read_u32_index(of_node, "cam_hw_fuse", i + 1,
				&fuse_addr);
		of_property_read_u32_index(of_node, "cam_hw_fuse", i + 2,
				&fuse_bit);
		CAM_INFO(CAM_CPAS, "feature_bit 0x%x addr 0x%x, bit %d",
				feature_bit_pos, fuse_addr, fuse_bit);
		soc_private->feature_info[num_feature].feature =
			feature;
		soc_private->feature_info[num_feature].hw_map = hw_map;
		soc_private->feature_info[num_feature].type = enable_type;
		feature_info = &soc_private->feature_info[num_feature];

		fuse = ioremap(fuse_addr, 4);
		if (fuse) {
			fuse_val = cam_io_r(fuse);
			if (fuse_val & BIT(fuse_bit))
				soc_private->feature_mask |= feature_bit_pos;
		if (enable_type != CAM_CPAS_FEATURE_TYPE_VALUE) {
			if (enable_type == CAM_CPAS_FEATURE_TYPE_ENABLE) {
				/*
				 * fuse is for enable feature
				 * if fust bit is set means feature is enabled
				 * or HW is enabled
				 */
				if (fuse_val & fuse_mask)
					feature_info->enable = true;
				else
					feature_info->enable = false;
			} else if (enable_type ==
				CAM_CPAS_FEATURE_TYPE_DISABLE){
				/*
				 * fuse is for disable feature
				 * if fust bit is set means feature is disabled
				 * or HW is disabled
				 */
				if (fuse_val & fuse_mask)
					feature_info->enable = false;
				else
				soc_private->feature_mask &= ~feature_bit_pos;
					feature_info->enable = true;
			} else {
				CAM_ERR(CAM_CPAS,
					"Feature type not valid, type: %d",
					enable_type);
				goto end;
			}
		CAM_INFO(CAM_CPAS, "fuse %pK, fuse_val %x, feature_mask %x",
				fuse, fuse_val, soc_private->feature_mask);

			CAM_INFO(CAM_CPAS,
				"feature 0x%x enable=%d hw_map=0x%x",
				feature_info->feature, feature_info->enable,
				feature_info->hw_map);
		} else {
			feature_info->value =
				(fuse_val & fuse_mask) >> fuse_shift;
			CAM_INFO(CAM_CPAS,
				"feature 0x%x value=0x%x hw_map=0x%x",
				feature_info->feature, feature_info->value,
				feature_info->hw_map);
		}
		num_feature++;
		iounmap(fuse);
	}

end:
	soc_private->fuse_info.num_fuses = num_fuse;
	soc_private->num_feature_info = num_feature;
	return 0;
}

@@ -565,7 +633,6 @@ int cam_cpas_get_custom_dt_info(struct cam_hw_info *cpas_hw,
	}

	of_node = pdev->dev.of_node;
	soc_private->feature_mask = 0xFFFFFFFF;

	rc = of_property_read_string(of_node, "arch-compat",
		&soc_private->arch_compat);
@@ -576,7 +643,6 @@ int cam_cpas_get_custom_dt_info(struct cam_hw_info *cpas_hw,
	}

	cam_cpas_get_hw_features(pdev, soc_private);
	cam_cpas_get_hw_fuse(pdev, soc_private);

	soc_private->camnoc_axi_min_ib_bw = 0;
	rc = of_property_read_u64(of_node,
+22 −2
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@

#define CAM_REGULATOR_LEVEL_MAX 16
#define CAM_CPAS_MAX_TREE_NODES 50
#define CAM_CPAS_MAX_FUSE_FEATURE 10

/**
 * struct cam_cpas_vdd_ahb_mapping : Voltage to ahb level mapping
@@ -71,6 +72,23 @@ struct cam_cpas_tree_node {
	struct cam_cpas_tree_node *parent_node;
};

/**
 * struct cam_cpas_feature_info : CPAS fuse feature info
 * @feature: Identifier for feature
 * @type: Type of feature
 * @value: Fuse value
 * @enable: Feature enable or disable
 * @hw_map: Each bit position indicates if the hw_id for the feature
 */

struct cam_cpas_feature_info {
	uint32_t feature;
	uint32_t type;
	uint32_t value;
	bool enable;
	uint32_t hw_map;
};

/**
 * struct cam_cpas_private_soc : CPAS private DT info
 *
@@ -90,9 +108,10 @@ struct cam_cpas_tree_node {
 * @camnoc_axi_clk_bw_margin : BW Margin in percentage to add while calculating
 *      camnoc axi clock
 * @camnoc_axi_min_ib_bw: Min camnoc BW which varies based on target
 * @feature_mask: feature mask value for hw supported features
 * @fuse_info: fuse information
 * @rpmh_info: RPMH BCM info
 * @num_feature_info: number of feature_info entries
 * @feature_info: Structure for storing feature information
 */
struct cam_cpas_private_soc {
	const char *arch_compat;
@@ -109,9 +128,10 @@ struct cam_cpas_private_soc {
	uint32_t camnoc_bus_width;
	uint32_t camnoc_axi_clk_bw_margin;
	uint64_t camnoc_axi_min_ib_bw;
	uint32_t feature_mask;
	struct cam_cpas_fuse_info fuse_info;
	uint32_t rpmh_info[CAM_RPMH_BCM_INFO_MAX];
	uint32_t num_feature_info;
	struct cam_cpas_feature_info  feature_info[CAM_CPAS_MAX_FUSE_FEATURE];
};

void cam_cpas_util_debug_parse_data(struct cam_cpas_private_soc *soc_private);
+22 −2
Original line number Diff line number Diff line
@@ -37,6 +37,22 @@ enum cam_cpas_reg_base {
	CAM_CPAS_REG_MAX
};

/**
 * enum cam_cpas_hw_index  - Enum for identify HW index
 */
enum cam_cpas_hw_index {
	CAM_CPAS_HW_IDX_ANY = 0,
	CAM_CPAS_HW_IDX_0 = 1<<0,
	CAM_CPAS_HW_IDX_1 = 1<<1,
	CAM_CPAS_HW_IDX_2 = 1<<2,
	CAM_CPAS_HW_IDX_3 = 1<<3,
	CAM_CPAS_HW_IDX_4 = 1<<4,
	CAM_CPAS_HW_IDX_5 = 1<<5,
	CAM_CPAS_HW_IDX_6 = 1<<6,
	CAM_CPAS_HW_IDX_7 = 1<<7,
	CAM_CPAS_HW_IDX_MAX = 1<<8
};

/**
 * enum cam_cpas_camera_version Enum for Titan Camera Versions
 */
@@ -608,11 +624,15 @@ int cam_cpas_get_cpas_hw_version(
 *
 * @flag  : Camera hw features to check
 *
 * @hw_map : To indicate which HWs are supported
 *
 * @fule_val : Return fule value in case of value type feature
 *
 * @return 1 if feature is supported
 *
 */
int cam_cpas_is_feature_supported(
	uint32_t flag);
bool cam_cpas_is_feature_supported(uint32_t flag, uint32_t hw_map,
	uint32_t *fuse_val);

/**
 * cam_cpas_axi_util_path_type_to_string()
+133 −1
Original line number Diff line number Diff line
@@ -1002,6 +1002,116 @@ int cam_ife_csid_cid_reserve(struct cam_ife_csid_hw *csid_hw,
	return rc;
}

bool cam_ife_csid_is_resolution_supported_by_fuse(uint32_t width)
{
	bool supported = true;
	uint32_t hw_version, fuse_val = UINT_MAX;
	int rc = 0;

	rc = cam_cpas_get_cpas_hw_version(&hw_version);

	if (rc) {
		CAM_ERR(CAM_ISP, "Could not get CPAS version");
		return supported;
	}

	switch (hw_version) {
	case CAM_CPAS_TITAN_570_V200:
		cam_cpas_is_feature_supported(CAM_CPAS_MP_LIMIT_FUSE,
			CAM_CPAS_HW_IDX_ANY, &fuse_val);
		switch (fuse_val) {
		case 0x0:
			if (width > CAM_CSID_RESOLUTION_22MP_WIDTH) {
				CAM_ERR(CAM_ISP,
					"Resolution not supported required_width: %d max_supported_width: %d",
					width, CAM_CSID_RESOLUTION_22MP_WIDTH);
				supported = false;
			}
			break;
		case  0x1:
			if (width > CAM_CSID_RESOLUTION_25MP_WIDTH) {
				CAM_ERR(CAM_ISP,
					"Resolution not supported required_width: %d max_supported_width: %d",
					width, CAM_CSID_RESOLUTION_25MP_WIDTH);
				supported  = false;
			}
			break;
		case 0x2:
			if (width > CAM_CSID_RESOLUTION_28MP_WIDTH) {
				CAM_ERR(CAM_ISP,
					"Resolution not supported required_width: %d max_supported_width: %d",
					width, CAM_CSID_RESOLUTION_28MP_WIDTH);
				supported = false;
			}
			break;
		case UINT_MAX:
			CAM_WARN(CAM_ISP, "Fuse value not updated");
			break;
		default:
			CAM_ERR(CAM_ISP,
				"Fuse value not defined, fuse_val: 0x%x",
				fuse_val);
			supported = false;
			break;
		}
		break;
	default:
		break;
	}
	return supported;
}

bool cam_ife_csid_is_resolution_supported_by_dt(struct cam_ife_csid_hw *csid_hw,
	uint32_t width)
{
	bool supported = true;
	struct cam_hw_soc_info soc_info;
	struct cam_csid_soc_private *soc_private = NULL;

	if (!csid_hw || !csid_hw->hw_info) {
		CAM_ERR(CAM_ISP, "Argument parsing error!");
		supported = false;
		goto end;
	}

	soc_info = csid_hw->hw_info->soc_info;

	soc_private = (struct cam_csid_soc_private *)soc_info.soc_private;

	if (!soc_private) {
		CAM_ERR(CAM_ISP, "soc_private not found");
		supported = false;
		goto end;
	}

	if (soc_private->max_width_enabled) {
		if (width > soc_private->max_width) {
			CAM_ERR(CAM_ISP,
				"Resolution not supported required_width: %d max_supported_width: %d",
				width, soc_private->max_width);
			supported = false;
		}
	}
end:
	return supported;
}

bool cam_ife_csid_is_resolution_supported(struct cam_ife_csid_hw *csid_hw,
	uint32_t width)
{
	bool supported = false;

	if (!csid_hw) {
		CAM_ERR(CAM_ISP, "csid_hw is NULL");
		return supported;
	}

	if (cam_ife_csid_is_resolution_supported_by_fuse(width) &&
		cam_ife_csid_is_resolution_supported_by_dt(csid_hw, width))
		supported = true;
	return supported;
}

int cam_ife_csid_path_reserve(struct cam_ife_csid_hw *csid_hw,
	struct cam_csid_hw_reserve_resource_args  *reserve)
{
@@ -1179,6 +1289,13 @@ int cam_ife_csid_path_reserve(struct cam_ife_csid_hw *csid_hw,
	}

	if (reserve->sync_mode == CAM_ISP_HW_SYNC_MASTER) {
		if ((reserve->res_id == CAM_IFE_PIX_PATH_RES_IPP) &&
			!(cam_ife_csid_is_resolution_supported(csid_hw,
			reserve->in_port->left_stop -
			reserve->in_port->left_start + 1))) {
			rc = -EINVAL;
			goto end;
		}
		path_data->start_pixel = reserve->in_port->left_start;
		path_data->end_pixel = reserve->in_port->left_stop;
		path_data->width  = reserve->in_port->left_width;
@@ -1198,6 +1315,13 @@ int cam_ife_csid_path_reserve(struct cam_ife_csid_hw *csid_hw,
			csid_hw->hw_intf->hw_idx, reserve->res_id,
			path_data->start_line, path_data->end_line);
	} else if (reserve->sync_mode == CAM_ISP_HW_SYNC_SLAVE) {
		if ((reserve->res_id == CAM_IFE_PIX_PATH_RES_IPP) &&
			!(cam_ife_csid_is_resolution_supported(csid_hw,
			reserve->in_port->right_stop -
			reserve->in_port->right_start + 1))) {
			rc = -EINVAL;
			goto end;
		}
		path_data->master_idx = reserve->master_idx;
		CAM_DBG(CAM_ISP, "CSID:%d master_idx=%d",
			csid_hw->hw_intf->hw_idx, path_data->master_idx);
@@ -1214,6 +1338,13 @@ int cam_ife_csid_path_reserve(struct cam_ife_csid_hw *csid_hw,
			csid_hw->hw_intf->hw_idx, reserve->res_id,
			path_data->start_line, path_data->end_line);
	} else {
		if ((reserve->res_id == CAM_IFE_PIX_PATH_RES_IPP) &&
			!(cam_ife_csid_is_resolution_supported(csid_hw,
			reserve->in_port->left_stop -
			reserve->in_port->left_start + 1))) {
			rc = -EINVAL;
			goto end;
		}
		path_data->width  = reserve->in_port->left_width;
		path_data->start_pixel = reserve->in_port->left_start;
		path_data->end_pixel = reserve->in_port->left_stop;
@@ -5188,7 +5319,8 @@ int cam_ife_csid_hw_probe_init(struct cam_hw_intf *csid_hw_intf,
		goto err;
	}

	if (cam_cpas_is_feature_supported(CAM_CPAS_QCFA_BINNING_ENABLE) == 1)
	if (cam_cpas_is_feature_supported(CAM_CPAS_QCFA_BINNING_ENABLE,
		CAM_CPAS_HW_IDX_ANY, NULL))
		ife_csid_hw->binning_enable = 1;

	ife_csid_hw->hw_intf->hw_ops.get_hw_caps = cam_ife_csid_get_hw_caps;
Loading