Explorar o código

msm: camera: csiphy: Expand CSIPHY Onthego support

Currently, onthego sysfs module only supports
configuration for one PHY at a time, and the
PHY cannot be selected, the configuration
committed will be applied to whichever PHY that is
streamed on after the settings is committed.

This change adds support to program multiple
settings for each PHY, and support for specifying
a particular PHY for which the setting is to be
applied.

CRs-Fixed: 3681877
Change-Id: Ia9555491dbfe40c83837897ac20662d9491dd82d
Signed-off-by: Li Sha Lim <[email protected]>
Li Sha Lim hai 1 ano
pai
achega
7942cd08b8
Modificáronse 1 ficheiros con 142 adicións e 20 borrados
  1. 142 20
      drivers/cam_sensor_module/cam_csiphy/cam_csiphy_core.c

+ 142 - 20
drivers/cam_sensor_module/cam_csiphy/cam_csiphy_core.c

@@ -33,13 +33,12 @@
  */
 #define CAM_MAX_PHYS_PER_CP_CTRL_REG 4
 
-static DEFINE_MUTEX(active_csiphy_cnt_mutex);
-static DEFINE_MUTEX(main_aon_selection);
+/*
+ * PHY ON-The-Go Buffer Size
+ */
+#define CSIPHY_ONTHEGO_BUFSIZE 30
+
 
