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

Commit a36fe9d5 authored by Harsh Shah's avatar Harsh Shah
Browse files

msm: camera: isp: Add master-slave capability to vfe driver



Add dual_hw MASTER and SLAVE tags to vfe_src.
Add info to src on ioctl and remove it during stop_axi.
Also maintain the same info in common_data.
This change adds the framework needed to use VFE src in
Master-Slave mode.

Change-Id: I41b210ed1ff7a7d26510a78590e62f46f4a3ca1c
Signed-off-by: default avatarHarsh Shah <harshs@codeaurora.org>
parent f82766f8
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -456,6 +456,8 @@ static int vfe_probe(struct platform_device *pdev)
	}

	vfe_parent_dev->common_sd->common_data = &vfe_common_data;
	memset(&vfe_common_data, 0, sizeof(vfe_common_data));
	spin_lock_init(&vfe_common_data.common_dev_data_lock);

	for_each_available_child_of_node(dt_node, node) {
		new_dev = of_platform_device_create(node, NULL, &pdev->dev);
+7 −5
Original line number Diff line number Diff line
@@ -67,10 +67,9 @@ struct msm_vfe_stats_stream;

#define VFE_SD_HW_MAX VFE_SD_COMMON

#define DUAL_CAM_NUM_SLAVE_MAX 1

struct msm_vfe_sof_info {
	struct timeval timestamp;
	uint32_t timestamp_ms;
	uint32_t mono_timestamp_ms;
	uint32_t frame_id;
};

@@ -600,11 +599,14 @@ struct dual_vfe_resource {
};

struct msm_vfe_common_dev_data {
	spinlock_t common_dev_data_lock;
	enum msm_vfe_dual_hw_type dual_hw_type;
	struct msm_vfe_sof_info master_sof_info;
	uint8_t master_active;
	uint32_t num_slave;
	uint32_t free_slave_mask;
	struct msm_vfe_sof_info slave_sof_info[DUAL_CAM_NUM_SLAVE_MAX];
	uint32_t reserved_slave_mask;
	uint32_t slave_active_mask;
	struct msm_vfe_sof_info slave_sof_info[MS_NUM_SLAVE_MAX];
};

struct msm_vfe_common_subdev {
+144 −3
Original line number Diff line number Diff line
@@ -24,6 +24,10 @@
#define BURST_SKIP_THRESHOLD              (16)
#define ISP_SOF_DEBUG_COUNT 0

static int msm_isp_update_dual_HW_ms_info_at_start(
	struct vfe_device *vfe_dev,
	enum msm_vfe_input_src stream_src);

int msm_isp_axi_create_stream(
	struct msm_vfe_axi_shared_data *axi_data,
	struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd)
@@ -635,6 +639,7 @@ void msm_isp_notify(struct vfe_device *vfe_dev, uint32_t event_type,
	switch (event_type) {
	case ISP_EVENT_SOF:
		src_info = &vfe_dev->axi_data.src_info[frame_src];
		msm_isp_update_dual_HW_ms_info_at_start(vfe_dev, frame_src);

		if (frame_src == VFE_PIX_0) {

@@ -1976,6 +1981,132 @@ static int msm_isp_axi_update_cgc_override(struct vfe_device *vfe_dev,
	return 0;
}

static int msm_isp_update_dual_HW_ms_info_at_start(
	struct vfe_device *vfe_dev,
	enum msm_vfe_input_src stream_src)
{
	int rc = 0;
	uint32_t j, k, max_sof = 0;
	uint8_t slave_id;
	struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
	struct msm_vfe_src_info *src_info = NULL;

	if (stream_src >= VFE_SRC_MAX) {
		pr_err("%s: Error! Invalid src %u\n", __func__, stream_src);
		return -EINVAL;
	}

	src_info = &axi_data->src_info[stream_src];
	if (src_info->dual_hw_type != DUAL_HW_MASTER_SLAVE)
		return rc;

	if (src_info->dual_hw_ms_info.dual_hw_ms_type ==
		MS_TYPE_MASTER) {
		spin_lock(&vfe_dev->common_data->common_dev_data_lock);
		vfe_dev->common_data->master_active = 1;
		spin_unlock(&vfe_dev->common_data->common_dev_data_lock);

		/*
		 * If any slaves are active, then find the max slave
		 * frame_id and set it to Master, so master will start
		 * higher and then the slave can copy master frame_id
		 * without repeating.
		 * For safety, add some buffer, currently 5.
		 */
		if (!vfe_dev->common_data->slave_active_mask)
			return rc;

		spin_lock(&vfe_dev->common_data->common_dev_data_lock);
		for (j = 0, k = 0; j < vfe_dev->common_data->num_slave;
			k++) {
			if (vfe_dev->common_data->reserved_slave_mask &
				(1 << k))
				j++;
			else
				continue;

			if (vfe_dev->common_data->slave_active_mask &
				(1 << k) &&
				(vfe_dev->common_data->slave_sof_info[
					(j - 1)].frame_id > max_sof)) {
				max_sof = vfe_dev->common_data->
					slave_sof_info[j - 1].frame_id;
			}
		}
		vfe_dev->axi_data.src_info[stream_src].frame_id =
			max_sof + 1;
		spin_unlock(&vfe_dev->common_data->common_dev_data_lock);

		ISP_DBG("%s: Setting Master frame_id to %u\n", __func__,
			max_sof + 1);
	} else {
		spin_lock(&vfe_dev->common_data->common_dev_data_lock);
		slave_id = src_info->dual_hw_ms_info.slave_id;
		vfe_dev->common_data->slave_active_mask |=
			(1 << slave_id);
		spin_unlock(&vfe_dev->common_data->common_dev_data_lock);
	}

	return rc;
}

static int msm_isp_update_dual_HW_ms_info_at_stop(
	struct vfe_device *vfe_dev,
	struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd,
	enum msm_isp_camif_update_state camif_update)
{
	int i, rc = 0;
	uint8_t slave_id;
	struct msm_vfe_axi_stream *stream_info = NULL;
	struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
	enum msm_vfe_input_src stream_src = VFE_SRC_MAX;
	struct msm_vfe_src_info *src_info = NULL;

	if (stream_cfg_cmd->num_streams > MAX_NUM_STREAM ||
		stream_cfg_cmd->num_streams == 0)
		return -EINVAL;

	for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
		if (HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i]) >=
			MAX_NUM_STREAM) {
			return -EINVAL;
		}
		stream_info = &axi_data->stream_info[
			HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])];
		stream_src = SRC_TO_INTF(stream_info->stream_src);

		/* Remove PIX if DISABLE CAMIF */
		if (stream_src == VFE_PIX_0 && !((camif_update == DISABLE_CAMIF)
			|| (camif_update == DISABLE_CAMIF_IMMEDIATELY)))
			continue;

		src_info = &axi_data->src_info[stream_src];
		if (src_info->dual_hw_type != DUAL_HW_MASTER_SLAVE)
			continue;

		spin_lock(&vfe_dev->common_data->common_dev_data_lock);
		if (src_info->dual_hw_ms_info.dual_hw_ms_type ==
			MS_TYPE_MASTER) {
			/*
			 * Once Master is inactive, slave will increment
			 * its own frame_id
			 */
			vfe_dev->common_data->master_active = 0;
		} else {
			slave_id = src_info->dual_hw_ms_info.slave_id;
			vfe_dev->common_data->reserved_slave_mask &=
				~(1 << slave_id);
			vfe_dev->common_data->slave_active_mask &=
				~(1 << slave_id);
			vfe_dev->common_data->num_slave--;
		}
		src_info->dual_hw_ms_info.sof_info = NULL;
		spin_unlock(&vfe_dev->common_data->common_dev_data_lock);
	}

	return rc;
}

