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

Commit daf6d56f authored by Terence Ho's avatar Terence Ho
Browse files

msm: ais: ife SOF and buffer done tracking



Add caching of HW SOF timestamps to correlate with frame done events to
ensure reported information for frame is accurate.
To ensure HW FIFO is fully utilized use HW FIFO status instead of SW
variable and enq buffers to HW on SOF or frame done interrupts.

Change-Id: I5e6eee4142ca4294afe172cede5724eb2ecb243e
Signed-off-by: default avatarTerence Ho <terenceh@codeaurora.org>
parent 15c00787
Loading
Loading
Loading
Loading
+0 −4
Original line number Diff line number Diff line
@@ -476,16 +476,12 @@ static int ais_ife_dev_cb(void *priv, struct ais_ife_event_data *evt_data)
		return -EINVAL;
	}

	spin_lock_bh(&p_ife_dev->eventq_lock);

	/* Queue the event */
	memcpy(event.u.data, (void *)evt_data, sizeof(*evt_data));
	event.id = V4L_EVENT_ID_AIS_IFE;
	event.type = V4L_EVENT_TYPE_AIS_IFE;
	v4l2_event_queue(p_ife_dev->cam_sd.sd.devnode, &event);

	spin_unlock_bh(&p_ife_dev->eventq_lock);

	return 0;
}

+1 −0
Original line number Diff line number Diff line
@@ -478,6 +478,7 @@ struct ais_ife_frame_msg {
enum ais_ife_msg_type {
	AIS_IFE_MSG_SOF,
	AIS_IFE_MSG_FRAME_DONE,
	AIS_IFE_MSG_OUTPUT_WARNING,
	AIS_IFE_MSG_OUTPUT_ERROR,
	AIS_IFE_MSG_CSID_WARNING,
	AIS_IFE_MSG_CSID_ERROR
+301 −31
Original line number Diff line number Diff line
@@ -52,9 +52,10 @@
#define AIS_VFE_REGUP_RDI_SHIFT 1
#define AIS_VFE_REGUP_RDI_ALL 0x1E

/*VFE BUS DEFINITIONS*/
#define MAX_NUM_BUF_HW_FIFOQ 4
/*Allow max of 4 HW FIFO Q + 2 delayed buffers before error*/
#define MAX_NUM_BUF_SW_FIFOQ_ERR 6

/*VFE BUS DEFINITIONS*/
#define AIS_VFE_BUS_SET_DEBUG_REG                0x82

#define AIS_VFE_RDI_BUS_DEFAULT_WIDTH               0xFF01
@@ -72,8 +73,6 @@ static void ais_clear_rdi_path(struct ais_vfe_rdi_output *rdi_path)
	int i;

	rdi_path->frame_cnt = 0;
	rdi_path->sof_ts = 0;
	rdi_path->sof_hw_ts = 0;

	rdi_path->num_buffer_hw_q = 0;
	INIT_LIST_HEAD(&rdi_path->buffer_q);
@@ -84,6 +83,17 @@ static void ais_clear_rdi_path(struct ais_vfe_rdi_output *rdi_path)
		list_add_tail(&rdi_path->buffers[i].list,
				&rdi_path->free_buffer_list);
	}

	memset(&rdi_path->last_sof_info, 0, sizeof(rdi_path->last_sof_info));

	rdi_path->num_sof_info_q = 0;
	INIT_LIST_HEAD(&rdi_path->sof_info_q);
	INIT_LIST_HEAD(&rdi_path->free_sof_info_list);
	for (i = 0; i < AIS_VFE_MAX_SOF_INFO; i++) {
		INIT_LIST_HEAD(&rdi_path->sof_info[i].list);
		list_add_tail(&rdi_path->sof_info[i].list,
				&rdi_path->free_sof_info_list);
	}
}

