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

Commit 9d1a75e4 authored by Peter Liu's avatar Peter Liu
Browse files

msm: camera: add support for bus overflow recovery



When bus overflow is detected, system can enter bad state. So we
halt, reset and restart the AXI Bus Bridge, CAMIF and ISPIF INTF
in order to recover.
On overflow detection, kernel halts the AXI Bus Bridge and sends ERROR
message to user spacce and wait for it to trigger reset and restart.

Change-Id: Ie473d230d4f64b6fd3a0d98f181f4c82a48dab03
Signed-off-by: default avatarPeter Liu <pingchie@codeaurora.org>
parent 8aa4fe35
Loading
Loading
Loading
Loading
+75 −7
Original line number Diff line number Diff line
@@ -37,7 +37,7 @@
#undef CDBG
#define CDBG(fmt, args...) pr_debug(fmt, ##args)

static struct msm_isp_bufq *msm_isp_get_bufq(
struct msm_isp_bufq *msm_isp_get_bufq(
	struct msm_isp_buf_mgr *buf_mgr,
	uint32_t bufq_handle)
{
@@ -497,6 +497,11 @@ static int msm_isp_put_buf(struct msm_isp_buf_mgr *buf_mgr,
		return rc;
	}


	buf_info->buf_get_count = 0;
	buf_info->buf_put_count = 0;
	memset(buf_info->buf_used, 0, sizeof(buf_info->buf_used));

	spin_lock_irqsave(&bufq->bufq_lock, flags);
	switch (buf_info->state) {
	case MSM_ISP_BUFFER_STATE_PREPARED:
@@ -529,6 +534,53 @@ static int msm_isp_put_buf(struct msm_isp_buf_mgr *buf_mgr,
	return rc;
}

static int msm_isp_put_buf_unsafe(struct msm_isp_buf_mgr *buf_mgr,
	uint32_t bufq_handle, uint32_t buf_index)
{
	int rc = -1;
	struct msm_isp_bufq *bufq = NULL;
	struct msm_isp_buffer *buf_info = NULL;

	bufq = msm_isp_get_bufq(buf_mgr, bufq_handle);
	if (!bufq) {
		pr_err("%s: Invalid bufq\n", __func__);
		return rc;
	}

	buf_info = msm_isp_get_buf_ptr(buf_mgr, bufq_handle, buf_index);
	if (!buf_info) {
		pr_err("%s: buf not found\n", __func__);
		return rc;
	}

	switch (buf_info->state) {
	case MSM_ISP_BUFFER_STATE_PREPARED:
	case MSM_ISP_BUFFER_STATE_DEQUEUED:
	case MSM_ISP_BUFFER_STATE_DIVERTED:
		if (BUF_SRC(bufq->stream_id))
			list_add_tail(&buf_info->list, &bufq->head);
		else
			buf_mgr->vb2_ops->put_buf(buf_info->vb2_buf,
				bufq->session_id, bufq->stream_id);
		buf_info->state = MSM_ISP_BUFFER_STATE_QUEUED;
		rc = 0;
		break;
	case MSM_ISP_BUFFER_STATE_DISPATCHED:
		buf_info->state = MSM_ISP_BUFFER_STATE_QUEUED;
		rc = 0;
		break;
	case MSM_ISP_BUFFER_STATE_QUEUED:
		rc = 0;
		break;
	default:
		pr_err("%s: incorrect state = %d",
			__func__, buf_info->state);
		break;
	}

	return rc;
}

static int msm_isp_buf_done(struct msm_isp_buf_mgr *buf_mgr,
	uint32_t bufq_handle, uint32_t buf_index,
	struct timeval *tv, uint32_t frame_id, uint32_t output_format)
@@ -607,19 +659,34 @@ static int msm_isp_flush_buf(struct msm_isp_buf_mgr *buf_mgr,
			pr_err("%s: buf not found\n", __func__);
			continue;
		}

		spin_lock_irqsave(&bufq->bufq_lock, flags);
		if (flush_type == MSM_ISP_BUFFER_FLUSH_DIVERTED &&
			buf_info->state == MSM_ISP_BUFFER_STATE_DIVERTED) {
			buf_info->state = MSM_ISP_BUFFER_STATE_QUEUED;
		} else if (flush_type == MSM_ISP_BUFFER_FLUSH_ALL &&
			(buf_info->state == MSM_ISP_BUFFER_STATE_DEQUEUED ||
			buf_info->state == MSM_ISP_BUFFER_STATE_DIVERTED ||
			buf_info->state == MSM_ISP_BUFFER_STATE_DISPATCHED)) {
			buf_info->state = MSM_ISP_BUFFER_STATE_QUEUED;
		} else if (flush_type == MSM_ISP_BUFFER_FLUSH_ALL) {
			if (buf_info->state == MSM_ISP_BUFFER_STATE_DIVERTED) {
				CDBG("%s: no need to queue Diverted buffer\n",
					__func__);
			} else if (buf_info->state ==
				MSM_ISP_BUFFER_STATE_DEQUEUED) {
				if (buf_info->buf_get_count ==
					ISP_SHARE_BUF_CLIENT) {
					msm_isp_put_buf_unsafe(buf_mgr,
						bufq_handle, buf_info->buf_idx);
				} else {
					buf_info->state =
						MSM_ISP_BUFFER_STATE_DEQUEUED;
					buf_info->buf_get_count = 0;
					buf_info->buf_put_count = 0;
					memset(buf_info->buf_used, 0,
						sizeof(uint8_t) * 2);
				}
			}
		}

		spin_unlock_irqrestore(&bufq->bufq_lock, flags);
	}

	return 0;
}

@@ -1074,6 +1141,7 @@ static struct msm_isp_buf_ops isp_buf_ops = {
	.buf_mgr_init = msm_isp_init_isp_buf_mgr,
	.buf_mgr_deinit = msm_isp_deinit_isp_buf_mgr,
	.buf_mgr_debug = msm_isp_buf_mgr_debug,
	.get_bufq = msm_isp_get_bufq,
};

int msm_isp_create_isp_buf_mgr(
+2 −0
Original line number Diff line number Diff line
@@ -146,6 +146,8 @@ struct msm_isp_buf_ops {
		const char *ctx_name, uint16_t num_buf_q);
	int (*buf_mgr_deinit) (struct msm_isp_buf_mgr *buf_mgr);
	int (*buf_mgr_debug) (struct msm_isp_buf_mgr *buf_mgr);
	struct msm_isp_bufq * (*get_bufq)(struct msm_isp_buf_mgr *buf_mgr,
		uint32_t bufq_handle);
};

struct msm_isp_buf_mgr {
+27 −2
Original line number Diff line number Diff line
@@ -154,12 +154,15 @@ struct msm_vfe_axi_ops {
	uint32_t (*get_wm_mask) (uint32_t irq_status0, uint32_t irq_status1);
	uint32_t (*get_comp_mask) (uint32_t irq_status0, uint32_t irq_status1);
	uint32_t (*get_pingpong_status) (struct vfe_device *vfe_dev);
	long (*halt) (struct vfe_device *vfe_dev);
	int (*halt) (struct vfe_device *vfe_dev, uint32_t blocking);
	int (*restart) (struct vfe_device *vfe_dev, uint32_t blocking,
		uint32_t enable_camif);
};

struct msm_vfe_core_ops {
	void (*reg_update) (struct vfe_device *vfe_dev);
	long (*reset_hw) (struct vfe_device *vfe_dev);
	long (*reset_hw) (struct vfe_device *vfe_dev, uint32_t first_start,
		uint32_t blocking_call);
	int (*init_hw) (struct vfe_device *vfe_dev);
	void (*init_hw_reg) (struct vfe_device *vfe_dev);
	void (*release_hw) (struct vfe_device *vfe_dev);
@@ -173,6 +176,14 @@ struct msm_vfe_core_ops {
	int (*get_platform_data) (struct vfe_device *vfe_dev);
	void (*get_error_mask) (uint32_t *error_mask0, uint32_t *error_mask1);
	void (*process_error_status) (struct vfe_device *vfe_dev);
	void (*get_overflow_mask) (uint32_t *overflow_mask);
	void (*get_irq_mask) (struct vfe_device *vfe_dev,
		uint32_t *irq0_mask, uint32_t *irq1_mask);
	void (*restore_irq_mask) (struct vfe_device *vfe_dev);
	void (*get_halt_restart_mask) (uint32_t *irq0_mask,
		uint32_t *irq1_mask);
	void (*get_rdi_wm_mask)(struct vfe_device *vfe_dev,
		uint32_t *rdi_wm_mask);
};
struct msm_vfe_stats_ops {
	int (*get_stats_idx) (enum msm_isp_stats_type stats_type);
@@ -290,6 +301,7 @@ struct msm_vfe_axi_stream {
	enum msm_vfe_axi_stream_type stream_type;
	uint32_t vt_enable;
	uint32_t frame_based;
	enum msm_vfe_frame_skip_pattern frame_skip_pattern;
	uint32_t framedrop_period;
	uint32_t framedrop_pattern;
	uint32_t num_burst_capture;/*number of frame to capture*/
@@ -343,6 +355,9 @@ struct msm_vfe_axi_shared_data {
	enum msm_wm_ub_cfg_type wm_ub_cfg_policy;
	uint8_t num_used_wm;
	uint8_t num_active_stream;
	uint8_t num_rdi_stream;
	uint8_t num_pix_stream;
	uint32_t rdi_wm_mask;
	struct msm_vfe_axi_composite_info
		composite_info[MAX_NUM_COMPOSITE_MASK];
	uint8_t num_used_composite_mask;
@@ -406,7 +421,17 @@ struct msm_vfe_tasklet_queue_cmd {

#define MSM_VFE_TASKLETQ_SIZE 200

enum msm_vfe_overflow_state {
	NO_OVERFLOW,
	OVERFLOW_DETECTED,
	HALT_REQUESTED,
	RESTART_REQUESTED,
};

struct msm_vfe_error_info {
	atomic_t overflow_state;
	uint32_t overflow_recover_irq_mask0;
	uint32_t overflow_recover_irq_mask1;
	uint32_t error_mask0;
	uint32_t error_mask1;
	uint32_t violation_status;
+3 −7
Original line number Diff line number Diff line
@@ -410,7 +410,8 @@ static void msm_vfe32_reg_update(
	msm_camera_io_w_mb(0xF, vfe_dev->vfe_base + 0x260);
}

static long msm_vfe32_reset_hardware(struct vfe_device *vfe_dev)
static long msm_vfe32_reset_hardware(struct vfe_device *vfe_dev,
	uint32_t first_start, uint32_t blocking)
{
	init_completion(&vfe_dev->reset_complete);
	msm_camera_io_w_mb(0x3FF, vfe_dev->vfe_base + 0x4);
@@ -676,13 +677,8 @@ static void msm_vfe32_update_camif_state(
		msm_camera_io_w_mb(0x0, vfe_dev->vfe_base + 0x1E0);
		vfe_dev->axi_data.src_info[VFE_PIX_0].active = 0;
	} else if (update_state == DISABLE_CAMIF_IMMEDIATELY) {
		vfe_dev->ignore_error = 1;
		msm_camera_io_w_mb(0x6, vfe_dev->vfe_base + 0x1E0);
		vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev);
		vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev);
		vfe_dev->hw_info->vfe_ops.core_ops.init_hw_reg(vfe_dev);
		vfe_dev->axi_data.src_info[VFE_PIX_0].active = 0;
		vfe_dev->ignore_error = 0;
	}
}

@@ -907,7 +903,7 @@ static void msm_vfe32_update_ping_pong_addr(struct vfe_device *vfe_dev,
		VFE32_PING_PONG_BASE(wm_idx, pingpong_status));
}

static long msm_vfe32_axi_halt(struct vfe_device *vfe_dev)
static int msm_vfe32_axi_halt(struct vfe_device *vfe_dev, uint32_t blocking)
{
	uint32_t halt_mask;
	uint32_t axi_busy_flag = true;
+113 −17
Original line number Diff line number Diff line
@@ -404,8 +404,10 @@ static void msm_vfe40_process_reset_irq(struct vfe_device *vfe_dev,
static void msm_vfe40_process_halt_irq(struct vfe_device *vfe_dev,
	uint32_t irq_status0, uint32_t irq_status1)
{
	if (irq_status1 & (1 << 8))
	if (irq_status1 & (1 << 8)) {
		complete(&vfe_dev->halt_complete);
		msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x2C0);
	}
}

static void msm_vfe40_process_camif_irq(struct vfe_device *vfe_dev,
@@ -655,13 +657,30 @@ static void msm_vfe40_reg_update(struct vfe_device *vfe_dev)
	msm_camera_io_w_mb(0xF, vfe_dev->vfe_base + 0x378);
}

static long msm_vfe40_reset_hardware(struct vfe_device *vfe_dev)
static long msm_vfe40_reset_hardware(struct vfe_device *vfe_dev,
	uint32_t first_start, uint32_t blocking_call)
{
	long rc = 0;
	init_completion(&vfe_dev->reset_complete);

	if (first_start) {
		msm_camera_io_w_mb(0x1FF, vfe_dev->vfe_base + 0xC);
	return wait_for_completion_timeout(
	} else {
		msm_camera_io_w_mb(0x1EF, vfe_dev->vfe_base + 0xC);
		msm_camera_io_w(0x7FFFFFFF, vfe_dev->vfe_base + 0x30);
		msm_camera_io_w(0xFEFFFEFF, vfe_dev->vfe_base + 0x34);
		msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x24);
		vfe_dev->hw_info->vfe_ops.axi_ops.
			reload_wm(vfe_dev, 0x0003FFFF);
	}


	if (blocking_call) {
		rc = wait_for_completion_interruptible_timeout(
			&vfe_dev->reset_complete, msecs_to_jiffies(50));
	}
	return rc;
}

static void msm_vfe40_axi_reload_wm(
	struct vfe_device *vfe_dev, uint32_t reload_mask)
@@ -953,19 +972,15 @@ static void msm_vfe40_update_camif_state(struct vfe_device *vfe_dev,
		val &= 0xFFFFFF3F;
		val = val | bus_en << 7 | vfe_en << 6;
		msm_camera_io_w(val, vfe_dev->vfe_base + 0x2F8);
		msm_camera_io_w_mb(0x4, vfe_dev->vfe_base + 0x2F4);
		msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x2F4);
		vfe_dev->axi_data.src_info[VFE_PIX_0].active = 1;
	} else if (update_state == DISABLE_CAMIF) {
		msm_camera_io_w_mb(0x0, vfe_dev->vfe_base + 0x2F4);
		vfe_dev->axi_data.src_info[VFE_PIX_0].active = 0;
	} else if (update_state == DISABLE_CAMIF_IMMEDIATELY) {
		vfe_dev->ignore_error = 1;
		msm_camera_io_w_mb(0x6, vfe_dev->vfe_base + 0x2F4);
		vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev);
		vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev);
		vfe_dev->hw_info->vfe_ops.core_ops.init_hw_reg(vfe_dev);
		vfe_dev->axi_data.src_info[VFE_PIX_0].active = 0;
		vfe_dev->ignore_error = 0;
	}
}

@@ -1219,17 +1234,56 @@ static void msm_vfe40_update_ping_pong_addr(
		VFE40_PING_PONG_BASE(wm_idx, pingpong_status));
}

static long msm_vfe40_axi_halt(struct vfe_device *vfe_dev)
static int msm_vfe40_axi_halt(struct vfe_device *vfe_dev,
	uint32_t blocking)
{
	uint32_t halt_mask;
	halt_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x2C);
	halt_mask |= (1 << 8);
	msm_camera_io_w_mb(halt_mask, vfe_dev->vfe_base + 0x2C);
	int rc = 0;

	/* Keep only halt and restart mask */
	msm_camera_io_w(BIT(31), vfe_dev->vfe_base + 0x28);
	msm_camera_io_w(BIT(8), vfe_dev->vfe_base + 0x2C);
	/*Clear IRQ Status */
	msm_camera_io_w(0x7FFFFFFF, vfe_dev->vfe_base + 0x30);
	msm_camera_io_w(0xFEFFFEFF, vfe_dev->vfe_base + 0x34);
	msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x24);
	/* if any stream is waiting for update, signal complete */
	if (vfe_dev->axi_data.stream_update) {
		pr_err("%s: calling complete on stream update\n", __func__);
		complete(&vfe_dev->stream_config_complete);
	}
	/* Halt AXI Bus Bridge */
	init_completion(&vfe_dev->halt_complete);
	msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x2C0);
	return wait_for_completion_interruptible_timeout(
	if (blocking) {
		rc = wait_for_completion_interruptible_timeout(
			&vfe_dev->halt_complete, msecs_to_jiffies(500));
	}
	return rc;
}

