From a8838e1f7575a59699699579befa82cdb6c5d166 Mon Sep 17 00:00:00 2001 From: Cai YiWei Date: Mon, 12 Apr 2021 17:41:33 +0800 Subject: [PATCH] media: rockchip: isp: add monitor to restart if abnormal enable monitor: add rockchip,restart-monitor-en to rkisp node on dts or echo Y > /sys/module/video_rkisp/parameters/monitor NOTE: shouldn't enable this when porting camera!!! Change-Id: I35fa45488136e2e0ec16c4e6179a39f34cf5ebc9 Signed-off-by: Cai YiWei --- .../media/platform/rockchip/isp/capture_v20.c | 30 +- .../media/platform/rockchip/isp/capture_v21.c | 30 +- drivers/media/platform/rockchip/isp/common.h | 1 + drivers/media/platform/rockchip/isp/csi.c | 17 +- drivers/media/platform/rockchip/isp/csi.h | 4 +- drivers/media/platform/rockchip/isp/dev.c | 4 + drivers/media/platform/rockchip/isp/dev.h | 3 +- drivers/media/platform/rockchip/isp/hw.c | 35 +- drivers/media/platform/rockchip/isp/hw.h | 14 +- drivers/media/platform/rockchip/isp/procfs.c | 4 + drivers/media/platform/rockchip/isp/rkisp.c | 304 +++++++++++++++++- 11 files changed, 404 insertions(+), 42 deletions(-) diff --git a/drivers/media/platform/rockchip/isp/capture_v20.c b/drivers/media/platform/rockchip/isp/capture_v20.c index e3607fde30c3..38ecdbc9bef2 100644 --- a/drivers/media/platform/rockchip/isp/capture_v20.c +++ b/drivers/media/platform/rockchip/isp/capture_v20.c @@ -2226,7 +2226,7 @@ void rkisp_mi_v20_isr(u32 mis_val, struct rkisp_device *dev) end_tx0 = false; end_tx1 = false; end_tx2 = false; - rkisp_trigger_read_back(&dev->csi_dev, false, false); + rkisp_trigger_read_back(&dev->csi_dev, false, false, false); } } } @@ -2244,11 +2244,18 @@ void rkisp_mipi_v20_isr(unsigned int phy, unsigned int packet, v4l2_dbg(3, rkisp_debug, &dev->v4l2_dev, "csi state:0x%x\n", state); - if (phy && (dev->isp_inp & INP_CSI)) + dev->csi_dev.irq_cnt++; + if (phy && (dev->isp_inp & INP_CSI) && + dev->csi_dev.err_cnt++ < RKISP_CONTI_ERR_MAX) v4l2_warn(v4l2_dev, "MIPI error: phy: 0x%08x\n", phy); - if (packet && (dev->isp_inp & INP_CSI)) + if (packet && (dev->isp_inp & INP_CSI) && + dev->csi_dev.err_cnt < RKISP_CONTI_ERR_MAX) { + if (packet & 0xfff) + dev->csi_dev.err_cnt++; v4l2_warn(v4l2_dev, "MIPI error: packet: 0x%08x\n", packet); - if (overflow) + } + if (overflow && + dev->csi_dev.err_cnt++ < RKISP_CONTI_ERR_MAX) v4l2_warn(v4l2_dev, "MIPI error: overflow: 0x%08x\n", overflow); if (state & 0xeff00) v4l2_warn(v4l2_dev, "MIPI error: size: 0x%08x\n", state); @@ -2268,6 +2275,7 @@ void rkisp_mipi_v20_isr(unsigned int phy, unsigned int packet, } } if (state & (RAW0_WR_FRAME | RAW1_WR_FRAME | RAW2_WR_FRAME)) { + dev->csi_dev.err_cnt = 0; for (i = 0; i < HDR_DMA_MAX; i++) { if (!((RAW0_WR_FRAME << i) & state)) continue; @@ -2283,4 +2291,18 @@ void rkisp_mipi_v20_isr(unsigned int phy, unsigned int packet, if (state & (RAW0_Y_STATE | RAW1_Y_STATE | RAW2_Y_STATE | RAW0_WR_FRAME | RAW1_WR_FRAME | RAW2_WR_FRAME)) rkisp_luma_isr(&dev->luma_vdev, state); + + if (dev->csi_dev.err_cnt > RKISP_CONTI_ERR_MAX) { + if (!(dev->isp_state & ISP_MIPI_ERROR)) { + dev->isp_state |= ISP_MIPI_ERROR; + rkisp_write(dev, CSI2RX_MASK_PHY, 0, true); + rkisp_write(dev, CSI2RX_MASK_PACKET, 0, true); + rkisp_write(dev, CSI2RX_MASK_OVERFLOW, 0, true); + if (dev->hw_dev->monitor.is_en) { + if (!completion_done(&dev->hw_dev->monitor.cmpl)) + complete(&dev->hw_dev->monitor.cmpl); + dev->hw_dev->monitor.state |= ISP_MIPI_ERROR; + } + } + } } diff --git a/drivers/media/platform/rockchip/isp/capture_v21.c b/drivers/media/platform/rockchip/isp/capture_v21.c index cc873144f863..1e3c1f495e4a 100644 --- a/drivers/media/platform/rockchip/isp/capture_v21.c +++ b/drivers/media/platform/rockchip/isp/capture_v21.c @@ -1443,7 +1443,7 @@ void rkisp_mi_v21_isr(u32 mis_val, struct rkisp_device *dev) (dev->hdr.op_mode == HDR_RDBK_FRAME2 && end_tx2 && end_tx0))) { end_tx0 = false; end_tx2 = false; - rkisp_trigger_read_back(&dev->csi_dev, false, false); + rkisp_trigger_read_back(&dev->csi_dev, false, false, false); } } } @@ -1476,11 +1476,17 @@ void rkisp_mipi_v21_isr(unsigned int phy, unsigned int packet, v4l2_dbg(3, rkisp_debug, &dev->v4l2_dev, "csi state:0x%x\n", state); - if (phy && (dev->isp_inp & INP_CSI)) + dev->csi_dev.irq_cnt++; + if (phy && (dev->isp_inp & INP_CSI) && + dev->csi_dev.err_cnt++ < RKISP_CONTI_ERR_MAX) v4l2_warn(v4l2_dev, "MIPI error: phy: 0x%08x\n", phy); - if (packet && (dev->isp_inp & INP_CSI)) + if (packet && (dev->isp_inp & INP_CSI) && + dev->csi_dev.err_cnt < RKISP_CONTI_ERR_MAX) { + if (packet & 0xfff) + dev->csi_dev.err_cnt++; v4l2_warn(v4l2_dev, "MIPI error: packet: 0x%08x\n", packet); - if (overflow) + } + if (overflow && dev->csi_dev.err_cnt++ < RKISP_CONTI_ERR_MAX) v4l2_warn(v4l2_dev, "MIPI error: overflow: 0x%08x\n", overflow); if (state & 0xeff00) v4l2_warn(v4l2_dev, "MIPI error: size: 0x%08x\n", state); @@ -1501,6 +1507,7 @@ void rkisp_mipi_v21_isr(unsigned int phy, unsigned int packet, } } if (state & (RAW0_WR_FRAME | RAW1_WR_FRAME)) { + dev->csi_dev.err_cnt = 0; for (i = 0; i < HDR_DMA_MAX - 1; i++) { if (!((RAW0_WR_FRAME << i) & state)) continue; @@ -1513,7 +1520,22 @@ void rkisp_mipi_v21_isr(unsigned int phy, unsigned int packet, } } if (state & ISP21_RAW3_WR_FRAME) { + dev->csi_dev.err_cnt = 0; stream = &dev->cap_dev.stream[RKISP_STREAM_DMATX3]; atomic_inc(&stream->sequence); } + + if (dev->csi_dev.err_cnt > RKISP_CONTI_ERR_MAX) { + if (!(dev->isp_state & ISP_MIPI_ERROR)) { + dev->isp_state |= ISP_MIPI_ERROR; + rkisp_write(dev, CSI2RX_MASK_PHY, 0, true); + rkisp_write(dev, CSI2RX_MASK_PACKET, 0, true); + rkisp_write(dev, CSI2RX_MASK_OVERFLOW, 0, true); + if (dev->hw_dev->monitor.is_en) { + if (!completion_done(&dev->hw_dev->monitor.cmpl)) + complete(&dev->hw_dev->monitor.cmpl); + dev->hw_dev->monitor.state |= ISP_MIPI_ERROR; + } + } + } } diff --git a/drivers/media/platform/rockchip/isp/common.h b/drivers/media/platform/rockchip/isp/common.h index 3b3ee06eef98..cce1b9b6720a 100644 --- a/drivers/media/platform/rockchip/isp/common.h +++ b/drivers/media/platform/rockchip/isp/common.h @@ -135,6 +135,7 @@ struct rkisp_dummy_buffer { }; extern int rkisp_debug; +extern bool rkisp_monitor; extern u64 rkisp_debug_reg; extern struct platform_driver rkisp_plat_drv; diff --git a/drivers/media/platform/rockchip/isp/csi.c b/drivers/media/platform/rockchip/isp/csi.c index 6b533a6ed76f..b50f43f47bdc 100644 --- a/drivers/media/platform/rockchip/isp/csi.c +++ b/drivers/media/platform/rockchip/isp/csi.c @@ -133,6 +133,8 @@ static int rkisp_csi_s_stream(struct v4l2_subdev *sd, int on) struct rkisp_csi_device *csi = v4l2_get_subdevdata(sd); struct rkisp_device *dev = csi->ispdev; + csi->err_cnt = 0; + csi->irq_cnt = 0; memset(csi->tx_first, 0, sizeof(csi->tx_first)); if (!IS_HDR_RDBK(dev->hdr.op_mode)) @@ -321,7 +323,7 @@ static int csi_config(struct rkisp_csi_device *csi) rkisp_write(dev, CSI2RX_MASK_PHY, 0xF0FFFF, true); rkisp_write(dev, CSI2RX_MASK_PACKET, 0xF1FFFFF, true); rkisp_write(dev, CSI2RX_MASK_OVERFLOW, 0x7F7FF1, true); - rkisp_write(dev, CSI2RX_MASK_STAT, 0x7FFFFF7F, true); + rkisp_write(dev, CSI2RX_MASK_STAT, 0x7FFFFF0F, true); /* hdr merge */ switch (dev->hdr.op_mode) { @@ -463,10 +465,10 @@ int rkisp_csi_config_patch(struct rkisp_device *dev) } } rkisp_write(dev, ISP_HDRMGE_BASE, val, false); - rkisp_write(dev, CSI2RX_MASK_STAT, 0x7FFFFF7F, true); } if (IS_HDR_RDBK(dev->hdr.op_mode)) { + rkisp_write(dev, CSI2RX_MASK_STAT, 0x700FFF0F, true); rkisp_set_bits(dev, CTRL_SWS_CFG, 0, SW_MPIP_DROP_FRM_DIS, true); } @@ -483,7 +485,7 @@ int rkisp_csi_config_patch(struct rkisp_device *dev) * for hdr read back mode, rawrd read back data * this will update rawrd base addr to shadow. */ -void rkisp_trigger_read_back(struct rkisp_csi_device *csi, u8 dma2frm, u32 mode) +void rkisp_trigger_read_back(struct rkisp_csi_device *csi, u8 dma2frm, u32 mode, bool is_try) { struct rkisp_device *dev = csi->ispdev; struct rkisp_hw_dev *hw = dev->hw_dev; @@ -523,7 +525,7 @@ void rkisp_trigger_read_back(struct rkisp_csi_device *csi, u8 dma2frm, u32 mode) dev->skip_frame = 2; is_upd = true; } - if (dev->isp_ver == ISP_V20 && dev->dmarx_dev.trigger == T_MANUAL) { + if (dev->isp_ver == ISP_V20 && dev->dmarx_dev.trigger == T_MANUAL && !is_try) { if (csi->rd_mode != rd_mode && dev->br_dev.en) { tmp = dev->isp_sdev.in_crop.height; val = rkisp_read(dev, CIF_DUAL_CROP_CTRL, false); @@ -551,7 +553,7 @@ void rkisp_trigger_read_back(struct rkisp_csi_device *csi, u8 dma2frm, u32 mode) dev->isp_sdev.quantization); rkisp_params_cfg(params_vdev, cur_frame_id); - if (!hw->is_single) { + if (!hw->is_single && !is_try) { rkisp_update_regs(dev, CTRL_VI_ISP_PATH, SUPER_IMP_COLOR_CR); rkisp_update_regs(dev, DUAL_CROP_M_H_OFFS, DUAL_CROP_S_V_SIZE); rkisp_update_regs(dev, ISP_ACQ_PROP, DUAL_CROP_CTRL); @@ -637,6 +639,9 @@ static void rkisp_dev_trigger_handle(struct rkisp_device *dev, u32 cmd) hw->is_idle = false; if (!hw->is_idle) goto end; + if (hw->monitor.state & ISP_MIPI_ERROR && hw->monitor.is_en) + goto end; + for (i = 0; i < hw->dev_num; i++) { isp = hw->isp[i]; if (!(isp->isp_state & ISP_START)) @@ -667,7 +672,7 @@ static void rkisp_dev_trigger_handle(struct rkisp_device *dev, u32 cmd) end: spin_unlock_irqrestore(&hw->rdbk_lock, lock_flags); if (times >= 0) - rkisp_trigger_read_back(&isp->csi_dev, times, mode); + rkisp_trigger_read_back(&isp->csi_dev, times, mode, false); } /* handle read back event from user or isp idle isr */ diff --git a/drivers/media/platform/rockchip/isp/csi.h b/drivers/media/platform/rockchip/isp/csi.h index a73c455d8f51..75e94ff1c9d5 100644 --- a/drivers/media/platform/rockchip/isp/csi.h +++ b/drivers/media/platform/rockchip/isp/csi.h @@ -86,6 +86,8 @@ struct rkisp_csi_device { int frame_cnt_x1; int frame_cnt_x2; int frame_cnt_x3; + u32 err_cnt; + u32 irq_cnt; u32 rd_mode; u8 mipi_di[CSI_PAD_MAX - 1]; u8 filt_state[CSI_F_MAX]; @@ -98,7 +100,7 @@ int rkisp_register_csi_subdev(struct rkisp_device *dev, void rkisp_unregister_csi_subdev(struct rkisp_device *dev); int rkisp_csi_config_patch(struct rkisp_device *dev); -void rkisp_trigger_read_back(struct rkisp_csi_device *csi, u8 dma2frm, u32 mode); +void rkisp_trigger_read_back(struct rkisp_csi_device *csi, u8 dma2frm, u32 mode, bool is_try); int rkisp_csi_trigger_event(struct rkisp_device *dev, u32 cmd, void *arg); void rkisp_csi_sof(struct rkisp_device *dev, u8 id); #endif diff --git a/drivers/media/platform/rockchip/isp/dev.c b/drivers/media/platform/rockchip/isp/dev.c index aeaea25c4f4d..9c2aece55d37 100644 --- a/drivers/media/platform/rockchip/isp/dev.c +++ b/drivers/media/platform/rockchip/isp/dev.c @@ -59,6 +59,10 @@ int rkisp_debug; module_param_named(debug, rkisp_debug, int, 0644); MODULE_PARM_DESC(debug, "Debug level (0-1)"); +bool rkisp_monitor; +module_param_named(monitor, rkisp_monitor, bool, 0644); +MODULE_PARM_DESC(monitor, "rkisp abnormal restart monitor"); + static bool rkisp_clk_dbg; module_param_named(clk_dbg, rkisp_clk_dbg, bool, 0644); MODULE_PARM_DESC(clk_dbg, "rkisp clk set by user"); diff --git a/drivers/media/platform/rockchip/isp/dev.h b/drivers/media/platform/rockchip/isp/dev.h index 755ae7d8c6e4..4cdd98fa5cb5 100644 --- a/drivers/media/platform/rockchip/isp/dev.h +++ b/drivers/media/platform/rockchip/isp/dev.h @@ -88,6 +88,7 @@ enum rkisp_isp_state { ISP_STOP = BIT(8), ISP_START = BIT(9), ISP_ERROR = BIT(10), + ISP_MIPI_ERROR = BIT(11), }; enum rkisp_isp_inp { @@ -202,7 +203,7 @@ struct rkisp_device { int vs_irq; struct gpio_desc *vs_irq_gpio; struct rkisp_hdr hdr; - enum rkisp_isp_state isp_state; + unsigned int isp_state; unsigned int isp_err_cnt; unsigned int isp_isr_cnt; unsigned int isp_inp; diff --git a/drivers/media/platform/rockchip/isp/hw.c b/drivers/media/platform/rockchip/isp/hw.c index 686de2f208e9..9fa5d58d54ca 100644 --- a/drivers/media/platform/rockchip/isp/hw.c +++ b/drivers/media/platform/rockchip/isp/hw.c @@ -520,28 +520,38 @@ static inline bool is_iommu_enable(struct device *dev) return true; } -void rkisp_soft_reset(struct rkisp_hw_dev *dev) +void rkisp_soft_reset(struct rkisp_hw_dev *dev, bool is_secure) { void __iomem *base = dev->base_addr; struct iommu_domain *domain = iommu_get_domain_for_dev(dev->dev); + if (domain) + iommu_detach_device(domain, dev->dev); + + if (is_secure) { + /* if isp working, cru reset isn't secure. + * isp soft reset first to protect isp reset. + */ + writel(0xffff, base + CIF_IRCL); + udelay(10); + } + if (dev->reset) { reset_control_assert(dev->reset); udelay(10); reset_control_deassert(dev->reset); udelay(10); } - /* reset for Dehaze */ - writel(CIF_ISP_CTRL_ISP_MODE_BAYER_ITU601, base + CIF_ISP_CTRL); - writel(0xffff, base + CIF_IRCL); - udelay(10); - if (domain) { -#ifdef CONFIG_IOMMU_API - domain->ops->detach_dev(domain, dev->dev); - domain->ops->attach_dev(domain, dev->dev); -#endif + if (dev->isp_ver == ISP_V20) { + /* reset for Dehaze */ + writel(CIF_ISP_CTRL_ISP_MODE_BAYER_ITU601, base + CIF_ISP_CTRL); + writel(0xffff, base + CIF_IRCL); + udelay(10); } + + if (domain) + iommu_attach_device(domain, dev->dev); } static void isp_config_clk(struct rkisp_hw_dev *dev, int on) @@ -609,7 +619,7 @@ static int enable_sys_clk(struct rkisp_hw_dev *dev) rkisp_set_clk_rate(dev->clks[0], dev->clk_rate_tbl[0].clk_rate * 1000000UL); - rkisp_soft_reset(dev); + rkisp_soft_reset(dev, false); isp_config_clk(dev, true); if (dev->isp_ver == ISP_V12 || dev->isp_ver == ISP_V13) { @@ -679,6 +689,8 @@ static int rkisp_hw_probe(struct platform_device *pdev) goto err; } + rkisp_monitor = device_property_read_bool(dev, "rockchip,restart-monitor-en"); + match_data = match->data; hw_dev->mipi_irq = -1; @@ -795,6 +807,7 @@ static int __maybe_unused rkisp_runtime_resume(struct device *dev) memcpy_fromio(buf, base, RKISP_ISP_SW_REG_SIZE); default_sw_reg_flag(hw_dev->isp[i]); } + hw_dev->monitor.is_en = rkisp_monitor; return 0; } diff --git a/drivers/media/platform/rockchip/isp/hw.h b/drivers/media/platform/rockchip/isp/hw.h index d6cd8867d809..80ed119081db 100644 --- a/drivers/media/platform/rockchip/isp/hw.h +++ b/drivers/media/platform/rockchip/isp/hw.h @@ -7,6 +7,7 @@ #include "bridge.h" #define RKISP_MAX_BUS_CLK 8 +#define RKISP_MAX_RETRY_CNT 5 struct isp_clk_info { u32 clk_rate; @@ -23,6 +24,16 @@ struct isp_match_data { int num_irqs; }; +struct rkisp_monitor { + struct rkisp_hw_dev *dev; + struct work_struct work; + struct completion cmpl; + int (*reset_handle)(struct rkisp_device *dev); + u32 state; + u8 retry; + bool is_en; +}; + struct rkisp_hw_dev { const struct isp_match_data *match_data; struct platform_device *pdev; @@ -55,6 +66,7 @@ struct rkisp_hw_dev { struct list_head rpt_list; struct rkisp_dummy_buffer dummy_buf; const struct vb2_mem_ops *mem_ops; + struct rkisp_monitor monitor; u64 iq_feature; int buf_init_cnt; bool is_feature_on; @@ -69,5 +81,5 @@ struct rkisp_hw_dev { }; int rkisp_register_irq(struct rkisp_hw_dev *dev); -void rkisp_soft_reset(struct rkisp_hw_dev *dev); +void rkisp_soft_reset(struct rkisp_hw_dev *dev, bool is_secure); #endif diff --git a/drivers/media/platform/rockchip/isp/procfs.c b/drivers/media/platform/rockchip/isp/procfs.c index 0200dac60dad..701a4040c819 100644 --- a/drivers/media/platform/rockchip/isp/procfs.c +++ b/drivers/media/platform/rockchip/isp/procfs.c @@ -303,6 +303,10 @@ static int isp_show(struct seq_file *p, void *v) break; } + seq_printf(p, "%-10s %s Cnt:%d\n", + "Monitor", + dev->hw_dev->monitor.is_en ? "ON" : "OFF", + dev->hw_dev->monitor.retry); return 0; } diff --git a/drivers/media/platform/rockchip/isp/rkisp.c b/drivers/media/platform/rockchip/isp/rkisp.c index 3b54b9df20d7..2ab7a0f3b029 100644 --- a/drivers/media/platform/rockchip/isp/rkisp.c +++ b/drivers/media/platform/rockchip/isp/rkisp.c @@ -81,6 +81,12 @@ * +---------------------------------------------------------+ */ +struct backup_reg { + const u32 base; + const u32 shd; + u32 val; +}; + static inline struct rkisp_device *sd_to_isp_dev(struct v4l2_subdev *sd) { return container_of(sd->v4l2_dev, struct rkisp_device, v4l2_dev); @@ -459,6 +465,12 @@ void rkisp_check_idle(struct rkisp_device *dev, u32 irq) v4l2_dbg(3, rkisp_debug, &dev->v4l2_dev, "%s irq:0x%x ends:0x%x mask:0x%x\n", __func__, irq, dev->irq_ends, dev->irq_ends_mask); + if (dev->irq_ends == dev->irq_ends_mask && dev->hw_dev->monitor.is_en) { + dev->hw_dev->monitor.retry = 0; + dev->hw_dev->monitor.state |= ISP_FRAME_END; + if (!completion_done(&dev->hw_dev->monitor.cmpl)) + complete(&dev->hw_dev->monitor.cmpl); + } if (dev->irq_ends != dev->irq_ends_mask || !IS_HDR_RDBK(dev->csi_dev.rd_mode)) return; @@ -486,17 +498,16 @@ void rkisp_check_idle(struct rkisp_device *dev, u32 irq) wake_up(&dev->sync_onoff); } -static void rkisp_set_state(struct rkisp_device *dev, u32 state) +static void rkisp_set_state(u32 *state, u32 val) { u32 mask = 0xff; - if (state < ISP_STOP) + if (val < ISP_STOP) mask = 0xff00; - dev->isp_state &= mask; - dev->isp_state |= state; + *state &= mask; + *state |= val; } - /* * Image Stabilization. * This should only be called when configuring CIF @@ -531,6 +542,243 @@ static void rkisp_config_ism(struct rkisp_device *dev) writel(val, base + CIF_ISP_CTRL); } +static int rkisp_reset_handle_v2x(struct rkisp_device *dev) +{ + void __iomem *base = dev->base_addr; + void *reg_buf = NULL; + u32 *reg, *reg1, i; + struct backup_reg backup[] = { + { + .base = MI_MP_WR_Y_BASE, + .shd = MI_MP_WR_Y_BASE_SHD, + }, { + .base = MI_MP_WR_CB_BASE, + .shd = MI_MP_WR_CB_BASE_SHD, + }, { + .base = MI_MP_WR_CR_BASE, + .shd = MI_MP_WR_CR_BASE_SHD, + }, { + .base = MI_SP_WR_Y_BASE, + .shd = MI_SP_WR_Y_BASE_SHD, + }, { + .base = MI_SP_WR_CB_BASE, + .shd = MI_SP_WR_CB_BASE_AD_SHD, + }, { + .base = MI_SP_WR_CR_BASE, + .shd = MI_SP_WR_CR_BASE_AD_SHD, + }, { + .base = MI_RAW0_WR_BASE, + .shd = MI_RAW0_WR_BASE_SHD, + }, { + .base = MI_RAW1_WR_BASE, + .shd = MI_RAW1_WR_BASE_SHD, + }, { + .base = MI_RAW2_WR_BASE, + .shd = MI_RAW2_WR_BASE_SHD, + }, { + .base = MI_RAW3_WR_BASE, + .shd = MI_RAW3_WR_BASE_SHD, + }, { + .base = MI_RAW0_RD_BASE, + .shd = MI_RAW0_RD_BASE_SHD, + }, { + .base = MI_RAW1_RD_BASE, + .shd = MI_RAW1_RD_BASE_SHD, + }, { + .base = MI_RAW2_RD_BASE, + .shd = MI_RAW2_RD_BASE_SHD, + }, { + .base = MI_GAIN_WR_BASE, + .shd = MI_GAIN_WR_BASE_SHD, + } + }; + + reg_buf = kzalloc(RKISP_ISP_SW_REG_SIZE, GFP_KERNEL); + if (!reg_buf) + return -ENOMEM; + + dev_info(dev->dev, "%s enter\n", __func__); + + memcpy_fromio(reg_buf, base, RKISP_ISP_SW_REG_SIZE); + rkisp_soft_reset(dev->hw_dev, true); + + /* process special reg */ + reg = reg_buf + ISP_CTRL; + *reg &= ~(CIF_ISP_CTRL_ISP_ENABLE | + CIF_ISP_CTRL_ISP_INFORM_ENABLE | + CIF_ISP_CTRL_ISP_CFG_UPD); + reg = reg_buf + MI_WR_INIT; + *reg = 0; + reg = reg_buf + CSI2RX_CTRL0; + *reg &= ~SW_CSI2RX_EN; + /* skip mmu range */ + memcpy_toio(base, reg_buf, ISP21_MI_BAY3D_RD_BASE_SHD); + memcpy_toio(base + CSI2RX_CTRL0, reg_buf + CSI2RX_CTRL0, + RKISP_ISP_SW_REG_SIZE - CSI2RX_CTRL0); + /* config shd_reg to base_reg */ + for (i = 0; i < ARRAY_SIZE(backup); i++) { + reg = reg_buf + backup[i].base; + reg1 = reg_buf + backup[i].shd; + backup[i].val = *reg; + writel(*reg1, base + backup[i].base); + } + + /* clear state */ + dev->isp_err_cnt = 0; + dev->isp_state &= ~ISP_ERROR; + rkisp_set_state(&dev->isp_state, ISP_FRAME_END); + dev->hw_dev->monitor.state = ISP_FRAME_END; + + /* update module */ + reg = reg_buf + DUAL_CROP_CTRL; + if (*reg & 0xf) + writel(*reg | CIF_DUAL_CROP_CFG_UPD, base + DUAL_CROP_CTRL); + reg = reg_buf + SELF_RESIZE_CTRL; + if (*reg & 0xf) + writel(*reg | CIF_RSZ_CTRL_CFG_UPD, base + SELF_RESIZE_CTRL); + reg = reg_buf + MAIN_RESIZE_CTRL; + if (*reg & 0xf) + writel(*reg | CIF_RSZ_CTRL_CFG_UPD, base + MAIN_RESIZE_CTRL); + + /* update mi and isp, base_reg will update to shd_reg */ + force_cfg_update(dev); + reg = reg_buf + ISP_CTRL; + *reg |= CIF_ISP_CTRL_ISP_ENABLE | + CIF_ISP_CTRL_ISP_INFORM_ENABLE | + CIF_ISP_CTRL_ISP_CFG_UPD; + writel(*reg, base + ISP_CTRL); + udelay(50); + /* config base_reg */ + for (i = 0; i < ARRAY_SIZE(backup); i++) + writel(backup[i].val, base + backup[i].base); + /* mpfbc base_reg = shd_reg, write is base but read is shd */ + if (dev->isp_ver == ISP_V20) + writel(rkisp_read_reg_cache(dev, ISP_MPFBC_HEAD_PTR), + base + ISP_MPFBC_HEAD_PTR); + rkisp_set_bits(dev, CIF_ISP_IMSC, 0, CIF_ISP_DATA_LOSS | CIF_ISP_PIC_SIZE_ERROR, true); + if (IS_HDR_RDBK(dev->hdr.op_mode)) { + if (!dev->hw_dev->is_idle) + rkisp_trigger_read_back(&dev->csi_dev, 1, 0, true); + else + rkisp_csi_trigger_event(dev, T_CMD_QUEUE, NULL); + } + kfree(reg_buf); + dev_info(dev->dev, "%s exit\n", __func__); + return 0; +} + +static void rkisp_restart_monitor(struct work_struct *work) +{ + struct rkisp_monitor *monitor = + container_of(work, struct rkisp_monitor, work); + struct rkisp_hw_dev *hw = monitor->dev; + struct rkisp_device *isp; + struct rkisp_pipeline *p; + int ret, i, j, timeout = 5, mipi_irq_cnt = 0; + + if (!monitor->reset_handle) { + monitor->is_en = false; + return; + } + + dev_info(hw->dev, "%s enter\n", __func__); + while (!(monitor->state & ISP_STOP) && monitor->is_en) { + ret = wait_for_completion_timeout(&monitor->cmpl, + msecs_to_jiffies(100)); + /* isp stop to exit + * isp err to reset + * mipi err wait isp idle, then reset + */ + if (monitor->state & ISP_STOP || + (ret && !(monitor->state & ISP_ERROR)) || + (!ret && + monitor->state & ISP_FRAME_END && + !(monitor->state & ISP_MIPI_ERROR))) { + for (i = 0; i < hw->dev_num; i++) { + isp = hw->isp[i]; + if (!(isp->isp_inp & INP_CSI)) + continue; + if (!(isp->isp_state & ISP_START)) + break; + if (isp->csi_dev.irq_cnt != mipi_irq_cnt) { + mipi_irq_cnt = isp->csi_dev.irq_cnt; + timeout = 5; + } else if (mipi_irq_cnt && timeout-- == 0) { + /* mipi no input */ + monitor->state |= ISP_MIPI_ERROR; + } + } + continue; + } + dev_info(hw->dev, "isp%d to restart state:0x%x try:%d mipi_irq_cnt:%d\n", + hw->cur_dev_id, monitor->state, monitor->retry, mipi_irq_cnt); + if (monitor->retry++ > RKISP_MAX_RETRY_CNT || hw->is_shutdown) { + monitor->is_en = false; + break; + } + for (i = 0; i < hw->dev_num; i++) { + isp = hw->isp[i]; + if (isp->isp_inp & INP_CSI || + isp->isp_inp & INP_DVP || + isp->isp_inp & INP_LVDS) { + if (!(isp->isp_state & ISP_START)) + break; + /* subdev stream off */ + p = &isp->pipe; + for (j = p->num_subdevs - 1; j >= 0; j--) + v4l2_subdev_call(p->subdevs[j], video, s_stream, 0); + for (i = 0; i < ISP2X_MIPI_RAW_MAX; i++) { + isp->luma_vdev.ystat_isrcnt[i] = 0; + isp->luma_vdev.ystat_rdflg[i] = 0; + } + } + } + + /* restart isp */ + isp = hw->isp[hw->cur_dev_id]; + ret = monitor->reset_handle(isp); + if (ret) { + monitor->is_en = false; + break; + } + + for (i = 0; i < hw->dev_num; i++) { + isp = hw->isp[i]; + if (isp->isp_inp & INP_CSI || + isp->isp_inp & INP_DVP || + isp->isp_inp & INP_LVDS) { + if (!(isp->isp_state & ISP_START)) + break; + if (isp->isp_inp & INP_CSI) { + rkisp_write(isp, CSI2RX_MASK_PHY, 0xF0FFFF, true); + rkisp_write(isp, CSI2RX_MASK_PACKET, 0xF1FFFFF, true); + rkisp_write(isp, CSI2RX_MASK_OVERFLOW, 0x7F7FF1, true); + } + /* subdev stream on */ + isp->csi_dev.err_cnt = 0; + isp->isp_state &= ~ISP_MIPI_ERROR; + p = &isp->pipe; + for (j = 0; j < p->num_subdevs; j++) + v4l2_subdev_call(p->subdevs[j], video, s_stream, 1); + } + } + } + dev_dbg(hw->dev, "%s exit\n", __func__); +} + +static void rkisp_monitor_init(struct rkisp_device *dev) +{ + struct rkisp_monitor *monitor = &dev->hw_dev->monitor; + + monitor->dev = dev->hw_dev; + monitor->reset_handle = NULL; + if (dev->isp_ver == ISP_V20 || dev->isp_ver == ISP_V21) + monitor->reset_handle = rkisp_reset_handle_v2x; + + init_completion(&monitor->cmpl); + INIT_WORK(&monitor->work, rkisp_restart_monitor); +} + /* * configure isp blocks with input format, size...... */ @@ -911,6 +1159,13 @@ static int rkisp_isp_stop(struct rkisp_device *dev) if (atomic_read(&dev->hw_dev->refcnt) > 1) goto end; + + if (dev->hw_dev->monitor.is_en) { + dev->hw_dev->monitor.is_en = 0; + dev->hw_dev->monitor.state = ISP_STOP; + if (!completion_done(&dev->hw_dev->monitor.cmpl)) + complete(&dev->hw_dev->monitor.cmpl); + } /* * ISP(mi) stop in mi frame end -> Stop ISP(mipi) -> * Stop ISP(isp) ->wait for ISP isp off @@ -982,7 +1237,7 @@ static int rkisp_isp_stop(struct rkisp_device *dev) clk_set_rate(dev->hw_dev->clks[0], safe_rate); udelay(100); } - rkisp_soft_reset(dev->hw_dev); + rkisp_soft_reset(dev->hw_dev, false); } rkisp_write(dev, CTRL_VI_ISP_CLK_CTRL, val, true); @@ -1001,7 +1256,7 @@ static int rkisp_isp_stop(struct rkisp_device *dev) end: dev->irq_ends_mask = 0; dev->hdr.op_mode = 0; - rkisp_set_state(dev, ISP_STOP); + rkisp_set_state(&dev->isp_state, ISP_STOP); if (dev->isp_ver == ISP_V20 || dev->isp_ver == ISP_V21) kfifo_reset(&dev->csi_dev.rdbk_kfifo); @@ -1073,6 +1328,11 @@ static int rkisp_isp_start(struct rkisp_device *dev) "%s MI_CTRL 0x%08x ISP_CTRL 0x%08x\n", __func__, readl(base + CIF_MI_CTRL), readl(base + CIF_ISP_CTRL)); + if (dev->hw_dev->monitor.is_en && atomic_read(&dev->hw_dev->refcnt) < 2) { + dev->hw_dev->monitor.retry = 0; + dev->hw_dev->monitor.state = ISP_FRAME_END; + schedule_work(&dev->hw_dev->monitor.work); + } rkisp_csi_trigger_event(dev, T_CMD_QUEUE, NULL); return 0; } @@ -2122,6 +2382,7 @@ int rkisp_register_isp_subdev(struct rkisp_device *isp_dev, isp_dev->hdr.sensor = NULL; isp_dev->isp_state = ISP_STOP; + rkisp_monitor_init(isp_dev); return 0; err_cleanup_media_entity: media_entity_cleanup(&sd->entity); @@ -2334,7 +2595,12 @@ void rkisp_isp_isr(unsigned int isp_mis, if (isp_mis & CIF_ISP_V_START) { if (!(dev->isp_state & ISP_FRAME_VS)) dev->isp_sdev.dbg.timestamp = ktime_get_ns(); - rkisp_set_state(dev, ISP_FRAME_VS); + rkisp_set_state(&dev->isp_state, ISP_FRAME_VS); + if (dev->hw_dev->monitor.is_en) { + rkisp_set_state(&dev->hw_dev->monitor.state, ISP_FRAME_VS); + if (!completion_done(&dev->hw_dev->monitor.cmpl)) + complete(&dev->hw_dev->monitor.cmpl); + } /* last vsync to config next buf */ if (!dev->csi_dev.filt_state[CSI_F_VS]) rkisp_bridge_update_mi(dev); @@ -2398,10 +2664,19 @@ vs_skip: } if (dev->isp_err_cnt++ > RKISP_CONTI_ERR_MAX) { - rkisp_isp_stop(dev); - rkisp_set_state(dev, ISP_ERROR); - v4l2_err(&dev->v4l2_dev, - "Too many isp error, stop isp!\n"); + if (!(dev->isp_state & ISP_ERROR)) { + rkisp_set_state(&dev->isp_state, ISP_ERROR); + rkisp_clear_bits(dev, CIF_ISP_IMSC, + CIF_ISP_DATA_LOSS | + CIF_ISP_PIC_SIZE_ERROR, true); + writel(CIF_ISP_PIC_SIZE_ERROR, base + CIF_ISP_ICR); + writel(CIF_ISP_DATA_LOSS, base + CIF_ISP_ICR); + if (dev->hw_dev->monitor.is_en) { + rkisp_set_state(&dev->hw_dev->monitor.state, ISP_ERROR); + if (!completion_done(&dev->hw_dev->monitor.cmpl)) + complete(&dev->hw_dev->monitor.cmpl); + } + } } } @@ -2416,7 +2691,7 @@ vs_skip: /* sampled input frame is complete */ if (isp_mis & CIF_ISP_FRAME_IN) { - rkisp_set_state(dev, ISP_FRAME_IN); + rkisp_set_state(&dev->isp_state, ISP_FRAME_IN); writel(CIF_ISP_FRAME_IN, base + CIF_ISP_ICR); isp_mis_tmp = readl(base + CIF_ISP_MIS); if (isp_mis_tmp & CIF_ISP_FRAME_IN) @@ -2424,6 +2699,7 @@ vs_skip: isp_mis_tmp); dev->isp_err_cnt = 0; + dev->isp_state &= ~ISP_ERROR; } /* frame was completely put out */ @@ -2431,7 +2707,7 @@ vs_skip: dev->isp_sdev.dbg.interval = ktime_get_ns() - dev->isp_sdev.dbg.timestamp; /* Clear Frame In (ISP) */ - rkisp_set_state(dev, ISP_FRAME_END); + rkisp_set_state(&dev->isp_state, ISP_FRAME_END); writel(CIF_ISP_FRAME, base + CIF_ISP_ICR); isp_mis_tmp = readl(base + CIF_ISP_MIS); if (isp_mis_tmp & CIF_ISP_FRAME)