From 28e17c1f65059cb0608003f30bdfaf9ecf3cf8f7 Mon Sep 17 00:00:00 2001 From: Srikanth Uyyala Date: Fri, 22 Mar 2019 10:41:46 +0530 Subject: [PATCH] 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 Signed-off-by: Sumalatha Malothu --- .../platform/msm/camera_v2/isp/msm_isp.h | 6 ++- .../platform/msm/camera_v2/isp/msm_isp47.c | 6 ++- .../msm/camera_v2/isp/msm_isp_axi_util.c | 49 ++++++++++++++++--- .../platform/msm/camera_v2/isp/msm_isp_util.c | 5 +- 4 files changed, 53 insertions(+), 13 deletions(-) diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h index 9bc6fde36774..cef2cd9ffcf1 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h @@ -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 * 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 */ struct msm_vfe_irq_dump vfe_irq_dump; struct msm_vfe_tasklet tasklets[MAX_VFE + 1]; + uint32_t drop_reconfig; }; struct msm_vfe_common_subdev { @@ -848,6 +849,9 @@ struct vfe_device { /* total bandwidth per vfe */ uint64_t total_bandwidth; struct isp_kstate *isp_page; + + /* irq info */ + uint32_t irq_sof_id; }; struct vfe_parent_device { diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c index a8341a7ff3e6..cf9e7547d4e2 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c @@ -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 * 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)) 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->irq_sof_id++; + } } void msm_vfe47_reg_update(struct vfe_device *vfe_dev, diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c index 79b45b6678b0..011a1d4019eb 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c @@ -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 * 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); break; case MSM_ISP_COMP_IRQ_EPOCH: - if (stream_info->state == ACTIVE) + if (stream_info->state == ACTIVE) { 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; default: 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-> stream_src)].frame_id = reset_cmd->frame_id; + temp_vfe_dev->irq_sof_id = reset_cmd->frame_id; } msm_isp_reset_burst_count_and_frame_drop( 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_PONG_FLAG); stream_info->undelivered_request_cnt = 0; + vfe_dev->irq_sof_id = 0; for (k = 0; k < stream_info->num_isp; k++) { vfe_dev = stream_info->vfe_dev[k]; 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); goto error; } - msm_isp_calculate_bandwidth(stream_info); for (k = 0; k < stream_info->num_isp; 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 && 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", __func__, __LINE__, frame_id); 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) && - (frame_id == - vfe_dev->axi_data.src_info[frame_src].frame_id) && + ((frame_id == + vfe_dev->axi_data.src_info[frame_src].frame_id) || + (frame_id == vfe_dev->irq_sof_id)) && (stream_info->undelivered_request_cnt <= MAX_BUFFERS_IN_HW)) { 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", __func__, vfe_dev->pdev->id, 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", __func__); } 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); } - if (!done_buf) { if (stream_info->buf_divert) { 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 */ 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); } else { spin_unlock_irqrestore(&stream_info->lock, flags); diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c index 684b331d9ac4..05c955ac7cd5 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c @@ -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 * 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_raw1_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) { pr_err("%s: init hardware failed\n", __func__); vfe_dev->vfe_open_cnt--;