-static int csiphy_onthego_reg_count;
-static unsigned int csiphy_onthego_regs[150];
-module_param_array(csiphy_onthego_regs, uint, &csiphy_onthego_reg_count, 0644);
-MODULE_PARM_DESC(csiphy_onthego_regs, "Functionality to let csiphy registers program on the fly");
 
 struct g_csiphy_data {
 	void __iomem *base_address;
@@ -51,8 +50,125 @@ struct g_csiphy_data {
 	struct cam_csiphy_aon_sel_params_t *aon_sel_param;
 };
 
+static DEFINE_MUTEX(active_csiphy_cnt_mutex);
+static DEFINE_MUTEX(main_aon_selection);
 static struct g_csiphy_data g_phy_data[MAX_CSIPHY] = {0};
 static int active_csiphy_hw_cnt;
+static char csiphy_onthego_regs[20];
+static int csiphy_onthego_reg_count[MAX_CSIPHY];
+static unsigned int csiphy_onthego_regvals[MAX_CSIPHY][CSIPHY_ONTHEGO_BUFSIZE];
+
+
+typedef int (*csiphy_onthego_func)(int *inp, int n_inp, int phy_idx, char *outp);
+static int csiphy_onthego_get_set(int *inp, int n_inp, int phy_idx, char *outp);
+
+static int csiphy_set_onthego_values(const char *val, const struct kernel_param *kp)
+{
+	/**
+	 * Expected format string: ":n1,n2,..:val1,val2,...""
+	 * Values between the colons, specify the PHY(s) with
+	 * which these settings apply to
+	 * The actual onthego values should have comma-delimited
+	 * entries with total a multiple of 3 (reg_addr, val, delay)
+	 */
+	csiphy_onthego_func fn = (csiphy_onthego_func) kp->arg;
+	int i, idx, onthego_val, onthego_idx = 0;
+	int onthego_values[CSIPHY_ONTHEGO_BUFSIZE] = {0};
+	char *p1, *p2, *token;
+	bool phy_target[MAX_CSIPHY] = {false};
+
+	p1 = strnchr(val, 1, ':'); p2 = strrchr(val, ':');
+	if (!p1 || !p2 || p2 - p1 < 2) {
+		CAM_ERR(CAM_CSIPHY, "Invalid csiphy onthego input string: %s", val);
+		return -EINVAL;
+	}
+
+	strscpy(csiphy_onthego_regs, p1, 20);
+	while ((token = strsep(&p1, ":")) != NULL) {
+		if (!kstrtoint(token, 0, &idx) && idx >= 0 && idx < MAX_CSIPHY)
+			phy_target[idx] = true;
+	}
+
+	p1 = p2 + 1;
+
+	if (!strncasecmp(p1, "X", 1)) {
+		for (i = 0; i < MAX_CSIPHY; i++) {
+			if (phy_target[i])
+				csiphy_onthego_reg_count[i] = 0;
+		}
+		return 0;
+	}
+
+	while ((token = strsep(&p1, ",")) != NULL) {
+		if (!kstrtoint(token, 0, &onthego_val))
+			onthego_values[onthego_idx++] = onthego_val;
+	}
+
+	if (!onthego_idx || (onthego_idx % 3)) {
+		CAM_ERR(CAM_CSIPHY, "Invalid multiple of onthego entries: %d,", onthego_idx);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < MAX_CSIPHY && onthego_idx; i++) {
+		if (phy_target[i])
+			fn(onthego_values, onthego_idx, i, NULL);
+	}
+
+	return 0;
+}
+
+static int csiphy_get_onthego_values(char *buffer, const struct kernel_param *kp)
+{
+	csiphy_onthego_func fn = (csiphy_onthego_func) kp->arg;
+	int rc = 0, i;
+	char *p = buffer;
+
+	for (i = 0; i < MAX_CSIPHY; i++)
+		rc += fn(NULL, 0, i, p + rc);
+
+	return rc;
+}
+
+static const struct kernel_param_ops csiphy_onthego_ops = {
+	.set = csiphy_set_onthego_values,
+	.get = csiphy_get_onthego_values,
+};
+
+module_param_cb(csiphy_onthego_regs, &csiphy_onthego_ops, csiphy_onthego_get_set, 0644);
+MODULE_PARM_DESC(csiphy_onthego_regs, "Functionality to let csiphy registers program on the fly");
+
+static int csiphy_onthego_get_set(int *inp, int n_inp, int phy_idx, char *outp)
+{
+	int i, idx, rc = 0;
+	char *p;
+
+	idx = csiphy_onthego_reg_count[phy_idx];
+
+	if (inp && n_inp) {
+		for (i = 0; i < n_inp; i++) {
+			csiphy_onthego_regvals[phy_idx][idx] = (unsigned int) inp[i];
+			if (++idx == CSIPHY_ONTHEGO_BUFSIZE) {
+				CAM_WARN(CAM_CSIPHY,
+					"Onthego input for PHY %d reached end of circular buffer, circling back",
+					phy_idx);
+				idx = idx % CSIPHY_ONTHEGO_BUFSIZE;
+			}
+		}
+	}
+
+	csiphy_onthego_reg_count[phy_idx] = idx;
+
+	if (outp) {
+		p = outp;
+		rc += scnprintf(p, PAGE_SIZE, "PHY idx %d: ", phy_idx);
+		for (i = 0; i < idx; i++)
+			rc += scnprintf(p + rc, PAGE_SIZE - rc, "0x%x,",
+				csiphy_onthego_regvals[phy_idx][i]);
+		rc += scnprintf(p + rc, PAGE_SIZE - rc, "\n");
+	}
+
+	return rc;
+}
 
 void cam_csiphy_update_auxiliary_mask(struct csiphy_device *csiphy_dev)
 {
@@ -172,23 +288,23 @@ static inline void cam_csiphy_apply_onthego_reg_values(void __iomem *csiphybase,
 
 	CAM_DBG(CAM_CSIPHY, "csiphy: %d, onthego_reg_count: %d",
 		csiphy_idx,
-		csiphy_onthego_reg_count);
+		csiphy_onthego_reg_count[csiphy_idx]);
 
-	if (csiphy_onthego_reg_count % 3)
-		csiphy_onthego_reg_count -= (csiphy_onthego_reg_count % 3);
+	for (i = 0; i < csiphy_onthego_reg_count[csiphy_idx]; i += 3) {
+		cam_io_w_mb(csiphy_onthego_regvals[csiphy_idx][i+1],
+			csiphybase + csiphy_onthego_regvals[csiphy_idx][i]);
 
-	for (i = 0; i < csiphy_onthego_reg_count; i += 3) {
-		cam_io_w_mb(csiphy_onthego_regs[i+1],
-			csiphybase + csiphy_onthego_regs[i]);
-
-		if (csiphy_onthego_regs[i+2])
-			usleep_range(csiphy_onthego_regs[i+2], csiphy_onthego_regs[i+2] + 5);
+		if (csiphy_onthego_regvals[csiphy_idx][i+2])
+			usleep_range(csiphy_onthego_regvals[csiphy_idx][i+2],
+				csiphy_onthego_regvals[csiphy_idx][i+2] + 5);
 
 		CAM_INFO(CAM_CSIPHY, "Offset: 0x%x, Val: 0x%x Delay(us): %u",
-			csiphy_onthego_regs[i],
-			cam_io_r_mb(csiphybase + csiphy_onthego_regs[i]),
-			csiphy_onthego_regs[i+2]);
+			csiphy_onthego_regvals[csiphy_idx][i],
+			cam_io_r_mb(csiphybase + csiphy_onthego_regvals[csiphy_idx][i]),
+			csiphy_onthego_regvals[csiphy_idx][i+2]);
 	}
+
+	csiphy_onthego_reg_count[csiphy_idx] = 0;
 }
 
 static inline int cam_csiphy_release_from_reset_state(struct csiphy_device *csiphy_dev,
@@ -2102,6 +2218,12 @@ int32_t cam_csiphy_core_cfg(void *phy_dev,
 	csiphy_reg = csiphy_dev->ctrl_reg->csiphy_reg;
 	status_reg_ptr = csiphy_reg->status_reg_params;
 
+	if (!status_reg_ptr) {
+		CAM_ERR(CAM_CSIPHY, "CSIPHY %d status reg is NULL: %s",
+			soc_info->index, CAM_IS_NULL_TO_STR(status_reg_ptr));
+		return -EINVAL;
+	}
+
 	CAM_DBG(CAM_CSIPHY, "Opcode received: %d", cmd->op_code);
 	mutex_lock(&csiphy_dev->mutex);
 	switch (cmd->op_code) {
@@ -2717,12 +2839,12 @@ int32_t cam_csiphy_core_cfg(void *phy_dev,
 			goto hw_cnt_decrement;
 		}
 
-		if (csiphy_onthego_reg_count)
+		if (csiphy_onthego_reg_count[soc_info->index])
 			cam_csiphy_apply_onthego_reg_values(csiphybase, soc_info->index);
 
 		cam_csiphy_release_from_reset_state(csiphy_dev, csiphybase, offset);
 
-		if (g_phy_data[soc_info->index].is_3phase && status_reg_ptr) {
+		if (g_phy_data[soc_info->index].is_3phase) {
 			for (i = 0; i < CAM_CSIPHY_MAX_CPHY_LANES; i++) {
 				if (status_reg_ptr->cphy_lane_status[i]) {
 					cphy_trio_status = cam_io_r_mb(csiphybase +