static int msm_vfe40_axi_restart(struct vfe_device *vfe_dev,
	uint32_t blocking, uint32_t enable_camif)
{
	vfe_dev->hw_info->vfe_ops.core_ops.restore_irq_mask(vfe_dev);
	/* Clear IRQ Status */
	msm_camera_io_w(0x7FFFFFFF, vfe_dev->vfe_base + 0x30);
	msm_camera_io_w(0xFEFFFEFF, vfe_dev->vfe_base + 0x34);
	msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x24);

	/* Start AXI */
	msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x2C0);

	vfe_dev->hw_info->vfe_ops.core_ops.reg_update(vfe_dev);
	memset(&vfe_dev->error_info, 0, sizeof(vfe_dev->error_info));
	atomic_set(&vfe_dev->error_info.overflow_state, NO_OVERFLOW);

	if (enable_camif) {
		vfe_dev->hw_info->vfe_ops.core_ops.
		update_camif_state(vfe_dev, ENABLE_CAMIF);
	}

	return 0;
}

static uint32_t msm_vfe40_get_wm_mask(
	uint32_t irq_status0, uint32_t irq_status1)
@@ -1550,6 +1604,41 @@ static void msm_vfe40_get_error_mask(
	*error_mask1 = 0x00FFFEFF;
}

