msm: camera_v2: isp: handle frame drop due to scheduling latency
There is possibility that due to scheduling latency of tasklet, user threads VFE hardware could not be updated intime resulting in fatal error, invalid memory access. Added logic to track and gracefully handle the scheduling issues. Change-Id: I29acde4fe23a59e6ff0e5190e1c4b9c59f6ae08f Signed-off-by: Srikanth Uyyala <suyyala@codeaurora.org> Signed-off-by: Sumalatha Malothu <smalot@codeaurora.org>
This commit is contained in:
parent
ed8c0918ee
commit
28e17c1f65
4 changed files with 53 additions and 13 deletions
|
@ -1,4 +1,4 @@
|
||||||
/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
|
/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License version 2 and
|
* it under the terms of the GNU General Public License version 2 and
|
||||||
|
@ -746,6 +746,7 @@ struct msm_vfe_common_dev_data {
|
||||||
/* Irq debug Info */
|
/* Irq debug Info */
|
||||||
struct msm_vfe_irq_dump vfe_irq_dump;
|
struct msm_vfe_irq_dump vfe_irq_dump;
|
||||||
struct msm_vfe_tasklet tasklets[MAX_VFE + 1];
|
struct msm_vfe_tasklet tasklets[MAX_VFE + 1];
|
||||||
|
uint32_t drop_reconfig;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct msm_vfe_common_subdev {
|
struct msm_vfe_common_subdev {
|
||||||
|
@ -848,6 +849,9 @@ struct vfe_device {
|
||||||
/* total bandwidth per vfe */
|
/* total bandwidth per vfe */
|
||||||
uint64_t total_bandwidth;
|
uint64_t total_bandwidth;
|
||||||
struct isp_kstate *isp_page;
|
struct isp_kstate *isp_page;
|
||||||
|
|
||||||
|
/* irq info */
|
||||||
|
uint32_t irq_sof_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct vfe_parent_device {
|
struct vfe_parent_device {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
|
/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License version 2 and
|
* it under the terms of the GNU General Public License version 2 and
|
||||||
|
@ -714,8 +714,10 @@ void msm_isp47_preprocess_camif_irq(struct vfe_device *vfe_dev,
|
||||||
{
|
{
|
||||||
if (irq_status0 & BIT(3))
|
if (irq_status0 & BIT(3))
|
||||||
vfe_dev->axi_data.src_info[VFE_PIX_0].accept_frame = false;
|
vfe_dev->axi_data.src_info[VFE_PIX_0].accept_frame = false;
|
||||||
if (irq_status0 & BIT(0))
|
if (irq_status0 & BIT(0)) {
|
||||||
vfe_dev->axi_data.src_info[VFE_PIX_0].accept_frame = true;
|
vfe_dev->axi_data.src_info[VFE_PIX_0].accept_frame = true;
|
||||||
|
vfe_dev->irq_sof_id++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void msm_vfe47_reg_update(struct vfe_device *vfe_dev,
|
void msm_vfe47_reg_update(struct vfe_device *vfe_dev,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
|
/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License version 2 and
|
* it under the terms of the GNU General Public License version 2 and
|
||||||
|
@ -662,9 +662,11 @@ void msm_isp_process_reg_upd_epoch_irq(struct vfe_device *vfe_dev,
|
||||||
__msm_isp_axi_stream_update(stream_info, ts);
|
__msm_isp_axi_stream_update(stream_info, ts);
|
||||||
break;
|
break;
|
||||||
case MSM_ISP_COMP_IRQ_EPOCH:
|
case MSM_ISP_COMP_IRQ_EPOCH:
|
||||||
if (stream_info->state == ACTIVE)
|
if (stream_info->state == ACTIVE) {
|
||||||
msm_isp_update_framedrop_reg(stream_info,
|
msm_isp_update_framedrop_reg(stream_info,
|
||||||
vfe_dev->isp_page->drop_reconfig);
|
vfe_dev->common_data->drop_reconfig);
|
||||||
|
vfe_dev->common_data->drop_reconfig = 0;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
WARN(1, "Invalid irq %d\n", irq);
|
WARN(1, "Invalid irq %d\n", irq);
|
||||||
|
@ -2739,6 +2741,7 @@ int msm_isp_axi_reset(struct vfe_device *vfe_dev,
|
||||||
axi_data->src_info[SRC_TO_INTF(stream_info->
|
axi_data->src_info[SRC_TO_INTF(stream_info->
|
||||||
stream_src)].frame_id =
|
stream_src)].frame_id =
|
||||||
reset_cmd->frame_id;
|
reset_cmd->frame_id;
|
||||||
|
temp_vfe_dev->irq_sof_id = reset_cmd->frame_id;
|
||||||
}
|
}
|
||||||
msm_isp_reset_burst_count_and_frame_drop(
|
msm_isp_reset_burst_count_and_frame_drop(
|
||||||
vfe_dev, stream_info);
|
vfe_dev, stream_info);
|
||||||
|
@ -3011,6 +3014,7 @@ static void __msm_isp_stop_axi_streams(struct vfe_device *vfe_dev,
|
||||||
msm_isp_cfg_stream_scratch(stream_info, VFE_PING_FLAG);
|
msm_isp_cfg_stream_scratch(stream_info, VFE_PING_FLAG);
|
||||||
msm_isp_cfg_stream_scratch(stream_info, VFE_PONG_FLAG);
|
msm_isp_cfg_stream_scratch(stream_info, VFE_PONG_FLAG);
|
||||||
stream_info->undelivered_request_cnt = 0;
|
stream_info->undelivered_request_cnt = 0;
|
||||||
|
vfe_dev->irq_sof_id = 0;
|
||||||
for (k = 0; k < stream_info->num_isp; k++) {
|
for (k = 0; k < stream_info->num_isp; k++) {
|
||||||
vfe_dev = stream_info->vfe_dev[k];
|
vfe_dev = stream_info->vfe_dev[k];
|
||||||
if (stream_info->num_planes > 1)
|
if (stream_info->num_planes > 1)
|
||||||
|
@ -3174,7 +3178,6 @@ static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev_ioctl,
|
||||||
mutex_unlock(&vfe_dev_ioctl->buf_mgr->lock);
|
mutex_unlock(&vfe_dev_ioctl->buf_mgr->lock);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
msm_isp_calculate_bandwidth(stream_info);
|
msm_isp_calculate_bandwidth(stream_info);
|
||||||
for (k = 0; k < stream_info->num_isp; k++) {
|
for (k = 0; k < stream_info->num_isp; k++) {
|
||||||
msm_isp_get_stream_wm_mask(stream_info->vfe_dev[k],
|
msm_isp_get_stream_wm_mask(stream_info->vfe_dev[k],
|
||||||
|
@ -3520,16 +3523,25 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev,
|
||||||
*/
|
*/
|
||||||
if (vfe_dev->axi_data.src_info[frame_src].active &&
|
if (vfe_dev->axi_data.src_info[frame_src].active &&
|
||||||
frame_src == VFE_PIX_0 &&
|
frame_src == VFE_PIX_0 &&
|
||||||
vfe_dev->axi_data.src_info[frame_src].accept_frame == false) {
|
vfe_dev->axi_data.src_info[frame_src].accept_frame == false &&
|
||||||
|
(stream_info->undelivered_request_cnt <=
|
||||||
|
MAX_BUFFERS_IN_HW)
|
||||||
|
) {
|
||||||
pr_debug("%s:%d invalid time to request frame %d\n",
|
pr_debug("%s:%d invalid time to request frame %d\n",
|
||||||
__func__, __LINE__, frame_id);
|
__func__, __LINE__, frame_id);
|
||||||
vfe_dev->isp_page->drop_reconfig = 1;
|
vfe_dev->isp_page->drop_reconfig = 1;
|
||||||
|
/*keep it in vfe_dev variable also to avoid skip pattern
|
||||||
|
* programming the variable in page can be overwritten by MCT
|
||||||
|
*/
|
||||||
|
vfe_dev->common_data->drop_reconfig = 1;
|
||||||
} else if ((vfe_dev->axi_data.src_info[frame_src].active) &&
|
} else if ((vfe_dev->axi_data.src_info[frame_src].active) &&
|
||||||
(frame_id ==
|
((frame_id ==
|
||||||
vfe_dev->axi_data.src_info[frame_src].frame_id) &&
|
vfe_dev->axi_data.src_info[frame_src].frame_id) ||
|
||||||
|
(frame_id == vfe_dev->irq_sof_id)) &&
|
||||||
(stream_info->undelivered_request_cnt <=
|
(stream_info->undelivered_request_cnt <=
|
||||||
MAX_BUFFERS_IN_HW)) {
|
MAX_BUFFERS_IN_HW)) {
|
||||||
vfe_dev->isp_page->drop_reconfig = 1;
|
vfe_dev->isp_page->drop_reconfig = 1;
|
||||||
|
vfe_dev->common_data->drop_reconfig = 1;
|
||||||
pr_debug("%s: vfe_%d request_frame %d cur frame id %d pix %d\n",
|
pr_debug("%s: vfe_%d request_frame %d cur frame id %d pix %d\n",
|
||||||
__func__, vfe_dev->pdev->id, frame_id,
|
__func__, vfe_dev->pdev->id, frame_id,
|
||||||
vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id,
|
vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id,
|
||||||
|
@ -4263,9 +4275,25 @@ void msm_isp_process_axi_irq_stream(struct vfe_device *vfe_dev,
|
||||||
ISP_DBG("%s: Error configuring ping_pong\n",
|
ISP_DBG("%s: Error configuring ping_pong\n",
|
||||||
__func__);
|
__func__);
|
||||||
} else if (done_buf && (done_buf->is_drop_reconfig != 1)) {
|
} else if (done_buf && (done_buf->is_drop_reconfig != 1)) {
|
||||||
|
int32_t frame_id_diff;
|
||||||
|
/* irq_sof should be always >= tasklet SOF id
|
||||||
|
* For dual camera usecase irq_sof could be behind
|
||||||
|
* as software frameid sync logic epoch event could
|
||||||
|
* update slave frame id so update if irqsof < tasklet sof
|
||||||
|
*/
|
||||||
|
if (vfe_dev->irq_sof_id < frame_id)
|
||||||
|
vfe_dev->irq_sof_id = frame_id;
|
||||||
|
|
||||||
|
frame_id_diff = vfe_dev->irq_sof_id - frame_id;
|
||||||
|
if (stream_info->controllable_output && frame_id_diff > 1) {
|
||||||
|
/*scheduling problem need to do recovery*/
|
||||||
|
spin_unlock_irqrestore(&stream_info->lock, flags);
|
||||||
|
msm_isp_halt_send_error(vfe_dev,
|
||||||
|
ISP_EVENT_PING_PONG_MISMATCH);
|
||||||
|
return;
|
||||||
|
}
|
||||||
msm_isp_cfg_stream_scratch(stream_info, pingpong_status);
|
msm_isp_cfg_stream_scratch(stream_info, pingpong_status);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!done_buf) {
|
if (!done_buf) {
|
||||||
if (stream_info->buf_divert) {
|
if (stream_info->buf_divert) {
|
||||||
vfe_dev->error_info.stream_framedrop_count[
|
vfe_dev->error_info.stream_framedrop_count[
|
||||||
|
@ -4314,6 +4342,11 @@ void msm_isp_process_axi_irq_stream(struct vfe_device *vfe_dev,
|
||||||
* then dont issue buf-done for current buffer
|
* then dont issue buf-done for current buffer
|
||||||
*/
|
*/
|
||||||
done_buf->is_drop_reconfig = 0;
|
done_buf->is_drop_reconfig = 0;
|
||||||
|
if (!stream_info->buf[pingpong_bit]) {
|
||||||
|
/*samebuffer is not re-programeed so program scratch*/
|
||||||
|
msm_isp_cfg_stream_scratch(stream_info,
|
||||||
|
pingpong_status);
|
||||||
|
}
|
||||||
spin_unlock_irqrestore(&stream_info->lock, flags);
|
spin_unlock_irqrestore(&stream_info->lock, flags);
|
||||||
} else {
|
} else {
|
||||||
spin_unlock_irqrestore(&stream_info->lock, flags);
|
spin_unlock_irqrestore(&stream_info->lock, flags);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
|
/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License version 2 and
|
* it under the terms of the GNU General Public License version 2 and
|
||||||
|
@ -2291,7 +2291,8 @@ int msm_isp_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
|
||||||
vfe_dev->isp_raw0_debug = 0;
|
vfe_dev->isp_raw0_debug = 0;
|
||||||
vfe_dev->isp_raw1_debug = 0;
|
vfe_dev->isp_raw1_debug = 0;
|
||||||
vfe_dev->isp_raw2_debug = 0;
|
vfe_dev->isp_raw2_debug = 0;
|
||||||
|
vfe_dev->irq_sof_id = 0;
|
||||||
|
vfe_dev->common_data->drop_reconfig = 0;
|
||||||
if (vfe_dev->hw_info->vfe_ops.core_ops.init_hw(vfe_dev) < 0) {
|
if (vfe_dev->hw_info->vfe_ops.core_ops.init_hw(vfe_dev) < 0) {
|
||||||
pr_err("%s: init hardware failed\n", __func__);
|
pr_err("%s: init hardware failed\n", __func__);
|
||||||
vfe_dev->vfe_open_cnt--;
|
vfe_dev->vfe_open_cnt--;
|
||||||
|
|
Loading…
Add table
Reference in a new issue