static int ais_vfe_bus_hw_init(struct ais_vfe_hw_core_info *core_info)
@@ -664,19 +674,26 @@ int ais_vfe_write(void *hw_priv, void *write_args, uint32_t arg_size)
	return -EPERM;
}

static void ais_vfe_queue_to_hw(struct ais_vfe_hw_core_info *core_info,
static void ais_vfe_q_bufs_to_hw(struct ais_vfe_hw_core_info *core_info,
		enum ais_ife_output_path_id path)
{
	struct ais_vfe_rdi_output *rdi_path = NULL;
	struct ais_vfe_buffer_t *vfe_buf = NULL;
	struct ais_vfe_bus_ver2_hw_info   *bus_hw_info = NULL;
	struct ais_vfe_bus_ver2_reg_offset_bus_client  *client_regs = NULL;
	uint32_t fifo_status = 0;
	bool is_full = false;

	rdi_path = &core_info->rdi_out[path];
	bus_hw_info = core_info->vfe_hw_info->bus_hw_info;
	client_regs = &bus_hw_info->bus_client_reg[path];

	while (rdi_path->num_buffer_hw_q < MAX_NUM_BUF_HW_FIFOQ) {
	fifo_status = cam_io_r_mb(core_info->mem_base +
			bus_hw_info->common_reg.addr_fifo_status);

	is_full =  fifo_status & (1 << path);

	while (!is_full) {
		if (list_empty(&rdi_path->buffer_q))
			break;

@@ -684,7 +701,7 @@ static void ais_vfe_queue_to_hw(struct ais_vfe_hw_core_info *core_info,
				struct ais_vfe_buffer_t, list);
		list_del_init(&vfe_buf->list);

		CAM_DBG(CAM_ISP, "IFE%d BUF| RDI%d Q %d(0x%x) FIFO:%d",
		CAM_DBG(CAM_ISP, "IFE%d|RDI%d: Q %d(0x%x) FIFO:%d",
			core_info->vfe_idx, path,
			vfe_buf->bufIdx, vfe_buf->iova_addr,
			rdi_path->num_buffer_hw_q);
@@ -694,7 +711,15 @@ static void ais_vfe_queue_to_hw(struct ais_vfe_hw_core_info *core_info,

		list_add_tail(&vfe_buf->list, &rdi_path->buffer_hw_q);
		++rdi_path->num_buffer_hw_q;

		fifo_status = cam_io_r_mb(core_info->mem_base +
			bus_hw_info->common_reg.addr_fifo_status);
		is_full =  fifo_status & (1 << path);
	}

	if (rdi_path->num_buffer_hw_q > MAX_NUM_BUF_SW_FIFOQ_ERR)
		CAM_WARN(CAM_ISP, "Excessive number of buffers in SW FIFO (%d)",
			rdi_path->num_buffer_hw_q);
}


@@ -767,7 +792,10 @@ static int ais_vfe_cmd_enq_buf(struct ais_vfe_hw_core_info *core_info,

		spin_lock(&rdi_path->buffer_lock);
		list_add_tail(&vfe_buf->list, &rdi_path->buffer_q);
		ais_vfe_queue_to_hw(core_info, enq_buf->path);

		if (rdi_path->state < AIS_ISP_RESOURCE_STATE_STREAMING)
			ais_vfe_q_bufs_to_hw(core_info, enq_buf->path);

		spin_unlock(&rdi_path->buffer_lock);
	}

@@ -816,6 +844,181 @@ int ais_vfe_process_cmd(void *hw_priv, uint32_t cmd_type,
	return rc;
}

static uint8_t ais_vfe_get_num_missed_sof(
	uint64_t cur_sof,
	uint64_t prev_sof,
	uint64_t last_sof,
	uint64_t ts_delta)
{
	uint8_t miss_sof = 0;

	if (prev_sof == last_sof) {
		miss_sof = 0;
	} else if (prev_sof < last_sof) {
		//rollover case
		miss_sof = (int)(((U64_MAX - last_sof) + prev_sof + 1 +
				ts_delta/2) / ts_delta);
	} else {
		miss_sof = (int)((prev_sof - last_sof + ts_delta/2) / ts_delta);
	}

	return miss_sof;
}

static int ais_vfe_q_sof(struct ais_vfe_hw_core_info *core_info,
	enum ais_ife_output_path_id path,
	struct ais_sof_info_t *p_sof)
{
	struct ais_vfe_rdi_output *p_rdi = &core_info->rdi_out[path];
	struct ais_sof_info_t *p_sof_info = NULL;
	int rc = 0;

	if (!list_empty(&p_rdi->free_sof_info_list)) {
		p_sof_info = list_first_entry(&p_rdi->free_sof_info_list,
			struct ais_sof_info_t, list);
		list_del_init(&p_sof_info->list);
		p_sof_info->frame_cnt = p_sof->frame_cnt;
		p_sof_info->sof_ts = p_sof->sof_ts;
		p_sof_info->cur_sof_hw_ts = p_sof->cur_sof_hw_ts;
		p_sof_info->prev_sof_hw_ts = p_sof->prev_sof_hw_ts;
		list_add_tail(&p_sof_info->list, &p_rdi->sof_info_q);
		p_rdi->num_sof_info_q++;


		CAM_DBG(CAM_ISP, "I%d|R%d|F%llu: sof %llu",
			core_info->vfe_idx, path, p_rdi->frame_cnt,
			p_sof_info->cur_sof_hw_ts);
	} else {
		rc = -1;

		CAM_ERR(CAM_ISP, "I%d|R%d|F%llu: free timestamp empty (%d)",
			core_info->vfe_idx, path, p_rdi->frame_cnt,
			p_rdi->num_buffer_hw_q);
	}

	return rc;
}


static void ais_vfe_handle_sof_rdi(struct ais_vfe_hw_core_info *core_info,
		struct ais_vfe_hw_work_data *work_data,
		enum ais_ife_output_path_id path)
{
	struct ais_vfe_rdi_output *p_rdi = &core_info->rdi_out[path];
	uint64_t cur_sof_hw_ts = work_data->ts_hw[path].cur_sof_ts;
	uint64_t prev_sof_hw_ts = work_data->ts_hw[path].prev_sof_ts;

	p_rdi->frame_cnt++;

	if (p_rdi->num_buffer_hw_q) {
		struct ais_sof_info_t sof = {};
		uint64_t ts_delta;
		uint8_t miss_sof = 0;

		if (cur_sof_hw_ts < prev_sof_hw_ts)
			ts_delta = cur_sof_hw_ts +
				(U64_MAX - prev_sof_hw_ts);
		else
			ts_delta = cur_sof_hw_ts - prev_sof_hw_ts;


		//check any missing SOFs
		if (p_rdi->frame_cnt > 1) {
			if (ts_delta == 0) {
				CAM_ERR(CAM_ISP, "IFE%d RDI%d ts_delta is 0",
						core_info->vfe_idx, path);
			} else {
				miss_sof = ais_vfe_get_num_missed_sof(
					cur_sof_hw_ts,
					prev_sof_hw_ts,
					p_rdi->last_sof_info.cur_sof_hw_ts,
					ts_delta);
			}
		}

		if (p_rdi->frame_cnt == 1 && prev_sof_hw_ts != 0) {
			//enq missed first frame
			sof.sof_ts = work_data->ts;
			sof.cur_sof_hw_ts = prev_sof_hw_ts;
			sof.frame_cnt = p_rdi->frame_cnt++;

			ais_vfe_q_sof(core_info, path, &sof);
		} else if (miss_sof > 0) {
			if (miss_sof > 1) {
				int i = 0;
				int miss_idx = miss_sof - 1;

				for (i = 0; i < (miss_sof - 1); i++) {

					sof.sof_ts = work_data->ts;
					sof.cur_sof_hw_ts = prev_sof_hw_ts -
						(ts_delta * miss_idx);
					sof.frame_cnt = p_rdi->frame_cnt++;

					ais_vfe_q_sof(core_info, path, &sof);

					miss_idx--;
				}
			}

			//enq prev
			sof.sof_ts = work_data->ts;
			sof.cur_sof_hw_ts = prev_sof_hw_ts;
			sof.frame_cnt = p_rdi->frame_cnt++;

			ais_vfe_q_sof(core_info, path, &sof);
		}

		//enq curr
		sof.sof_ts = work_data->ts;
		sof.cur_sof_hw_ts = prev_sof_hw_ts;
		sof.frame_cnt = p_rdi->frame_cnt;

		ais_vfe_q_sof(core_info, path, &sof);

	} else {
		CAM_DBG(CAM_ISP, "I%d R%d Flush SOF (%d) HW Q empty",
				core_info->vfe_idx, path,
				p_rdi->num_sof_info_q);

		if (p_rdi->num_sof_info_q) {
			struct ais_sof_info_t *p_sof_info;

			while (!list_empty(&p_rdi->sof_info_q)) {
				p_sof_info = list_first_entry(
					&p_rdi->sof_info_q,
					struct ais_sof_info_t, list);
				list_del_init(&p_sof_info->list);
				list_add_tail(&p_sof_info->list,
						&p_rdi->free_sof_info_list);
			}
			p_rdi->num_sof_info_q = 0;
		}


		//send warning
		core_info->event.type = AIS_IFE_MSG_OUTPUT_WARNING;
		core_info->event.path = path;
		core_info->event.u.err_msg.reserved = 0;

		core_info->event_cb(core_info->event_cb_priv,
			&core_info->event);

	}

	p_rdi->last_sof_info.cur_sof_hw_ts = cur_sof_hw_ts;

	//send sof only for current frame
	core_info->event.type = AIS_IFE_MSG_SOF;
	core_info->event.path = path;
	core_info->event.u.sof_msg.frame_id = p_rdi->frame_cnt;
	core_info->event.u.sof_msg.hw_ts = cur_sof_hw_ts;

	core_info->event_cb(core_info->event_cb_priv,
		&core_info->event);

}

static int ais_vfe_handle_sof(
	struct ais_vfe_hw_core_info *core_info,
	struct ais_vfe_hw_work_data *work_data)
@@ -836,17 +1039,12 @@ static int ais_vfe_handle_sof(
		if (p_rdi->state != AIS_ISP_RESOURCE_STATE_STREAMING)
			continue;

		p_rdi->frame_cnt++;
		p_rdi->sof_ts = work_data->ts;
		p_rdi->sof_hw_ts = work_data->ts_hw[path].cur_sof_ts;
		ais_vfe_handle_sof_rdi(core_info, work_data, path);

		core_info->event.type = AIS_IFE_MSG_SOF;
		core_info->event.path = path;
		core_info->event.u.sof_msg.frame_id = p_rdi->frame_cnt;
		core_info->event.u.sof_msg.hw_ts = p_rdi->sof_ts;

		core_info->event_cb(core_info->event_cb_priv,
				&core_info->event);
		//enq buffers
		spin_lock_bh(&p_rdi->buffer_lock);
		ais_vfe_q_bufs_to_hw(core_info, path);
		spin_unlock_bh(&p_rdi->buffer_lock);
	}

	return rc;
@@ -914,19 +1112,32 @@ static void ais_vfe_bus_handle_client_frame_done(
	struct ais_vfe_rdi_output         *rdi_path = NULL;
	struct ais_vfe_buffer_t           *vfe_buf = NULL;
	struct ais_vfe_bus_ver2_hw_info   *bus_hw_info = NULL;
	uint64_t                           frame_cnt;
	uint64_t                           sof_ts;
	uint64_t                           cur_sof_hw_ts;
	bool last_addr_match = false;

	CAM_DBG(CAM_ISP, "Frame Done Client %d", path);
	CAM_DBG(CAM_ISP, "I%d|R%d last_addr 0x%x",
			core_info->vfe_idx, path, last_addr);

	if (last_addr == 0) {
		CAM_ERR(CAM_ISP, "I%d|R%d null last_addr",
				core_info->vfe_idx, path);
		return;
	}

	rdi_path = &core_info->rdi_out[path];
	bus_hw_info = core_info->vfe_hw_info->bus_hw_info;

	spin_lock_bh(&rdi_path->buffer_lock);
	core_info->event.type = AIS_IFE_MSG_FRAME_DONE;
	core_info->event.path = path;

	while (rdi_path->num_buffer_hw_q && !last_addr_match) {
		struct ais_sof_info_t *p_sof_info = NULL;

		if (list_empty(&rdi_path->buffer_hw_q)) {
			CAM_ERR(CAM_ISP, "Received RDI%d FD while SW Q empty",
				path);
			CAM_DBG(CAM_ISP, "I%d|R%d: FD while HW Q empty",
				core_info->vfe_idx, path);
			break;
		}

@@ -935,17 +1146,39 @@ static void ais_vfe_bus_handle_client_frame_done(
		list_del_init(&vfe_buf->list);
		--rdi_path->num_buffer_hw_q;

		CAM_DBG(CAM_ISP, "IFE%d BUF| RDI%d DQ %d (0x%x) FIFO:%d|0x%x",
		CAM_DBG(CAM_ISP, "I%d|R%d BUF DQ %d (0x%x) FIFO:%d|0x%x",
			core_info->vfe_idx, path,
			vfe_buf->bufIdx, vfe_buf->iova_addr,
			rdi_path->num_buffer_hw_q, last_addr);

		core_info->event.type = AIS_IFE_MSG_FRAME_DONE;
		core_info->event.path = path;
		core_info->event.u.frame_msg.frame_id = rdi_path->frame_cnt;
		if (!list_empty(&rdi_path->sof_info_q)) {
			p_sof_info = list_first_entry(&rdi_path->sof_info_q,
					struct ais_sof_info_t, list);
			list_del_init(&p_sof_info->list);
			rdi_path->num_sof_info_q--;
		} else
			CAM_ERR(CAM_ISP, "I%d|R%d: SOF info Q is empty",
				core_info->vfe_idx, path);

		if (p_sof_info) {
			frame_cnt = p_sof_info->frame_cnt;
			sof_ts = p_sof_info->sof_ts;
			cur_sof_hw_ts = p_sof_info->cur_sof_hw_ts;
			list_add_tail(&p_sof_info->list,
					&rdi_path->free_sof_info_list);
		} else {
			frame_cnt = sof_ts = cur_sof_hw_ts = 0;
		}

		CAM_DBG(CAM_ISP, "I%d|R%d|F%llu: si [%llu, %llu, %llu]",
			core_info->vfe_idx, path, frame_cnt, sof_ts,
			cur_sof_hw_ts);


		core_info->event.u.frame_msg.frame_id = frame_cnt;
		core_info->event.u.frame_msg.buf_idx = vfe_buf->bufIdx;
		core_info->event.u.frame_msg.ts = rdi_path->sof_ts;
		core_info->event.u.frame_msg.hw_ts = rdi_path->sof_hw_ts;
		core_info->event.u.frame_msg.ts = sof_ts;
		core_info->event.u.frame_msg.hw_ts = cur_sof_hw_ts;

		core_info->event_cb(core_info->event_cb_priv,
				&core_info->event);
@@ -955,15 +1188,52 @@ static void ais_vfe_bus_handle_client_frame_done(
		else
			CAM_WARN(CAM_ISP, "IFE%d buf %d did not match addr",
				core_info->vfe_idx, vfe_buf->bufIdx);

		list_add_tail(&vfe_buf->list, &rdi_path->free_buffer_list);
	}

	if (!last_addr_match)
	if (!last_addr_match) {
		CAM_ERR(CAM_ISP, "IFE%d BUF| RDI%d NO MATCH addr 0x%x",
			core_info->vfe_idx, path, last_addr);

	ais_vfe_queue_to_hw(core_info, path);
		//send warning
		core_info->event.type = AIS_IFE_MSG_OUTPUT_WARNING;
		core_info->event.path = path;
		core_info->event.u.err_msg.reserved = 1;

		core_info->event_cb(core_info->event_cb_priv,
			&core_info->event);
	}

	/* Flush SOF info Q if HW Buffer Q is empty */
	if (rdi_path->num_buffer_hw_q == 0) {
		struct ais_sof_info_t *p_sof_info = NULL;

		CAM_DBG(CAM_ISP, "I%d|R%d|F%llu: Flush SOF (%d) HW Q empty",
			core_info->vfe_idx, path, frame_cnt,
			rdi_path->num_sof_info_q);

		while (!list_empty(&rdi_path->sof_info_q)) {
			p_sof_info = list_first_entry(&rdi_path->sof_info_q,
					struct ais_sof_info_t, list);
			list_del_init(&p_sof_info->list);
			list_add_tail(&p_sof_info->list,
				&rdi_path->free_sof_info_list);
		}

		rdi_path->num_sof_info_q = 0;

		//send warning
		core_info->event.type = AIS_IFE_MSG_OUTPUT_WARNING;
		core_info->event.path = path;
		core_info->event.u.err_msg.reserved = 0;

		core_info->event_cb(core_info->event_cb_priv,
			&core_info->event);
	}

	spin_lock_bh(&rdi_path->buffer_lock);

	ais_vfe_q_bufs_to_hw(core_info, path);

	spin_unlock_bh(&rdi_path->buffer_lock);
}
+16 −3
Original line number Diff line number Diff line
@@ -20,8 +20,9 @@
#include "ais_vfe_bus_ver2.h"
#include "ais_vfe_top_ver2.h"

#define AIS_VFE_WORKQ_NUM_TASK             16
#define AIS_VFE_WORKQ_NUM_TASK             20
#define AIS_VFE_MAX_BUF                    12
#define AIS_VFE_MAX_SOF_INFO               8

enum ais_vfe_hw_irq_event {
	AIS_VFE_HW_IRQ_EVENT_SOF,
@@ -83,6 +84,14 @@ struct ais_vfe_buffer_t {
	uint32_t                   bufIdx;
};

struct ais_sof_info_t {
	struct list_head        list;
	uint64_t                frame_cnt;
	uint64_t                sof_ts;
	uint64_t                cur_sof_hw_ts;
	uint64_t                prev_sof_hw_ts;
};

struct ais_vfe_rdi_output {
	enum ais_isp_resource_state      state;

@@ -96,8 +105,12 @@ struct ais_vfe_rdi_output {
	struct list_head                 free_buffer_list;

	uint64_t                         frame_cnt;
	uint64_t                         sof_ts;
	uint64_t                         sof_hw_ts;

	uint8_t                          num_sof_info_q;
	struct ais_sof_info_t            sof_info[AIS_VFE_MAX_SOF_INFO];
	struct list_head                 sof_info_q;
	struct list_head                 free_sof_info_list;
	struct ais_sof_info_t            last_sof_info;
};

struct ais_vfe_hw_core_info {
+1 −0
Original line number Diff line number Diff line
@@ -429,6 +429,7 @@ static struct ais_vfe_bus_ver2_hw_info vfe170_bus_hw_info = {
		.addr_sync_cfg                = 0x0000207C,
		.addr_sync_frame_hdr          = 0x00002080,
		.addr_sync_no_sync            = 0x00002084,
		.addr_fifo_status             = 0x000020A8,
		.debug_status_cfg             = 0x0000226C,
		.debug_status_0               = 0x00002270,
	},
Loading