Merge "msm📷 Bus overflow recovery mechanism"

This commit is contained in:
Linux Build Service Account
2014-05-27 22:50:32 -07:00
committed by Gerrit - the friendly Code Review server
6 changed files with 268 additions and 25 deletions
@@ -147,13 +147,13 @@ 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);
long (*halt) (struct vfe_device *vfe_dev, uint32_t blocking);
};
struct msm_vfe_core_ops {
void (*reg_update) (struct vfe_device *vfe_dev);
long (*reset_hw) (struct vfe_device *vfe_dev,
enum msm_isp_reset_type reset_type);
enum msm_isp_reset_type reset_type, uint32_t blocking);
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);
@@ -167,6 +167,12 @@ 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);
};
struct msm_vfe_stats_ops {
int (*get_stats_idx) (enum msm_isp_stats_type stats_type);
@@ -298,6 +304,15 @@ struct msm_vfe_axi_stream {
uint32_t runtime_num_burst_capture;
uint8_t runtime_framedrop_update;
uint32_t runtime_output_format;
enum msm_vfe_frame_skip_pattern frame_skip_pattern;
};
enum msm_vfe_overflow_state {
NO_OVERFLOW,
OVERFLOW_DETECTED,
HALT_REQUESTED,
RESTART_REQUESTED,
};
struct msm_vfe_axi_composite_info {
@@ -393,6 +408,9 @@ struct msm_vfe_tasklet_queue_cmd {
#define MSM_VFE_TASKLETQ_SIZE 200
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;
@@ -393,19 +393,25 @@ static uint32_t msm_vfe32_reset_values[ISP_RST_MAX] =
};
static long msm_vfe32_reset_hardware(struct vfe_device *vfe_dev ,
enum msm_isp_reset_type reset_type)
enum msm_isp_reset_type reset_type, uint32_t blocking)
{
uint32_t rst_val;
long rc = 0;
if (reset_type >= ISP_RST_MAX) {
pr_err("%s: Error Invalid parameter\n", __func__);
reset_type = ISP_RST_HARD;
}
rst_val = msm_vfe32_reset_values[reset_type];
init_completion(&vfe_dev->reset_complete);
msm_camera_io_w_mb(rst_val, vfe_dev->vfe_base + 0x4);
return wait_for_completion_timeout(
&vfe_dev->reset_complete, msecs_to_jiffies(50));
if (blocking) {
msm_camera_io_w_mb(rst_val, vfe_dev->vfe_base + 0x4);
rc = wait_for_completion_timeout(
&vfe_dev->reset_complete, msecs_to_jiffies(50));
} else {
msm_camera_io_w_mb(0x3EF, vfe_dev->vfe_base + 0x4);
}
return rc;
}
static void msm_vfe32_axi_reload_wm(
@@ -659,6 +665,7 @@ static void msm_vfe32_update_camif_state(
val &= 0xFFFFFF3F;
val = val | bus_en << 7 | vfe_en << 6;
msm_camera_io_w(val, vfe_dev->vfe_base + 0x1E4);
msm_camera_io_w_mb(0x4, vfe_dev->vfe_base + 0x1E0);
msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x1E0);
vfe_dev->axi_data.src_info[VFE_PIX_0].active = 1;
} else if (update_state == DISABLE_CAMIF) {
@@ -891,12 +898,15 @@ 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 long msm_vfe32_axi_halt(struct vfe_device *vfe_dev,
uint32_t blocking)
{
uint32_t halt_mask;
uint32_t axi_busy_flag = true;
uint32_t axi_busy_flag = false;
msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x1D8);
if (blocking) {
axi_busy_flag = true;
}
while (axi_busy_flag) {
if (msm_camera_io_r(
vfe_dev->vfe_base + 0x1DC) & 0x1)
@@ -1067,6 +1077,33 @@ static uint32_t msm_vfe32_stats_get_wm_mask(uint32_t irq_status0,
return (irq_status0 >> 13) & 0x7F;
}
static void msm_vfe32_get_overflow_mask(uint32_t *overflow_mask)
{
*overflow_mask = 0x002FFF7E;
}
static void msm_vfe32_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 + 0x1C);
*irq1_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x20);
}
static void msm_vfe32_restore_irq_mask(struct vfe_device *vfe_dev)
{
msm_camera_io_w(vfe_dev->error_info.overflow_recover_irq_mask0,
vfe_dev->vfe_base + 0x1C);
msm_camera_io_w(vfe_dev->error_info.overflow_recover_irq_mask1,
vfe_dev->vfe_base + 0x20);
}
static void msm_vfe32_get_halt_restart_mask(uint32_t *irq0_mask,
uint32_t *irq1_mask)
{
*irq0_mask = 0x0;
*irq1_mask = 0x01800000;
}
static uint32_t msm_vfe32_stats_get_comp_mask(uint32_t irq_status0,
uint32_t irq_status1)
{
@@ -1220,6 +1257,11 @@ struct msm_vfe_hardware_info vfe32_hw_info = {
.release_hw = msm_vfe32_release_hardware,
.get_platform_data = msm_vfe32_get_platform_data,
.get_error_mask = msm_vfe32_get_error_mask,
.get_overflow_mask = msm_vfe32_get_overflow_mask,
.get_irq_mask = msm_vfe32_get_irq_mask,
.restore_irq_mask = msm_vfe32_restore_irq_mask,
.get_halt_restart_mask =
msm_vfe32_get_halt_restart_mask,
.process_error_status = msm_vfe32_process_error_status,
},
.stats_ops = {
@@ -620,18 +620,24 @@ static uint32_t msm_vfe40_reset_values[ISP_RST_MAX] =
static long msm_vfe40_reset_hardware(struct vfe_device *vfe_dev ,
enum msm_isp_reset_type reset_type)
enum msm_isp_reset_type reset_type, uint32_t blocking)
{
uint32_t rst_val;
long rc = 0;
if (reset_type >= ISP_RST_MAX) {
pr_err("%s: Error Invalid parameter\n", __func__);
reset_type = ISP_RST_HARD;
}
rst_val = msm_vfe40_reset_values[reset_type];
init_completion(&vfe_dev->reset_complete);
msm_camera_io_w_mb(rst_val, vfe_dev->vfe_base + 0xC);
return wait_for_completion_timeout(
&vfe_dev->reset_complete, msecs_to_jiffies(50));
if (blocking) {
msm_camera_io_w_mb(rst_val, vfe_dev->vfe_base + 0xC);
rc = wait_for_completion_timeout(
&vfe_dev->reset_complete, msecs_to_jiffies(50));
} else {
msm_camera_io_w_mb(0x1EF, vfe_dev->vfe_base + 0xC);
}
return rc;
}
static void msm_vfe40_axi_reload_wm(
@@ -924,6 +930,7 @@ 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) {
@@ -1165,16 +1172,24 @@ 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 long 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);
long rc = 0;
/* Keep only restart mask and halt 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(0xFFFFFFFF, vfe_dev->vfe_base + 0x30);
msm_camera_io_w(0xFEFFFFFF, vfe_dev->vfe_base + 0x34);
init_completion(&vfe_dev->halt_complete);
msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x2C0);
return wait_for_completion_interruptible_timeout(
&vfe_dev->halt_complete, msecs_to_jiffies(500));
if (blocking) {
atomic_set(&vfe_dev->error_info.overflow_state, NO_OVERFLOW);
rc = wait_for_completion_interruptible_timeout(
&vfe_dev->halt_complete, msecs_to_jiffies(500));
}
return rc;
}
static uint32_t msm_vfe40_get_wm_mask(
@@ -1183,6 +1198,33 @@ static uint32_t msm_vfe40_get_wm_mask(
return (irq_status0 >> 8) & 0x7F;
}
static void msm_vfe40_get_overflow_mask(uint32_t *overflow_mask)
{
*overflow_mask = 0x00FFFE7E;
}
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 uint32_t msm_vfe40_get_comp_mask(
uint32_t irq_status0, uint32_t irq_status1)
{
@@ -1519,6 +1561,11 @@ 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_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 = {
@@ -478,7 +478,8 @@ void msm_isp_calculate_framedrop(
framedrop_period = msm_isp_get_framedrop_period(
stream_cfg_cmd->frame_skip_pattern);
stream_info->frame_skip_pattern =
stream_cfg_cmd->frame_skip_pattern;
if (stream_cfg_cmd->frame_skip_pattern == SKIP_ALL)
stream_info->framedrop_pattern = 0x0;
else
@@ -1331,9 +1332,9 @@ static int msm_isp_stop_axi_stream(struct vfe_device *vfe_dev,
if (cur_stream_cnt == 0) {
vfe_dev->ignore_error = 1;
if (camif_update == DISABLE_CAMIF_IMMEDIATELY) {
vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev);
vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev, 1);
}
vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev, ISP_RST_HARD);
vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev, ISP_RST_HARD, 1);
vfe_dev->hw_info->vfe_ops.core_ops.init_hw_reg(vfe_dev);
vfe_dev->ignore_error = 0;
}
@@ -37,6 +37,10 @@ int msm_isp_axi_check_stream_state(
struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd);
void msm_isp_calculate_framedrop(
struct msm_vfe_axi_shared_data *axi_data,
struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd);
int msm_isp_request_axi_stream(struct vfe_device *vfe_dev, void *arg);
int msm_isp_cfg_axi_stream(struct vfe_device *vfe_dev, void *arg);
int msm_isp_release_axi_stream(struct vfe_device *vfe_dev, void *arg);
@@ -981,6 +981,125 @@ static inline void msm_isp_update_error_info(struct vfe_device *vfe_dev,
vfe_dev->error_info.error_count++;
}
static inline void msm_isp_process_overflow_irq(
struct vfe_device *vfe_dev,
uint32_t *irq_status0, uint32_t *irq_status1)
{
uint32_t overflow_mask;
uint32_t halt_restart_mask0, halt_restart_mask1;
/*Mask out all other irqs if recovery is started*/
if (atomic_read(&vfe_dev->error_info.overflow_state) !=
NO_OVERFLOW) {
vfe_dev->hw_info->vfe_ops.core_ops.
get_halt_restart_mask(&halt_restart_mask0,
&halt_restart_mask1);
*irq_status0 &= halt_restart_mask0;
*irq_status1 &= halt_restart_mask1;
return;
}
/*Check if any overflow bit is set*/
vfe_dev->hw_info->vfe_ops.core_ops.
get_overflow_mask(&overflow_mask);
overflow_mask &= *irq_status1;
if (overflow_mask) {
pr_warning("%s: Bus overflow detected: 0x%x\n",
__func__, overflow_mask);
atomic_set(&vfe_dev->error_info.overflow_state,
OVERFLOW_DETECTED);
pr_warning("%s: Start bus overflow recovery\n", __func__);
/*Store current IRQ mask*/
vfe_dev->hw_info->vfe_ops.core_ops.get_irq_mask(vfe_dev,
&vfe_dev->error_info.overflow_recover_irq_mask0,
&vfe_dev->error_info.overflow_recover_irq_mask1);
/*Stop CAMIF Immediately*/
vfe_dev->hw_info->vfe_ops.core_ops.
update_camif_state(vfe_dev, DISABLE_CAMIF_IMMEDIATELY);
/*Halt the hardware & Clear all other IRQ mask*/
vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev, 0);
/*Update overflow state*/
atomic_set(&vfe_dev->error_info.overflow_state, HALT_REQUESTED);
*irq_status0 = 0;
*irq_status1 = 0;
}
}
static inline void msm_isp_reset_burst_count(
struct vfe_device *vfe_dev)
{
int i;
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
struct msm_vfe_axi_stream *stream_info;
struct msm_vfe_axi_stream_request_cmd framedrop_info;
for (i = 0; i < MAX_NUM_STREAM; i++) {
stream_info = &axi_data->stream_info[i];
if (stream_info->state != ACTIVE)
continue;
if (stream_info->stream_type == BURST_STREAM &&
stream_info->num_burst_capture != 0) {
framedrop_info.burst_count =
stream_info->num_burst_capture;
framedrop_info.frame_skip_pattern =
stream_info->frame_skip_pattern;
framedrop_info.init_frame_drop = 0;
msm_isp_calculate_framedrop(&vfe_dev->axi_data,
&framedrop_info);
}
}
}
static void msm_isp_process_overflow_recovery(
struct vfe_device *vfe_dev,
uint32_t irq_status0, uint32_t irq_status1)
{
uint32_t halt_restart_mask0, halt_restart_mask1;
vfe_dev->hw_info->vfe_ops.core_ops.
get_halt_restart_mask(&halt_restart_mask0,
&halt_restart_mask1);
irq_status0 &= halt_restart_mask0;
irq_status1 &= halt_restart_mask1;
if (irq_status0 == 0 && irq_status1 == 0)
return;
switch (atomic_read(&vfe_dev->error_info.overflow_state)) {
case HALT_REQUESTED: {
pr_err("%s: Halt done, Restart Pending\n", __func__);
/*Reset the hardware*/
vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev,
ISP_RST_SOFT, 0);
/*Update overflow state*/
atomic_set(&vfe_dev->error_info.overflow_state,
RESTART_REQUESTED);
}
break;
case RESTART_REQUESTED: {
pr_err("%s: Restart done, Resuming\n", __func__);
/*Reset the burst stream frame drop pattern, in the
*case where bus overflow happens during the burstshot,
*the framedrop pattern might be updated after reg update
*to skip all the frames after the burst shot. The burst shot
*might not be completed due to the overflow, so the framedrop
*pattern need to change back to the original settings in order
*to recovr from overflow.
*/
msm_isp_reset_burst_count(vfe_dev);
vfe_dev->hw_info->vfe_ops.axi_ops.
reload_wm(vfe_dev, 0xFFFFFFFF);
vfe_dev->hw_info->vfe_ops.core_ops.restore_irq_mask(vfe_dev);
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);
vfe_dev->hw_info->vfe_ops.core_ops.
update_camif_state(vfe_dev, ENABLE_CAMIF);
}
break;
case NO_OVERFLOW:
case OVERFLOW_DETECTED:
default:
break;
}
}
irqreturn_t msm_isp_process_irq(int irq_num, void *data)
{
unsigned long flags;
@@ -991,6 +1110,8 @@ irqreturn_t msm_isp_process_irq(int irq_num, void *data)
vfe_dev->hw_info->vfe_ops.irq_ops.
read_irq_status(vfe_dev, &irq_status0, &irq_status1);
msm_isp_process_overflow_irq(vfe_dev,
&irq_status0, &irq_status1);
vfe_dev->hw_info->vfe_ops.core_ops.
get_error_mask(&error_mask0, &error_mask1);
error_mask0 &= irq_status0;
@@ -1055,6 +1176,13 @@ void msm_isp_do_tasklet(unsigned long data)
irq_status1 = queue_cmd->vfeInterruptStatus1;
ts = queue_cmd->ts;
spin_unlock_irqrestore(&vfe_dev->tasklet_lock, flags);
if (atomic_read(&vfe_dev->error_info.overflow_state) !=
NO_OVERFLOW) {
pr_err("There is Overflow, kicking up recovery !!!!");
msm_isp_process_overflow_recovery(vfe_dev,
irq_status0, irq_status1);
continue;
}
ISP_DBG("%s: status0: 0x%x status1: 0x%x\n",
__func__, irq_status0, irq_status1);
irq_ops->process_reset_irq(vfe_dev,
@@ -1105,7 +1233,10 @@ int msm_isp_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
return -EBUSY;
}
rc = vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev, ISP_RST_HARD);
memset(&vfe_dev->error_info, 0, sizeof(vfe_dev->error_info));
atomic_set(&vfe_dev->error_info.overflow_state, NO_OVERFLOW);
rc = vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev,
ISP_RST_HARD, 1);
if (rc <= 0) {
pr_err("%s: reset timeout\n", __func__);
mutex_unlock(&vfe_dev->core_mutex);
@@ -1157,7 +1288,7 @@ int msm_isp_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
return -ENODEV;
}
rc = vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev);
rc = vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev, 1);
if (rc <= 0)
pr_err("%s: halt timeout rc=%ld\n", __func__, rc);