static void msm_vfe40_get_overflow_mask(uint32_t *overflow_mask)
{
	*overflow_mask = 0x00FFFE7E;
}

static void msm_vfe40_get_rdi_wm_mask(struct vfe_device *vfe_dev,
	uint32_t *rdi_wm_mask)
{
	*rdi_wm_mask = vfe_dev->axi_data.rdi_wm_mask;
}

static void msm_vfe40_get_irq_mask(struct vfe_device *vfe_dev,
	uint32_t *irq0_mask, uint32_t *irq1_mask)
{
	*irq0_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x28);
	*irq1_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x2C);
}


static void msm_vfe40_restore_irq_mask(struct vfe_device *vfe_dev)
{
	msm_camera_io_w(vfe_dev->error_info.overflow_recover_irq_mask0,
		vfe_dev->vfe_base + 0x28);
	msm_camera_io_w(vfe_dev->error_info.overflow_recover_irq_mask1,
		vfe_dev->vfe_base + 0x2C);
}


static void msm_vfe40_get_halt_restart_mask(uint32_t *irq0_mask,
	uint32_t *irq1_mask)
{
	*irq0_mask = BIT(31);
	*irq1_mask = BIT(8);
}

static struct msm_vfe_axi_hardware_info msm_vfe40_axi_hw_info = {
	.num_wm = 7,
	.num_comp_mask = 3,
@@ -1620,6 +1709,7 @@ struct msm_vfe_hardware_info vfe40_hw_info = {
			.get_wm_mask = msm_vfe40_get_wm_mask,
			.get_pingpong_status = msm_vfe40_get_pingpong_status,
			.halt = msm_vfe40_axi_halt,
			.restart = msm_vfe40_axi_restart,
		},
		.core_ops = {
			.reg_update = msm_vfe40_reg_update,
@@ -1632,6 +1722,12 @@ struct msm_vfe_hardware_info vfe40_hw_info = {
			.release_hw = msm_vfe40_release_hardware,
			.get_platform_data = msm_vfe40_get_platform_data,
			.get_error_mask = msm_vfe40_get_error_mask,
			.get_overflow_mask = msm_vfe40_get_overflow_mask,
			.get_rdi_wm_mask = msm_vfe40_get_rdi_wm_mask,
			.get_irq_mask = msm_vfe40_get_irq_mask,
			.restore_irq_mask = msm_vfe40_restore_irq_mask,
			.get_halt_restart_mask =
				msm_vfe40_get_halt_restart_mask,
			.process_error_status = msm_vfe40_process_error_status,
		},
		.stats_ops = {
Loading