static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev,
			struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd,
			enum msm_isp_camif_update_state camif_update)
@@ -2244,7 +2375,7 @@ static int msm_isp_stop_axi_stream(struct vfe_device *vfe_dev,

int msm_isp_cfg_axi_stream(struct vfe_device *vfe_dev, void *arg)
{
	int rc = 0;
	int rc = 0, ret;
	struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd = arg;
	struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
	enum msm_isp_camif_update_state camif_update;
@@ -2278,6 +2409,16 @@ int msm_isp_cfg_axi_stream(struct vfe_device *vfe_dev, void *arg)
			/* Reset hvx state */
			vfe_dev->hvx_cmd = HVX_DISABLE;
		}

		/*
		 * Use different ret value to not overwrite the error from
		 * msm_isp_stop_axi_stream
		 */
		ret = msm_isp_update_dual_HW_ms_info_at_stop(
			vfe_dev, stream_cfg_cmd, camif_update);
		if (ret < 0)
			pr_warn("%s: Warning! Update dual_cam failed\n",
				__func__);
	}

	if (rc < 0)
+69 −0
Original line number Diff line number Diff line
@@ -707,6 +707,70 @@ int msm_isp_cfg_input(struct vfe_device *vfe_dev, void *arg)
	return rc;
}

