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

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

msm: ais: fix start stop stability



Fix IFE BUS_WR irq handling to read status and lastaddr on receiving
interrupt and then send them as payload to deferred workq.
Stop CSID before VFE to ensure no SOF events are propagated as we are
stopping a session.
Always skip first frame at CSID as it is often a bad frame.

Change-Id: I31bdfa32fd8dbcc48d346a501b09fc08b541ea39
Signed-off-by: default avatarTerence Ho <terenceh@codeaurora.org>
parent 33325985
Loading
Loading
Loading
Loading
+27 −27
Original line number Diff line number Diff line
@@ -139,26 +139,23 @@ static int ais_ife_cmd_reserve(struct ais_ife_dev *p_ife_dev,

	rc = csid_drv->hw_ops.init(
		csid_drv->hw_priv, rdi_init, cmd_size);
	if (!rc) {
	if (rc)
		goto fail_csid_init;

	rc = vfe_drv->hw_ops.init(
			vfe_drv->hw_priv, rdi_init, cmd_size);
	if (rc)
		goto fail_vfe_init;
	}

	if (!rc) {
	rc = csid_drv->hw_ops.reserve(
			csid_drv->hw_priv, rdi_init, cmd_size);
	if (rc)
		goto fail_csid_reserve;
	}

	if (!rc) {
	rc = vfe_drv->hw_ops.reserve(
			vfe_drv->hw_priv, rdi_init, cmd_size);
	if (rc)
		goto fail_vfe_reserve;
	}

	return rc;

@@ -171,7 +168,7 @@ static int ais_ife_cmd_reserve(struct ais_ife_dev *p_ife_dev,
fail_vfe_init:
	csid_drv->hw_ops.deinit(
		csid_drv->hw_priv, &rdi_deinit, sizeof(rdi_deinit));

fail_csid_init:
	return rc;
}

@@ -189,6 +186,7 @@ static int ais_ife_cmd_release(struct ais_ife_dev *p_ife_dev,

	rc = csid_drv->hw_ops.release(
		csid_drv->hw_priv, rdi_deinit, cmd_size);

	tmp = vfe_drv->hw_ops.release(
		vfe_drv->hw_priv, rdi_deinit, cmd_size);
	if (!rc)
@@ -313,20 +311,22 @@ static int ais_ife_driver_cmd(struct ais_ife_dev *p_ife_dev, void *arg)
				cmd->size)) {
			rc = -EFAULT;
		} else {
			rc = csid_drv->hw_ops.start(
				csid_drv->hw_priv, &rdi_start, cmd->size);
			rc = vfe_drv->hw_ops.start(vfe_drv->hw_priv,
				&rdi_start, cmd->size);
			if (!rc) {
				rc = csid_drv->hw_ops.start(
					csid_drv->hw_priv, &rdi_start,
					cmd->size);
				if (rc) {
					struct ais_ife_rdi_stop_args rdi_stop;

					rdi_stop.path = rdi_start.path;
				rc = vfe_drv->hw_ops.start(vfe_drv->hw_priv,
					&rdi_start, cmd->size);
				if (rc)
					csid_drv->hw_ops.stop(csid_drv->hw_priv,
					vfe_drv->hw_ops.stop(vfe_drv->hw_priv,
						&rdi_stop, sizeof(rdi_stop));
				}
			}
		}
	}
		break;
	case AIS_IFE_STOP: {
		struct ais_ife_rdi_stop_args rdi_stop;
@@ -341,10 +341,10 @@ static int ais_ife_driver_cmd(struct ais_ife_dev *p_ife_dev, void *arg)
		} else {
			int tmp;

			rc = vfe_drv->hw_ops.stop(
				vfe_drv->hw_priv, &rdi_stop, cmd->size);
			tmp = csid_drv->hw_ops.stop(
			rc = csid_drv->hw_ops.stop(
				csid_drv->hw_priv, &rdi_stop, cmd->size);
			tmp = vfe_drv->hw_ops.stop(
				vfe_drv->hw_priv, &rdi_stop, cmd->size);
			if (!rc)
				rc = tmp;
		}
+25 −21
Original line number Diff line number Diff line
@@ -214,10 +214,8 @@ static int ais_ife_csid_path_reset(struct ais_ife_csid_hw *csid_hw,
		return -EINVAL;
	}

	reset_strb_addr =
			csid_reg->rdi_reg[id]->csid_rdi_rst_strobes_addr;
	complete =
			&csid_hw->csid_rdi_complete[id];
	reset_strb_addr = csid_reg->rdi_reg[id]->csid_rdi_rst_strobes_addr;
	complete = &csid_hw->csid_rdi_complete[id];

	/* Enable path reset done interrupt */
	val = cam_io_r_mb(soc_info->reg_map[0].mem_base +
@@ -226,7 +224,7 @@ static int ais_ife_csid_path_reset(struct ais_ife_csid_hw *csid_hw,
	cam_io_w_mb(val, soc_info->reg_map[0].mem_base +
			csid_reg->rdi_reg[id]->csid_rdi_irq_mask_addr);

	init_completion(complete);
	reinit_completion(complete);
	reset_strb_val = csid_reg->cmn_reg->path_rst_stb_all;

	/* Reset the corresponding ife csid path */
@@ -235,11 +233,11 @@ static int ais_ife_csid_path_reset(struct ais_ife_csid_hw *csid_hw,

	rc = wait_for_completion_timeout(complete,
		msecs_to_jiffies(IFE_CSID_TIMEOUT));
	if (rc <= 0) {
		CAM_ERR(CAM_ISP, "CSID:%d Res id %d fail rc = %d",
			 csid_hw->hw_intf->hw_idx,
			reset_path,  rc);
		if (rc == 0)
	if (rc) {
		rc = 0;
	} else {
		CAM_ERR(CAM_ISP, "CSID:%d RDI%d reset fail",
			 csid_hw->hw_intf->hw_idx, reset_path, rc);
		rc = -ETIMEDOUT;
	}

@@ -815,6 +813,7 @@ static int ais_ife_csid_enable_rdi_path(
	soc_info = &csid_hw->hw_info->soc_info;
	path_data = &csid_hw->rdi_cfg[id];

	path_data->init_frame_drop = 1;
	path_data->sof_cnt = 0;

	/* Enable the required RDI interrupts */
@@ -1326,7 +1325,7 @@ static int ais_ife_csid_start(void *hw_priv, void *start_args,

	if (start_cmd->path >= csid_reg->cmn_reg->num_rdis ||
		!csid_reg->rdi_reg[start_cmd->path]) {
		CAM_DBG(CAM_ISP, "CSID:%d RDI:%d is not supported on HW",
		CAM_ERR(CAM_ISP, "CSID:%d RDI:%d is not supported on HW",
			csid_hw->hw_intf->hw_idx, start_cmd->path);
		rc = -EINVAL;
		goto end;
@@ -1367,7 +1366,7 @@ static int ais_ife_csid_stop(void *hw_priv,

	if (stop_cmd->path >= csid_reg->cmn_reg->num_rdis ||
		!csid_reg->rdi_reg[stop_cmd->path]) {
		CAM_DBG(CAM_ISP, "CSID:%d RDI:%d is not supported on HW",
		CAM_ERR(CAM_ISP, "CSID:%d RDI:%d is not supported on HW",
			csid_hw->hw_intf->hw_idx, stop_cmd->path);
		rc = -EINVAL;
		goto end;
@@ -1677,6 +1676,7 @@ static irqreturn_t ais_ife_csid_irq(int irq_num, void *data)
	const struct ais_ife_csid_rdi_reg_offset       *rdi_reg;
	uint32_t i;
	uint32_t val, val2;
	uint32_t warn_cnt = 0;
	bool fatal_err_detected = false;
	uint32_t sof_irq_debug_en = 0;
	unsigned long flags;
@@ -1778,27 +1778,29 @@ static irqreturn_t ais_ife_csid_irq(int irq_num, void *data)
		}
		if (irq_status[CSID_IRQ_STATUS_RX] &
			CSID_CSI2_RX_ERROR_CPHY_EOT_RECEPTION) {
			csid_hw->error_irq_count++;
			warn_cnt++;
		}
		if (irq_status[CSID_IRQ_STATUS_RX] &
			CSID_CSI2_RX_ERROR_CPHY_SOT_RECEPTION) {
			csid_hw->error_irq_count++;
			warn_cnt++;
		}
		if (irq_status[CSID_IRQ_STATUS_RX] &
			CSID_CSI2_RX_ERROR_STREAM_UNDERFLOW) {
			csid_hw->error_irq_count++;
			warn_cnt++;
		}
		if (irq_status[CSID_IRQ_STATUS_RX] &
			CSID_CSI2_RX_ERROR_UNBOUNDED_FRAME) {
			csid_hw->error_irq_count++;
			warn_cnt++;
		}
	}

	csid_hw->error_irq_count += warn_cnt;

	if (csid_hw->error_irq_count >
		AIS_IFE_CSID_MAX_IRQ_ERROR_COUNT) {
		fatal_err_detected = true;
		csid_hw->error_irq_count = 0;
	} else if (csid_hw->error_irq_count) {
	} else if (warn_cnt) {
		uint64_t timestamp;

		timestamp = (uint64_t)((ts.tv_sec * 1000000000) + ts.tv_nsec);
@@ -1951,18 +1953,20 @@ static irqreturn_t ais_ife_csid_irq(int irq_num, void *data)
			(path_data->init_frame_drop) &&
			(path_data->state ==
			AIS_ISP_RESOURCE_STATE_STREAMING)) {
			path_data[i].sof_cnt++;
			path_data->sof_cnt++;
			CAM_DBG(CAM_ISP,
				"CSID:%d RDI:%d SOF cnt:%d init_frame_drop:%d",
				csid_hw->hw_intf->hw_idx, i,
				path_data[i].sof_cnt,
				path_data->sof_cnt,
				path_data->init_frame_drop);
			if (path_data[i].sof_cnt ==
			if (path_data->sof_cnt ==
				path_data->init_frame_drop) {
				cam_io_w_mb(AIS_CSID_RESUME_AT_FRAME_BOUNDARY,
					soc_info->reg_map[0].mem_base +
					rdi_reg->csid_rdi_ctrl_addr);

				path_data->init_frame_drop = 0;

				if (!(csid_hw->csid_debug &
					CSID_DEBUG_ENABLE_SOF_IRQ)) {
					val = cam_io_r_mb(
+67 −31
Original line number Diff line number Diff line
@@ -99,7 +99,7 @@ static int ais_vfe_bus_hw_init(struct ais_vfe_hw_core_info *core_info)
	cam_io_w_mb(core_info->irq_mask0,
		core_info->mem_base + AIS_VFE_IRQ_MASK0);

	cam_io_w_mb(0x7800,
	cam_io_w_mb(0x7801,
		core_info->mem_base + bus_hw_irq_regs[0].mask_reg_offset);
	cam_io_w_mb(0x0,
		core_info->mem_base + bus_hw_irq_regs[1].mask_reg_offset);
@@ -220,7 +220,8 @@ static int ais_vfe_reset(void *hw_priv,
	CAM_DBG(CAM_ISP, "waiting for vfe reset complete");

	/* Wait for Completion or Timeout of 500ms */
	rc = wait_for_completion_timeout(&vfe_hw->hw_complete, 500);
	rc = wait_for_completion_timeout(&vfe_hw->hw_complete,
					msecs_to_jiffies(500));
	if (rc) {
		rc = 0;
	} else {
@@ -622,17 +623,28 @@ int ais_vfe_stop(void *hw_priv, void *stop_args, uint32_t arg_size)

	rdi_path->state = AIS_ISP_RESOURCE_STATE_INIT_HW;

	core_info->bus_wr_mask1 &= ~(1 << stop_cmd->path);
	cam_io_w_mb(core_info->bus_wr_mask1,
		core_info->mem_base + bus_hw_irq_regs[1].mask_reg_offset);

	/* Disable WM and reg-update */
	cam_io_w_mb(0x0, core_info->mem_base + client_regs->cfg);
	cam_io_w_mb(AIS_VFE_REGUP_RDI_ALL, core_info->mem_base +
			top_hw_info->common_reg->reg_update_cmd);

	/* issue bus wr reset and wait for reset ack */
	reinit_completion(&vfe_hw->hw_complete);

	cam_io_w_mb((1 << stop_cmd->path), core_info->mem_base +
			bus_hw_info->common_reg.sw_reset);

	core_info->bus_wr_mask1 &= ~(1 << stop_cmd->path);
	cam_io_w_mb(core_info->bus_wr_mask1,
		core_info->mem_base + bus_hw_irq_regs[1].mask_reg_offset);
	/* Wait for completion or timeout of 50ms */
	rc = wait_for_completion_timeout(&vfe_hw->hw_complete,
					msecs_to_jiffies(50));
	if (rc)
		rc = 0;
	else
		CAM_WARN(CAM_ISP, "Reset Bus WR timeout");

	ais_clear_rdi_path(rdi_path);

@@ -892,7 +904,8 @@ static int ais_vfe_handle_error(

static void ais_vfe_bus_handle_client_frame_done(
	struct ais_vfe_hw_core_info *core_info,
	enum ais_ife_output_path_id path)
	enum ais_ife_output_path_id path,
	uint32_t last_addr)
{
	struct ais_vfe_rdi_output         *rdi_path = NULL;
	struct ais_vfe_buffer_t           *vfe_buf = NULL;
@@ -921,7 +934,7 @@ static void ais_vfe_bus_handle_client_frame_done(
		CAM_DBG(CAM_ISP, "IFE%d BUF| RDI%d 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, rdi_path->last_addr);
			rdi_path->num_buffer_hw_q, last_addr);

		core_info->event.type = AIS_IFE_MSG_FRAME_DONE;
		core_info->event.path = path;
@@ -933,15 +946,18 @@ static void ais_vfe_bus_handle_client_frame_done(
		core_info->event_cb(core_info->event_cb_priv,
				&core_info->event);

		if (rdi_path->last_addr == vfe_buf->iova_addr)
		if (last_addr == vfe_buf->iova_addr)
			last_addr_match = true;
		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)
		CAM_ERR(CAM_ISP, "IFE%d BUF| RDI%d NO MATCH addr 0x%x",
			core_info->vfe_idx, path, rdi_path->last_addr);
			core_info->vfe_idx, path, last_addr);

	ais_vfe_queue_to_hw(core_info, path);

@@ -950,9 +966,10 @@ static void ais_vfe_bus_handle_client_frame_done(

static int ais_vfe_bus_handle_frame_done(
	struct ais_vfe_hw_core_info *core_info,
	uint32_t client_mask)
	struct ais_vfe_hw_work_data *work_data)
{
	struct ais_vfe_rdi_output *p_rdi = &core_info->rdi_out[0];
	uint32_t client_mask = work_data->bus_wr_status[1];
	uint32_t client;
	int rc = 0;

@@ -967,19 +984,18 @@ static int ais_vfe_bus_handle_frame_done(

		if (client_mask & (0x1 << client)) {
			//process frame done
			ais_vfe_bus_handle_client_frame_done(core_info, client);
			ais_vfe_bus_handle_client_frame_done(core_info,
				client, work_data->last_addr[client]);
		}
	}

	return rc;
}

static int ais_vfe_handle_bus_wr_irq(struct cam_hw_info *vfe_hw,
static void ais_vfe_irq_fill_bus_wr_status(
	struct ais_vfe_hw_core_info *core_info,
	struct ais_vfe_hw_work_data *work_data)
{
	int rc = 0;
	uint32_t vfe_bus_status[3] = {};
	struct ais_vfe_bus_ver2_hw_info   *bus_hw_info = NULL;
	struct ais_irq_register_set       *bus_hw_irq_regs = NULL;
	struct ais_vfe_bus_ver2_reg_offset_bus_client  *client_regs = NULL;
@@ -989,53 +1005,72 @@ static int ais_vfe_handle_bus_wr_irq(struct cam_hw_info *vfe_hw,
	bus_hw_irq_regs = bus_hw_info->common_reg.irq_reg_info.irq_reg_set;
	client_regs = &bus_hw_info->bus_client_reg[client];

	vfe_bus_status[0] = cam_io_r_mb(core_info->mem_base +
	work_data->bus_wr_status[0] = cam_io_r_mb(core_info->mem_base +
		bus_hw_irq_regs[0].status_reg_offset);
	vfe_bus_status[1] = cam_io_r_mb(core_info->mem_base +
	work_data->bus_wr_status[1] = cam_io_r_mb(core_info->mem_base +
		bus_hw_irq_regs[1].status_reg_offset);
	vfe_bus_status[2] = cam_io_r_mb(core_info->mem_base +
	work_data->bus_wr_status[2] = cam_io_r_mb(core_info->mem_base +
		bus_hw_irq_regs[2].status_reg_offset);

	if (vfe_bus_status[1]) {
	if (work_data->bus_wr_status[1]) {
		struct ais_vfe_rdi_output *p_rdi;

		for (client = 0 ; client < AIS_IFE_PATH_MAX; client++) {
			if (vfe_bus_status[1] & (0x1 << client)) {
			if (work_data->bus_wr_status[1] & (0x1 << client)) {
				p_rdi = &core_info->rdi_out[client];
				client_regs =
					&bus_hw_info->bus_client_reg[client];
				p_rdi->last_addr = cam_io_r(
				work_data->last_addr[client] = cam_io_r(
					core_info->mem_base +
					client_regs->status0);
			}
		}
	}

	cam_io_w(vfe_bus_status[0], core_info->mem_base +
	cam_io_w(work_data->bus_wr_status[0], core_info->mem_base +
		bus_hw_irq_regs[0].clear_reg_offset);
	cam_io_w(vfe_bus_status[1], core_info->mem_base +
	cam_io_w(work_data->bus_wr_status[1], core_info->mem_base +
		bus_hw_irq_regs[1].clear_reg_offset);
	cam_io_w(vfe_bus_status[2], core_info->mem_base +
	cam_io_w(work_data->bus_wr_status[2], core_info->mem_base +
		bus_hw_irq_regs[2].clear_reg_offset);
	cam_io_w_mb(0x1, core_info->mem_base +
		bus_hw_info->common_reg.irq_reg_info.global_clear_offset);
}

static int ais_vfe_handle_bus_wr_irq(struct cam_hw_info *vfe_hw,
	struct ais_vfe_hw_core_info *core_info,
	struct ais_vfe_hw_work_data *work_data)
{
	int rc = 0;
	struct ais_vfe_bus_ver2_hw_info   *bus_hw_info = NULL;
	struct ais_irq_register_set       *bus_hw_irq_regs = NULL;
	struct ais_vfe_bus_ver2_reg_offset_bus_client  *client_regs = NULL;
	uint32_t client = 0;

	bus_hw_info = core_info->vfe_hw_info->bus_hw_info;
	bus_hw_irq_regs = bus_hw_info->common_reg.irq_reg_info.irq_reg_set;
	client_regs = &bus_hw_info->bus_client_reg[client];


	CAM_DBG(CAM_ISP, "VFE%d BUS status 0x%x 0x%x 0x%x", core_info->vfe_idx,
		vfe_bus_status[0], vfe_bus_status[1], vfe_bus_status[2]);
		work_data->bus_wr_status[0],
		work_data->bus_wr_status[1],
		work_data->bus_wr_status[2]);

	if (vfe_bus_status[1])
		ais_vfe_bus_handle_frame_done(core_info, vfe_bus_status[1]);
	if (work_data->bus_wr_status[1])
		ais_vfe_bus_handle_frame_done(core_info, work_data);

	if (vfe_bus_status[0] & 0x7800)	{
	if (work_data->bus_wr_status[0] & 0x7800) {
		CAM_ERR(CAM_ISP, "VFE%d: WR BUS error occurred status = 0x%x",
			core_info->vfe_idx, vfe_bus_status[0]);
		work_data->path = (vfe_bus_status[0] >> 11) & 0xF;
			core_info->vfe_idx, work_data->bus_wr_status[0]);
		work_data->path = (work_data->bus_wr_status[0] >> 11) & 0xF;
		rc = ais_vfe_handle_error(core_info, work_data);
	}

	if (vfe_bus_status[0] & 0x1) {
	if (work_data->bus_wr_status[0] & 0x1) {
		CAM_INFO(CAM_ISP, "VFE%d: WR BUS reset completed",
			core_info->vfe_idx);
		complete(&vfe_hw->hw_complete);
	}

	return rc;
@@ -1190,6 +1225,7 @@ irqreturn_t ais_vfe_irq(int irq_num, void *data)
			//BUS_WR IRQ
			CAM_DBG(CAM_ISP, "IFE%d BUS_WR", core_info->vfe_idx);
			work_data.evt_type = AIS_VFE_HW_IRQ_EVENT_BUS_WR;
			ais_vfe_irq_fill_bus_wr_status(core_info, &work_data);
			ais_vfe_dispatch_irq(vfe_hw, &work_data);
		}
		if (ife_status[1]) {
+2 −1
Original line number Diff line number Diff line
@@ -40,6 +40,8 @@ struct ais_vfe_hw_work_data {
	enum ais_vfe_hw_irq_event evt_type;
	uint32_t           path;
	uint64_t           ts;
	uint32_t           bus_wr_status[3];
	uint32_t           last_addr[AIS_IFE_PATH_MAX];
	struct ais_ife_rdi_timestamps ts_hw[AIS_IFE_PATH_MAX];
};

@@ -93,7 +95,6 @@ struct ais_vfe_rdi_output {
	struct list_head                 buffer_hw_q;
	struct list_head                 free_buffer_list;

	uint32_t                         last_addr;
	uint64_t                         frame_cnt;
	uint64_t                         sof_ts;
	uint64_t                         sof_hw_ts;
+2 −1
Original line number Diff line number Diff line
@@ -417,7 +417,8 @@ int cam_vfe_reset(void *hw_priv, void *reset_core_args, uint32_t arg_size)
		reset_core_args, arg_size);
	CAM_DBG(CAM_ISP, "waiting for vfe reset complete");
	/* Wait for Completion or Timeout of 500ms */
	rc = wait_for_completion_timeout(&vfe_hw->hw_complete, 500);
	rc = wait_for_completion_timeout(&vfe_hw->hw_complete,
					msecs_to_jiffies(500));
	if (!rc)
		CAM_ERR(CAM_ISP, "Error! Reset Timeout");