static int msm_isp_set_dual_HW_master_slave_mode(struct vfe_device *vfe_dev,
	void *arg)
{
	/*
	 * This method assumes no 2 processes are accessing it simultaneously.
	 * Currently this is guaranteed by mutex lock in ioctl.
	 * If that changes, need to revisit this
	 */
	int rc = 0, j;
	struct msm_isp_set_dual_hw_ms_cmd *dual_hw_ms_cmd = NULL;
	struct msm_vfe_src_info *src_info = NULL;

	if (!vfe_dev || !arg) {
		pr_err("%s: Error! Invalid input vfe_dev %p arg %p\n",
			__func__, vfe_dev, arg);
		return -EINVAL;
	}

	dual_hw_ms_cmd = (struct msm_isp_set_dual_hw_ms_cmd *)arg;
	vfe_dev->common_data->dual_hw_type = DUAL_HW_MASTER_SLAVE;

	if (dual_hw_ms_cmd->input_src >= VFE_SRC_MAX) {
		pr_err("%s: Error! Invalid SRC param %d\n", __func__,
			dual_hw_ms_cmd->input_src);
		return -EINVAL;
	}

	src_info = &vfe_dev->axi_data.
		src_info[dual_hw_ms_cmd->input_src];

	src_info->dual_hw_ms_info.dual_hw_ms_type =
		dual_hw_ms_cmd->dual_hw_ms_type;

	if (dual_hw_ms_cmd->dual_hw_ms_type == MS_TYPE_MASTER) {
		src_info->dual_hw_type = DUAL_HW_MASTER_SLAVE;
		src_info->dual_hw_ms_info.sof_info =
			&vfe_dev->common_data->master_sof_info;
	} else {
		spin_lock(&vfe_dev->common_data->common_dev_data_lock);
		src_info->dual_hw_type = DUAL_HW_MASTER_SLAVE;
		for (j = 0; j < MS_NUM_SLAVE_MAX; j++) {
			if (vfe_dev->common_data->reserved_slave_mask &
				(1 << j))
				continue;

			vfe_dev->common_data->reserved_slave_mask |= (1 << j);
			vfe_dev->common_data->num_slave++;
			src_info->dual_hw_ms_info.sof_info =
				&vfe_dev->common_data->slave_sof_info[j];
			src_info->dual_hw_ms_info.slave_id = j;
			break;
		}
		spin_unlock(&vfe_dev->common_data->common_dev_data_lock);

		if (j == MS_NUM_SLAVE_MAX) {
			pr_err("%s: Error! Cannot find free aux resource\n",
				__func__);
			return -EBUSY;
		}
	}

	return rc;
}

static int msm_isp_proc_cmd_list_unlocked(struct vfe_device *vfe_dev, void *arg)
{
	int rc = 0;
@@ -918,6 +982,11 @@ static long msm_isp_ioctl_unlocked(struct v4l2_subdev *sd,
		rc = msm_isp_cfg_input(vfe_dev, arg);
		mutex_unlock(&vfe_dev->core_mutex);
		break;
	case VIDIOC_MSM_ISP_SET_DUAL_HW_MASTER_SLAVE:
		mutex_lock(&vfe_dev->core_mutex);
		rc = msm_isp_set_dual_HW_master_slave_mode(vfe_dev, arg);
		mutex_unlock(&vfe_dev->core_mutex);
		break;
	case VIDIOC_MSM_ISP_FETCH_ENG_START:
		mutex_lock(&vfe_dev->core_mutex);
		rc = vfe_dev->hw_info->vfe_ops.core_ops.
+16 −6
Original line number Diff line number Diff line
@@ -449,6 +449,11 @@ enum vfe_sd_type {
};

/* Usecases when 2 HW need to be related or synced */

/* When you change the value below, check for the sof event_data size.
 * V4l2 limits payload to 64 bytes */
#define MS_NUM_SLAVE_MAX 1

enum msm_vfe_dual_hw_type {
	DUAL_NONE = 0,
	DUAL_HW_VFE_SPLIT = 1,
@@ -456,15 +461,14 @@ enum msm_vfe_dual_hw_type {
};

enum msm_vfe_dual_hw_ms_type {
	DUAL_HW_MASTER,
	DUAL_HW_SLAVE,
	DUAL_HW_MAX,
	MS_TYPE_NONE,
	MS_TYPE_MASTER,
	MS_TYPE_SLAVE,
};

struct msm_isp_set_dual_hw_ms_cmd {
	uint8_t num_src;
	enum msm_vfe_dual_hw_ms_type dual_hw_ms_type[VFE_SRC_MAX];
	enum msm_vfe_input_src input_src[VFE_SRC_MAX];
	enum msm_vfe_dual_hw_ms_type dual_hw_ms_type;
	enum msm_vfe_input_src input_src;
};

enum msm_isp_buf_type {
@@ -639,6 +643,10 @@ struct msm_isp_output_info {
	uint32_t stats_framedrop_mask;
};

struct msm_isp_ms_delta_info {
	uint32_t delta[MS_NUM_SLAVE_MAX];
};

struct msm_isp_event_data {
	/*Wall clock except for buffer divert events
	 *which use monotonic clock
@@ -652,6 +660,7 @@ struct msm_isp_event_data {
		struct msm_isp_buf_event buf_done;
		struct msm_isp_error_info error_info;
		struct msm_isp_output_info output_info;
		struct msm_isp_ms_delta_info ms_delta_info;
	} u; /* union can have max 52 bytes */
};

@@ -665,6 +674,7 @@ struct msm_isp_event_data32 {
		struct msm_isp_buf_event buf_done;
		struct msm_isp_error_info error_info;
		struct msm_isp_output_info output_info;
		struct msm_isp_ms_delta_info ms_delta_info;
	} u;
};
#endif