drm/msm/dsi-staging: add error handling support to DSI driver

Add functions to handle and recover from various DSI errors
in DRM DSI driver. The errors that are currently handled are
DSI FIFO OVERFLOW, FIFO UNDERFLOW, DSI BTA TIMEOUT and
DSI LP/RX TIMEOUT.

Change-Id: Ifc4eeaf01b3b5204b26745b6cc2dcffb71cb3f57
Signed-off-by: Sandeep Panda <spanda@codeaurora.org>
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c
index 46e13c2..b610270 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c
@@ -62,6 +62,11 @@
 	ctrl->ops.debug_bus = dsi_ctrl_hw_cmn_debug_bus;
 	ctrl->ops.get_cmd_read_data = dsi_ctrl_hw_cmn_get_cmd_read_data;
 	ctrl->ops.clear_rdbk_register = dsi_ctrl_hw_cmn_clear_rdbk_reg;
+	ctrl->ops.ctrl_reset = dsi_ctrl_hw_cmn_ctrl_reset;
+	ctrl->ops.mask_error_intr = dsi_ctrl_hw_cmn_mask_error_intr;
+	ctrl->ops.error_intr_ctrl = dsi_ctrl_hw_cmn_error_intr_ctrl;
+	ctrl->ops.get_error_mask = dsi_ctrl_hw_cmn_get_error_mask;
+	ctrl->ops.get_hw_version = dsi_ctrl_hw_cmn_get_hw_version;
 
 	switch (version) {
 	case DSI_CTRL_VERSION_1_4:
@@ -201,6 +206,7 @@
 	phy->ops.ulps_ops.is_lanes_in_ulps =
 		dsi_phy_hw_v3_0_is_lanes_in_ulps;
 	phy->ops.phy_timing_val = dsi_phy_hw_timing_val_v3_0;
+	phy->ops.phy_lane_reset = dsi_phy_hw_v3_0_lane_reset;
 }
 
 /**
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h
index eda192d..2b409c2 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h
@@ -101,6 +101,7 @@
 bool dsi_phy_hw_v3_0_is_lanes_in_ulps(u32 lanes, u32 ulps_lanes);
 int dsi_phy_hw_timing_val_v3_0(struct dsi_phy_per_lane_cfgs *timing_cfg,
 		u32 *timing_val, u32 size);
+int dsi_phy_hw_v3_0_lane_reset(struct dsi_phy_hw *phy);
 
 /* DSI controller common ops */
 u32 dsi_ctrl_hw_cmn_get_interrupt_status(struct dsi_ctrl_hw *ctrl);
@@ -177,6 +178,13 @@
 				     u32 pkt_size, u32 *hw_read_cnt);
 void dsi_ctrl_hw_cmn_clear_rdbk_reg(struct dsi_ctrl_hw *ctrl);
 void dsi_ctrl_hw_22_schedule_dma_cmd(struct dsi_ctrl_hw *ctrl, int line_on);
+int dsi_ctrl_hw_cmn_ctrl_reset(struct dsi_ctrl_hw *ctrl,
+			int mask);
+void dsi_ctrl_hw_cmn_mask_error_intr(struct dsi_ctrl_hw *ctrl, u32 idx,
+			bool en);
+void dsi_ctrl_hw_cmn_error_intr_ctrl(struct dsi_ctrl_hw *ctrl, bool en);
+u32 dsi_ctrl_hw_cmn_get_error_mask(struct dsi_ctrl_hw *ctrl);
+u32 dsi_ctrl_hw_cmn_get_hw_version(struct dsi_ctrl_hw *ctrl);
 
 /* Definitions specific to 1.4 DSI controller hardware */
 int dsi_ctrl_hw_14_wait_for_lane_idle(struct dsi_ctrl_hw *ctrl, u32 lanes);
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
index a18efe0..a04d212 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
@@ -1041,6 +1041,9 @@
 		dsi_ctrl_wait_for_video_done(dsi_ctrl);
 		dsi_ctrl_enable_status_interrupt(dsi_ctrl,
 					DSI_SINT_CMD_MODE_DMA_DONE, NULL);
+		if (dsi_ctrl->hw.ops.mask_error_intr)
+			dsi_ctrl->hw.ops.mask_error_intr(&dsi_ctrl->hw,
+					BIT(DSI_FIFO_OVERFLOW), true);
 		reinit_completion(&dsi_ctrl->irq_info.cmd_dma_done);
 
 		if (flags & DSI_CTRL_CMD_FETCH_MEMORY) {
@@ -1081,6 +1084,9 @@
 			}
 		}
 
+		if (dsi_ctrl->hw.ops.mask_error_intr)
+			dsi_ctrl->hw.ops.mask_error_intr(&dsi_ctrl->hw,
+					BIT(DSI_FIFO_OVERFLOW), false);
 		dsi_ctrl->hw.ops.reset_cmd_fifo(&dsi_ctrl->hw);
 	}
 error:
@@ -1941,7 +1947,7 @@
 	}
 
 	dsi_ctrl->hw.ops.enable_status_interrupts(&dsi_ctrl->hw, 0x0);
-	dsi_ctrl->hw.ops.enable_error_interrupts(&dsi_ctrl->hw, 0x0);
+	dsi_ctrl->hw.ops.enable_error_interrupts(&dsi_ctrl->hw, 0xFF00E0);
 	dsi_ctrl->hw.ops.ctrl_en(&dsi_ctrl->hw, true);
 
 	mutex_unlock(&dsi_ctrl->ctrl_lock);
@@ -1992,33 +1998,77 @@
 static void dsi_ctrl_handle_error_status(struct dsi_ctrl *dsi_ctrl,
 				unsigned long int error)
 {
-	pr_err("%s: %lu\n", __func__, error);
+	struct dsi_event_cb_info cb_info;
+
+	cb_info = dsi_ctrl->irq_info.irq_err_cb;
+
+	/* disable error interrupts */
+	if (dsi_ctrl->hw.ops.error_intr_ctrl)
+		dsi_ctrl->hw.ops.error_intr_ctrl(&dsi_ctrl->hw, false);
+
+	/* clear error interrupts first */
+	if (dsi_ctrl->hw.ops.clear_error_status)
+		dsi_ctrl->hw.ops.clear_error_status(&dsi_ctrl->hw,
+					error);
 
 	/* DTLN PHY error */
-	if (error & 0x3000e00)
-		if (dsi_ctrl->hw.ops.clear_error_status)
-			dsi_ctrl->hw.ops.clear_error_status(&dsi_ctrl->hw,
-					0x3000e00);
+	if (error & 0x3000E00)
+		pr_err("dsi PHY contention error: 0x%lx\n", error);
+
+	/* TX timeout error */
+	if (error & 0xE0) {
+		if (error & 0xA0) {
+			if (cb_info.event_cb) {
+				cb_info.event_idx = DSI_LP_Rx_TIMEOUT;
+				(void)cb_info.event_cb(cb_info.event_usr_ptr,
+							cb_info.event_idx,
+							dsi_ctrl->cell_index,
+							0, 0, 0, 0);
+			}
+		}
+		pr_err("tx timeout error: 0x%lx\n", error);
+	}
 
 	/* DSI FIFO OVERFLOW error */
-	if (error & 0xf0000) {
-		if (dsi_ctrl->hw.ops.clear_error_status)
-			dsi_ctrl->hw.ops.clear_error_status(&dsi_ctrl->hw,
-					0xf0000);
+	if (error & 0xF0000) {
+		u32 mask = 0;
+
+		if (dsi_ctrl->hw.ops.get_error_mask)
+			mask = dsi_ctrl->hw.ops.get_error_mask(&dsi_ctrl->hw);
+		/* no need to report FIFO overflow if already masked */
+		if (cb_info.event_cb && !(mask & 0xf0000)) {
+			cb_info.event_idx = DSI_FIFO_OVERFLOW;
+			(void)cb_info.event_cb(cb_info.event_usr_ptr,
+						cb_info.event_idx,
+						dsi_ctrl->cell_index,
+						0, 0, 0, 0);
+			pr_err("dsi FIFO OVERFLOW error: 0x%lx\n", error);
+		}
 	}
 
 	/* DSI FIFO UNDERFLOW error */
-	if (error & 0xf00000) {
-		if (dsi_ctrl->hw.ops.clear_error_status)
-			dsi_ctrl->hw.ops.clear_error_status(&dsi_ctrl->hw,
-					0xf00000);
+	if (error & 0xF00000) {
+		if (cb_info.event_cb) {
+			cb_info.event_idx = DSI_FIFO_UNDERFLOW;
+			(void)cb_info.event_cb(cb_info.event_usr_ptr,
+						cb_info.event_idx,
+						dsi_ctrl->cell_index,
+						0, 0, 0, 0);
+		}
+		pr_err("dsi FIFO UNDERFLOW error: 0x%lx\n", error);
 	}
 
 	/* DSI PLL UNLOCK error */
 	if (error & BIT(8))
-		if (dsi_ctrl->hw.ops.clear_error_status)
-			dsi_ctrl->hw.ops.clear_error_status(&dsi_ctrl->hw,
-					BIT(8));
+		pr_err("dsi PLL unlock error: 0x%lx\n", error);
+
+	/* ACK error */
+	if (error & 0xF)
+		pr_err("ack error: 0x%lx\n", error);
+
+	/* enable back DSI interrupts */
+	if (dsi_ctrl->hw.ops.error_intr_ctrl)
+		dsi_ctrl->hw.ops.error_intr_ctrl(&dsi_ctrl->hw, true);
 }
 
 /**
@@ -2032,39 +2082,28 @@
 	struct dsi_ctrl *dsi_ctrl;
 	struct dsi_event_cb_info cb_info;
 	unsigned long flags;
-	uint32_t cell_index, status, i;
-	uint64_t errors;
+	uint32_t status = 0x0, i;
+	uint64_t errors = 0x0;
 
 	if (!ptr)
 		return IRQ_NONE;
 	dsi_ctrl = ptr;
 
-	/* clear status interrupts */
+	/* check status interrupts */
 	if (dsi_ctrl->hw.ops.get_interrupt_status)
 		status = dsi_ctrl->hw.ops.get_interrupt_status(&dsi_ctrl->hw);
-	else
-		status = 0x0;
 
-	if (dsi_ctrl->hw.ops.clear_interrupt_status)
-		dsi_ctrl->hw.ops.clear_interrupt_status(&dsi_ctrl->hw, status);
-
-	spin_lock_irqsave(&dsi_ctrl->irq_info.irq_lock, flags);
-	cell_index = dsi_ctrl->cell_index;
-	spin_unlock_irqrestore(&dsi_ctrl->irq_info.irq_lock, flags);
-
-	/* clear error interrupts */
+	/* check error interrupts */
 	if (dsi_ctrl->hw.ops.get_error_status)
 		errors = dsi_ctrl->hw.ops.get_error_status(&dsi_ctrl->hw);
-	else
-		errors = 0x0;
 
-	if (errors) {
-		/* handle DSI error recovery */
+	/* clear interrupts */
+	if (dsi_ctrl->hw.ops.clear_interrupt_status)
+		dsi_ctrl->hw.ops.clear_interrupt_status(&dsi_ctrl->hw, 0x0);
+
+	/* handle DSI error recovery */
+	if (status & DSI_ERROR)
 		dsi_ctrl_handle_error_status(dsi_ctrl, errors);
-		if (dsi_ctrl->hw.ops.clear_error_status)
-			dsi_ctrl->hw.ops.clear_error_status(&dsi_ctrl->hw,
-							errors);
-	}
 
 	if (status & DSI_CMD_MODE_DMA_DONE) {
 		dsi_ctrl_disable_status_interrupt(dsi_ctrl,
@@ -2085,9 +2124,16 @@
 	}
 
 	if (status & DSI_BTA_DONE) {
+		u32 fifo_overflow_mask = (DSI_DLN0_HS_FIFO_OVERFLOW |
+					DSI_DLN1_HS_FIFO_OVERFLOW |
+					DSI_DLN2_HS_FIFO_OVERFLOW |
+					DSI_DLN3_HS_FIFO_OVERFLOW);
 		dsi_ctrl_disable_status_interrupt(dsi_ctrl,
 					DSI_SINT_BTA_DONE);
 		complete_all(&dsi_ctrl->irq_info.bta_done);
+		if (dsi_ctrl->hw.ops.clear_error_status)
+			dsi_ctrl->hw.ops.clear_error_status(&dsi_ctrl->hw,
+					fifo_overflow_mask);
 	}
 
 	for (i = 0; status && i < DSI_STATUS_INTERRUPT_COUNT; ++i) {
@@ -2100,7 +2146,8 @@
 			if (cb_info.event_cb)
 				(void)cb_info.event_cb(cb_info.event_usr_ptr,
 						cb_info.event_idx,
-						cell_index, irq, 0, 0, 0);
+						dsi_ctrl->cell_index,
+						irq, 0, 0, 0);
 		}
 		status >>= 1;
 	}
@@ -2310,7 +2357,7 @@
 	dsi_ctrl_setup_isr(dsi_ctrl);
 
 	dsi_ctrl->hw.ops.enable_status_interrupts(&dsi_ctrl->hw, 0x0);
-	dsi_ctrl->hw.ops.enable_error_interrupts(&dsi_ctrl->hw, 0x0);
+	dsi_ctrl->hw.ops.enable_error_interrupts(&dsi_ctrl->hw, 0xFF00E0);
 
 	pr_debug("[DSI_%d]Host initialization complete, continuous splash status:%d\n",
 		dsi_ctrl->cell_index, is_splash_enabled);
@@ -2333,6 +2380,48 @@
 	return 0;
 }
 
+int dsi_ctrl_reset(struct dsi_ctrl *dsi_ctrl, int mask)
+{
+	int rc = 0;
+
+	if (!dsi_ctrl)
+		return -EINVAL;
+
+	mutex_lock(&dsi_ctrl->ctrl_lock);
+	rc = dsi_ctrl->hw.ops.ctrl_reset(&dsi_ctrl->hw, mask);
+	mutex_unlock(&dsi_ctrl->ctrl_lock);
+
+	return rc;
+}
+
+int dsi_ctrl_get_hw_version(struct dsi_ctrl *dsi_ctrl)
+{
+	int rc = 0;
+
+	if (!dsi_ctrl)
+		return -EINVAL;
+
+	mutex_lock(&dsi_ctrl->ctrl_lock);
+	rc = dsi_ctrl->hw.ops.get_hw_version(&dsi_ctrl->hw);
+	mutex_unlock(&dsi_ctrl->ctrl_lock);
+
+	return rc;
+}
+
+int dsi_ctrl_vid_engine_en(struct dsi_ctrl *dsi_ctrl, bool on)
+{
+	int rc = 0;
+
+	if (!dsi_ctrl)
+		return -EINVAL;
+
+	mutex_lock(&dsi_ctrl->ctrl_lock);
+	dsi_ctrl->hw.ops.video_engine_en(&dsi_ctrl->hw, on);
+	mutex_unlock(&dsi_ctrl->ctrl_lock);
+
+	return rc;
+}
+
 /**
  * dsi_ctrl_host_deinit() - De-Initialize DSI host hardware.
  * @dsi_ctrl:        DSI controller handle.
@@ -2534,6 +2623,9 @@
 		dsi_ctrl_wait_for_video_done(dsi_ctrl);
 		dsi_ctrl_enable_status_interrupt(dsi_ctrl,
 					DSI_SINT_CMD_MODE_DMA_DONE, NULL);
+		if (dsi_ctrl->hw.ops.mask_error_intr)
+			dsi_ctrl->hw.ops.mask_error_intr(&dsi_ctrl->hw,
+					BIT(DSI_FIFO_OVERFLOW), true);
 		reinit_completion(&dsi_ctrl->irq_info.cmd_dma_done);
 
 		/* trigger command */
@@ -2564,6 +2656,9 @@
 						dsi_ctrl->cell_index);
 			}
 		}
+		if (dsi_ctrl->hw.ops.mask_error_intr)
+			dsi_ctrl->hw.ops.mask_error_intr(&dsi_ctrl->hw,
+					BIT(DSI_FIFO_OVERFLOW), false);
 	}
 
 	mutex_unlock(&dsi_ctrl->ctrl_lock);
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
index 62291dc..e7b3707 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
@@ -146,6 +146,7 @@
  * @irq_stat_mask:       Hardware mask of currently enabled interrupts.
  * @irq_stat_refcount:   Number of times each interrupt has been requested.
  * @irq_stat_cb:         Status IRQ callback definitions.
+ * @irq_err_cb:          IRQ callback definition to handle DSI ERRORs.
  * @cmd_dma_done:          Completion signal for DSI_CMD_MODE_DMA_DONE interrupt
  * @vid_frame_done:        Completion signal for DSI_VIDEO_MODE_FRAME_DONE int.
  * @cmd_frame_done:        Completion signal for DSI_CMD_FRAME_DONE interrupt.
@@ -156,6 +157,7 @@
 	uint32_t irq_stat_mask;
 	int irq_stat_refcount[DSI_STATUS_INTERRUPT_COUNT];
 	struct dsi_event_cb_info irq_stat_cb[DSI_STATUS_INTERRUPT_COUNT];
+	struct dsi_event_cb_info irq_err_cb;
 
 	struct completion cmd_dma_done;
 	struct completion vid_frame_done;
@@ -177,6 +179,7 @@
  * @current_state:       Current driver and hardware state.
  * @clk_cb:		 Callback for DSI clock control.
  * @irq_info:            Interrupt information.
+ * @recovery_cb:         Recovery call back to SDE.
  * @clk_info:            Clock information.
  * @clk_freq:            DSi Link clock frequency information.
  * @pwr_info:            Power information.
@@ -213,6 +216,7 @@
 	struct clk_ctrl_cb clk_cb;
 
 	struct dsi_ctrl_interrupts irq_info;
+	struct dsi_event_cb_info recovery_cb;
 
 	/* Clock and power states */
 	struct dsi_ctrl_clk_info clk_info;
@@ -648,4 +652,24 @@
  */
 void dsi_ctrl_drv_unregister(void);
 
+/**
+ * dsi_ctrl_reset() - Reset DSI PHY CLK/DATA lane
+ * @dsi_ctrl:        DSI controller handle.
+ * @mask:	     Mask to indicate if CLK and/or DATA lane needs reset.
+ */
+int dsi_ctrl_reset(struct dsi_ctrl *dsi_ctrl, int mask);
+
+/**
+ * dsi_ctrl_get_hw_version() - read dsi controller hw revision
+ * @dsi_ctrl:        DSI controller handle.
+ */
+int dsi_ctrl_get_hw_version(struct dsi_ctrl *dsi_ctrl);
+
+/**
+ * dsi_ctrl_vid_engine_en() - Control DSI video engine HW state
+ * @dsi_ctrl:        DSI controller handle.
+ * @on:		variable to control video engine ON/OFF.
+ */
+int dsi_ctrl_vid_engine_en(struct dsi_ctrl *dsi_ctrl, bool on);
+
 #endif /* _DSI_CTRL_H_ */
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h
index c34fe44..43b766a 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h
@@ -96,6 +96,7 @@
  * @DSI_SINT_DESKEW_DONE:              The deskew calibration operation done.
  * @DSI_SINT_DYN_BLANK_DMA_DONE:       The dynamic blankin DMA operation has
  *                                     completed.
+ * @DSI_SINT_ERROR:                    DSI error has happened.
  */
 enum dsi_status_int_index {
 	DSI_SINT_CMD_MODE_DMA_DONE = 0,
@@ -108,6 +109,7 @@
 	DSI_SINT_DYN_REFRESH_DONE = 7,
 	DSI_SINT_DESKEW_DONE = 8,
 	DSI_SINT_DYN_BLANK_DMA_DONE = 9,
+	DSI_SINT_ERROR = 10,
 
 	DSI_STATUS_INTERRUPT_COUNT
 };
@@ -126,6 +128,7 @@
  * @DSI_DESKEW_DONE:              The deskew calibration operation has completed
  * @DSI_DYN_BLANK_DMA_DONE:       The dynamic blankin DMA operation has
  *                                completed.
+ * @DSI_ERROR:                    DSI error has happened.
  */
 enum dsi_status_int_type {
 	DSI_CMD_MODE_DMA_DONE = BIT(DSI_SINT_CMD_MODE_DMA_DONE),
@@ -137,7 +140,8 @@
 	DSI_CMD_FRAME_DONE = BIT(DSI_SINT_CMD_FRAME_DONE),
 	DSI_DYN_REFRESH_DONE = BIT(DSI_SINT_DYN_REFRESH_DONE),
 	DSI_DESKEW_DONE = BIT(DSI_SINT_DESKEW_DONE),
-	DSI_DYN_BLANK_DMA_DONE = BIT(DSI_SINT_DYN_BLANK_DMA_DONE)
+	DSI_DYN_BLANK_DMA_DONE = BIT(DSI_SINT_DYN_BLANK_DMA_DONE),
+	DSI_ERROR = BIT(DSI_SINT_ERROR)
 };
 
 /**
@@ -175,6 +179,7 @@
  * @DSI_EINT_DLN1_LP1_CONTENTION:        PHY level contention while lane 1 high.
  * @DSI_EINT_DLN2_LP1_CONTENTION:        PHY level contention while lane 2 high.
  * @DSI_EINT_DLN3_LP1_CONTENTION:        PHY level contention while lane 3 high.
+ * @DSI_EINT_PANEL_SPECIFIC_ERR:         DSI Protocol violation error.
  */
 enum dsi_error_int_index {
 	DSI_EINT_RDBK_SINGLE_ECC_ERR = 0,
@@ -209,6 +214,7 @@
 	DSI_EINT_DLN1_LP1_CONTENTION = 29,
 	DSI_EINT_DLN2_LP1_CONTENTION = 30,
 	DSI_EINT_DLN3_LP1_CONTENTION = 31,
+	DSI_EINT_PANEL_SPECIFIC_ERR = 32,
 
 	DSI_ERROR_INTERRUPT_COUNT
 };
@@ -248,6 +254,7 @@
  * @DSI_DLN1_LP1_CONTENTION:        PHY level contention while lane 1 is high.
  * @DSI_DLN2_LP1_CONTENTION:        PHY level contention while lane 2 is high.
  * @DSI_DLN3_LP1_CONTENTION:        PHY level contention while lane 3 is high.
+ * @DSI_PANEL_SPECIFIC_ERR:         DSI Protocol violation.
  */
 enum dsi_error_int_type {
 	DSI_RDBK_SINGLE_ECC_ERR = BIT(DSI_EINT_RDBK_SINGLE_ECC_ERR),
@@ -282,6 +289,7 @@
 	DSI_DLN1_LP1_CONTENTION = BIT(DSI_EINT_DLN1_LP1_CONTENTION),
 	DSI_DLN2_LP1_CONTENTION = BIT(DSI_EINT_DLN2_LP1_CONTENTION),
 	DSI_DLN3_LP1_CONTENTION = BIT(DSI_EINT_DLN3_LP1_CONTENTION),
+	DSI_PANEL_SPECIFIC_ERR = BIT(DSI_EINT_PANEL_SPECIFIC_ERR),
 };
 
 /**
@@ -735,6 +743,40 @@
 	 *                needs to be sent.
 	 */
 	void (*schedule_dma_cmd)(struct dsi_ctrl_hw *ctrl, int line_no);
+
+	/**
+	 * ctrl_reset() - Reset DSI lanes to recover from DSI errors
+	 * @ctrl:         Pointer to the controller host hardware.
+	 * @mask:         Indicates the error type.
+	 */
+	int (*ctrl_reset)(struct dsi_ctrl_hw *ctrl, int mask);
+
+	/**
+	 * mask_error_int() - Mask/Unmask particular DSI error interrupts
+	 * @ctrl:         Pointer to the controller host hardware.
+	 * @idx:	  Indicates the errors to be masked.
+	 * @en:		  Bool for mask or unmask of the error
+	 */
+	void (*mask_error_intr)(struct dsi_ctrl_hw *ctrl, u32 idx, bool en);
+
+	/**
+	 * error_intr_ctrl() - Mask/Unmask master DSI error interrupt
+	 * @ctrl:         Pointer to the controller host hardware.
+	 * @en:		  Bool for mask or unmask of DSI error
+	 */
+	void (*error_intr_ctrl)(struct dsi_ctrl_hw *ctrl, bool en);
+
+	/**
+	 * get_error_mask() - get DSI error interrupt mask status
+	 * @ctrl:         Pointer to the controller host hardware.
+	 */
+	u32 (*get_error_mask)(struct dsi_ctrl_hw *ctrl);
+
+	/**
+	 * get_hw_version() - get DSI controller hw version
+	 * @ctrl:         Pointer to the controller host hardware.
+	 */
+	u32 (*get_hw_version)(struct dsi_ctrl_hw *ctrl);
 };
 
 /*
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c
index 2959e94..1b4387f 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c
@@ -391,6 +391,9 @@
 		height_final = mode->v_active;
 	}
 
+	/* HS Timer value */
+	DSI_W32(ctrl, DSI_HS_TIMER_CTRL, 0x3FD08);
+
 	stream_ctrl = (stride_final + 1) << 16;
 	stream_ctrl |= (vc_id & 0x3) << 8;
 	stream_ctrl |= 0x39; /* packet data type */
@@ -834,6 +837,8 @@
 		ints |= DSI_DYN_REFRESH_DONE;
 	if (reg & BIT(30))
 		ints |= DSI_DESKEW_DONE;
+	if (reg & BIT(24))
+		ints |= DSI_ERROR;
 
 	pr_debug("[DSI_%d] Interrupt status = 0x%x, INT_CTRL=0x%x\n",
 		 ctrl->index, ints, reg);
@@ -870,6 +875,12 @@
 	if (ints & DSI_DESKEW_DONE)
 		reg |= BIT(30);
 
+	/*
+	 * Do not clear error status.
+	 * It will be cleared as part of
+	 * error handler function.
+	 */
+	reg &= ~BIT(24);
 	DSI_W32(ctrl, DSI_INT_CTRL, reg);
 
 	pr_debug("[DSI_%d] Clear interrupts, ints = 0x%x, INT_CTRL=0x%x\n",
@@ -936,7 +947,7 @@
 	u32 timeout_errors;
 	u32 clk_error;
 	u32 dsi_status;
-	u64 errors = 0;
+	u64 errors = 0, shift = 0x1;
 
 	dln0_phy_err = DSI_R32(ctrl, DSI_DLN0_PHY_ERR);
 	if (dln0_phy_err & BIT(0))
@@ -983,6 +994,8 @@
 		errors |= DSI_RDBK_INCOMPLETE_PKT;
 	if (ack_error & BIT(24))
 		errors |= DSI_PERIPH_ERROR_PKT;
+	if (ack_error & BIT(15))
+		errors |= (shift << DSI_EINT_PANEL_SPECIFIC_ERR);
 
 	timeout_errors = DSI_R32(ctrl, DSI_TIMEOUT_STATUS);
 	if (timeout_errors & BIT(0))
@@ -1020,7 +1033,6 @@
 	u32 timeout_error = 0;
 	u32 clk_error = 0;
 	u32 dsi_status = 0;
-	u32 int_ctrl = 0;
 
 	if (errors & DSI_RDBK_SINGLE_ECC_ERR)
 		ack_error |= BIT(16);
@@ -1032,6 +1044,8 @@
 		ack_error |= BIT(23);
 	if (errors & DSI_PERIPH_ERROR_PKT)
 		ack_error |= BIT(24);
+	if (errors & DSI_PANEL_SPECIFIC_ERR)
+		ack_error |= BIT(15);
 
 	if (errors & DSI_LP_RX_TIMEOUT)
 		timeout_error |= BIT(4);
@@ -1080,14 +1094,14 @@
 
 	DSI_W32(ctrl, DSI_DLN0_PHY_ERR, dln0_phy_err);
 	DSI_W32(ctrl, DSI_FIFO_STATUS, fifo_status);
+	/* Writing of an extra 0 is needed to clear ack error bits */
 	DSI_W32(ctrl, DSI_ACK_ERR_STATUS, ack_error);
+	wmb(); /* make sure register is committed */
+	DSI_W32(ctrl, DSI_ACK_ERR_STATUS, 0x0);
 	DSI_W32(ctrl, DSI_TIMEOUT_STATUS, timeout_error);
 	DSI_W32(ctrl, DSI_CLK_STATUS, clk_error);
 	DSI_W32(ctrl, DSI_STATUS, dsi_status);
 
-	int_ctrl = DSI_R32(ctrl, DSI_INT_CTRL);
-	int_ctrl |= BIT(24);
-	DSI_W32(ctrl, DSI_INT_CTRL, int_ctrl);
 	pr_debug("[DSI_%d] clear errors = 0x%llx, phy=0x%x, fifo=0x%x",
 		 ctrl->index, errors, dln0_phy_err, fifo_status);
 	pr_debug("[DSI_%d] ack=0x%x, timeout=0x%x, clk=0x%x, dsi=0x%x\n",
@@ -1348,3 +1362,102 @@
 	DSI_MMSS_MISC_W32(ctrl, MMSS_MISC_CLAMP_REG_OFF, reg);
 }
 
+int dsi_ctrl_hw_cmn_ctrl_reset(struct dsi_ctrl_hw *ctrl,
+		int mask)
+{
+	int rc = 0;
+	u32 data;
+
+	pr_debug("DSI CTRL and PHY reset. ctrl-num = %d %d\n",
+			ctrl->index, mask);
+
+	data = DSI_R32(ctrl, 0x0004);
+	/* Disable DSI video mode */
+	DSI_W32(ctrl, 0x004, (data & ~BIT(1)));
+	wmb(); /* ensure register committed */
+	/* Disable DSI controller */
+	DSI_W32(ctrl, 0x004, (data & ~(BIT(0) | BIT(1))));
+	wmb(); /* ensure register committed */
+	/* "Force On" all dynamic clocks */
+	DSI_W32(ctrl, 0x11c, 0x100a00);
+
+	/* DSI_SW_RESET */
+	DSI_W32(ctrl, 0x118, 0x1);
+	wmb(); /* ensure register is committed */
+	DSI_W32(ctrl, 0x118, 0x0);
+	wmb(); /* ensure register is committed */
+
+	/* Remove "Force On" all dynamic clocks */
+	DSI_W32(ctrl, 0x11c, 0x00);
+	/* Enable DSI controller */
+	DSI_W32(ctrl, 0x004, (data & ~BIT(1)));
+	wmb(); /* ensure register committed */
+
+	return rc;
+}
+
+void dsi_ctrl_hw_cmn_mask_error_intr(struct dsi_ctrl_hw *ctrl, u32 idx, bool en)
+{
+	u32 reg = 0;
+
+	reg = DSI_R32(ctrl, 0x10c);
+
+	if (idx & BIT(DSI_FIFO_OVERFLOW)) {
+		if (en)
+			reg |= (0xf << 16);
+		else
+			reg &= ~(0xf << 16);
+	}
+
+	if (idx & BIT(DSI_FIFO_UNDERFLOW)) {
+		if (en)
+			reg |= (0xf << 26);
+		else
+			reg &= ~(0xf << 26);
+	}
+
+	if (idx & BIT(DSI_LP_Rx_TIMEOUT)) {
+		if (en)
+			reg |= (0x7 << 23);
+		else
+			reg &= ~(0x7 << 23);
+	}
+
+	DSI_W32(ctrl, 0x10c, reg);
+	wmb(); /* ensure error is masked */
+}
+
+void dsi_ctrl_hw_cmn_error_intr_ctrl(struct dsi_ctrl_hw *ctrl, bool en)
+{
+	u32 reg = 0;
+	u32 dsi_total_mask = 0x2222AA02;
+
+	reg = DSI_R32(ctrl, 0x110);
+	reg &= dsi_total_mask;
+
+	if (en)
+		reg |= (BIT(24) | BIT(25));
+	else
+		reg &= ~BIT(25);
+
+	DSI_W32(ctrl, 0x110, reg);
+	wmb(); /* ensure error is masked */
+}
+
+u32 dsi_ctrl_hw_cmn_get_error_mask(struct dsi_ctrl_hw *ctrl)
+{
+	u32 reg = 0;
+
+	reg = DSI_R32(ctrl, 0x10c);
+
+	return reg;
+}
+
+u32 dsi_ctrl_hw_cmn_get_hw_version(struct dsi_ctrl_hw *ctrl)
+{
+	u32 reg = 0;
+
+	reg = DSI_R32(ctrl, 0x0);
+
+	return reg;
+}
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h b/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h
index 01a2478..d45f849 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h
@@ -585,4 +585,16 @@
 		uint32_t data2, uint32_t data3);
 };
 
+/**
+ * enum dsi_error_status - various dsi errors
+ * @DSI_FIFO_OVERFLOW:     DSI FIFO Overflow error
+ * @DSI_FIFO_UNDERFLOW:    DSI FIFO Underflow error
+ * @DSI_LP_Rx_TIMEOUT:     DSI LP/RX Timeout error
+ */
+enum dsi_error_status {
+	DSI_FIFO_OVERFLOW = 1,
+	DSI_FIFO_UNDERFLOW,
+	DSI_LP_Rx_TIMEOUT,
+};
+
 #endif /* _DSI_DEFS_H_ */
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
index 04413d9..b2e3e9c 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
@@ -1067,6 +1067,13 @@
 	case SDE_CONN_EVENT_CMD_DONE:
 		irq_status_idx = DSI_SINT_CMD_FRAME_DONE;
 		break;
+	case SDE_CONN_EVENT_VID_FIFO_OVERFLOW:
+	case SDE_CONN_EVENT_CMD_FIFO_UNDERFLOW:
+		if (event_info) {
+			for (i = 0; i < display->ctrl_count; i++)
+				display->ctrl[i].ctrl->recovery_cb =
+							*event_info;
+		}
 	default:
 		/* nothing to do */
 		pr_debug("[%s] unhandled event %d\n", display->name, event_idx);
@@ -4212,6 +4219,232 @@
 	return rc;
 }
 
+static void dsi_display_handle_fifo_underflow(struct work_struct *work)
+{
+	struct dsi_display *display = NULL;
+
+	display =  container_of(work, struct dsi_display, fifo_underflow_work);
+	if (!display)
+		return;
+	pr_debug("handle DSI FIFO underflow error\n");
+
+	dsi_display_clk_ctrl(display->dsi_clk_handle,
+			DSI_ALL_CLKS, DSI_CLK_ON);
+	dsi_display_soft_reset(display);
+	dsi_display_clk_ctrl(display->dsi_clk_handle,
+			DSI_ALL_CLKS, DSI_CLK_OFF);
+}
+
+static void dsi_display_handle_fifo_overflow(struct work_struct *work)
+{
+	struct dsi_display *display = NULL;
+	struct dsi_display_ctrl *ctrl;
+	int i, rc;
+	int mask = BIT(20); /* clock lane */
+	int (*cb_func)(void *event_usr_ptr,
+		uint32_t event_idx, uint32_t instance_idx,
+		uint32_t data0, uint32_t data1,
+		uint32_t data2, uint32_t data3);
+	void *data;
+	u32 version = 0;
+
+	display =  container_of(work, struct dsi_display, fifo_overflow_work);
+	if (!display || !display->panel ||
+			(display->panel->panel_mode != DSI_OP_VIDEO_MODE))
+		return;
+
+	pr_debug("handle DSI FIFO overflow error\n");
+	dsi_display_clk_ctrl(display->dsi_clk_handle,
+			DSI_ALL_CLKS, DSI_CLK_ON);
+
+	/*
+	 * below recovery sequence is not applicable to
+	 * hw version 2.0.0, 2.1.0 and 2.2.0, so return early.
+	 */
+	ctrl = &display->ctrl[display->clk_master_idx];
+	version = dsi_ctrl_get_hw_version(ctrl->ctrl);
+	if (!version || (version < 0x20020001))
+		goto end;
+
+	/* reset ctrl and lanes */
+	for (i = 0 ; i < display->ctrl_count; i++) {
+		ctrl = &display->ctrl[i];
+		rc = dsi_ctrl_reset(ctrl->ctrl, mask);
+		rc = dsi_phy_lane_reset(ctrl->phy);
+	}
+
+	/* wait for display line count to be in active area */
+	ctrl = &display->ctrl[display->clk_master_idx];
+	if (ctrl->ctrl->recovery_cb.event_cb) {
+		cb_func = ctrl->ctrl->recovery_cb.event_cb;
+		data = ctrl->ctrl->recovery_cb.event_usr_ptr;
+		rc = cb_func(data, SDE_CONN_EVENT_VID_FIFO_OVERFLOW,
+				display->clk_master_idx, 0, 0, 0, 0);
+		if (rc < 0) {
+			pr_debug("sde callback failed\n");
+			goto end;
+		}
+	}
+
+	/* Enable Video mode for DSI controller */
+	for (i = 0 ; i < display->ctrl_count; i++) {
+		ctrl = &display->ctrl[i];
+		dsi_ctrl_vid_engine_en(ctrl->ctrl, true);
+	}
+	/*
+	 * Add sufficient delay to make sure
+	 * pixel transmission has started
+	 */
+	udelay(200);
+end:
+	dsi_display_clk_ctrl(display->dsi_clk_handle,
+			DSI_ALL_CLKS, DSI_CLK_OFF);
+}
+
+static void dsi_display_handle_lp_rx_timeout(struct work_struct *work)
+{
+	struct dsi_display *display = NULL;
+	struct dsi_display_ctrl *ctrl;
+	int i, rc;
+	int mask = (BIT(20) | (0xF << 16)); /* clock lane and 4 data lane */
+	int (*cb_func)(void *event_usr_ptr,
+		uint32_t event_idx, uint32_t instance_idx,
+		uint32_t data0, uint32_t data1,
+		uint32_t data2, uint32_t data3);
+	void *data;
+	u32 version = 0;
+
+	display =  container_of(work, struct dsi_display, fifo_overflow_work);
+	if (!display || (display->panel->panel_mode != DSI_OP_VIDEO_MODE))
+		return;
+	pr_debug("handle DSI LP RX Timeout error\n");
+
+	dsi_display_clk_ctrl(display->dsi_clk_handle,
+			DSI_ALL_CLKS, DSI_CLK_ON);
+
+	/*
+	 * below recovery sequence is not applicable to
+	 * hw version 2.0.0, 2.1.0 and 2.2.0, so return early.
+	 */
+	ctrl = &display->ctrl[display->clk_master_idx];
+	version = dsi_ctrl_get_hw_version(ctrl->ctrl);
+	if (!version || (version < 0x20020001))
+		goto end;
+
+	/* reset ctrl and lanes */
+	for (i = 0 ; i < display->ctrl_count; i++) {
+		ctrl = &display->ctrl[i];
+		rc = dsi_ctrl_reset(ctrl->ctrl, mask);
+		rc = dsi_phy_lane_reset(ctrl->phy);
+	}
+
+	ctrl = &display->ctrl[display->clk_master_idx];
+	if (ctrl->ctrl->recovery_cb.event_cb) {
+		cb_func = ctrl->ctrl->recovery_cb.event_cb;
+		data = ctrl->ctrl->recovery_cb.event_usr_ptr;
+		rc = cb_func(data, SDE_CONN_EVENT_VID_FIFO_OVERFLOW,
+				display->clk_master_idx, 0, 0, 0, 0);
+		if (rc < 0) {
+			pr_debug("Target is in suspend/shutdown\n");
+			goto end;
+		}
+	}
+
+	/* Enable Video mode for DSI controller */
+	for (i = 0 ; i < display->ctrl_count; i++) {
+		ctrl = &display->ctrl[i];
+		dsi_ctrl_vid_engine_en(ctrl->ctrl, true);
+	}
+
+	/*
+	 * Add sufficient delay to make sure
+	 * pixel transmission as started
+	 */
+	udelay(200);
+end:
+	dsi_display_clk_ctrl(display->dsi_clk_handle,
+			DSI_ALL_CLKS, DSI_CLK_OFF);
+}
+
+static int dsi_display_cb_error_handler(void *data,
+		uint32_t event_idx, uint32_t instance_idx,
+		uint32_t data0, uint32_t data1,
+		uint32_t data2, uint32_t data3)
+{
+	struct dsi_display *display =  data;
+
+	if (!display)
+		return -EINVAL;
+
+	switch (event_idx) {
+	case DSI_FIFO_UNDERFLOW:
+		queue_work(display->err_workq, &display->fifo_underflow_work);
+		break;
+	case DSI_FIFO_OVERFLOW:
+		queue_work(display->err_workq, &display->fifo_overflow_work);
+		break;
+	case DSI_LP_Rx_TIMEOUT:
+		queue_work(display->err_workq, &display->lp_rx_timeout_work);
+		break;
+	default:
+		pr_warn("unhandled error interrupt: %d\n", event_idx);
+		break;
+	}
+
+	return 0;
+}
+
+static void dsi_display_register_error_handler(struct dsi_display *display)
+{
+	int i = 0;
+	struct dsi_display_ctrl *ctrl;
+	struct dsi_event_cb_info event_info;
+
+	if (!display)
+		return;
+
+	display->err_workq = create_singlethread_workqueue("dsi_err_workq");
+	if (!display->err_workq) {
+		pr_err("failed to create dsi workq!\n");
+		return;
+	}
+
+	INIT_WORK(&display->fifo_underflow_work,
+				dsi_display_handle_fifo_underflow);
+	INIT_WORK(&display->fifo_overflow_work,
+				dsi_display_handle_fifo_overflow);
+	INIT_WORK(&display->lp_rx_timeout_work,
+				dsi_display_handle_lp_rx_timeout);
+
+	memset(&event_info, 0, sizeof(event_info));
+
+	event_info.event_cb = dsi_display_cb_error_handler;
+	event_info.event_usr_ptr = display;
+
+	for (i = 0; i < display->ctrl_count; i++) {
+		ctrl = &display->ctrl[i];
+		ctrl->ctrl->irq_info.irq_err_cb = event_info;
+	}
+}
+
+static void dsi_display_unregister_error_handler(struct dsi_display *display)
+{
+	int i = 0;
+	struct dsi_display_ctrl *ctrl;
+
+	if (!display)
+		return;
+
+	for (i = 0; i < display->ctrl_count; i++) {
+		ctrl = &display->ctrl[i];
+		memset(&ctrl->ctrl->irq_info.irq_err_cb,
+				0, sizeof(struct dsi_event_cb_info));
+	}
+
+	if (display->err_workq)
+		destroy_workqueue(display->err_workq);
+}
+
 int dsi_display_prepare(struct dsi_display *display)
 {
 	int rc = 0;
@@ -4289,6 +4522,8 @@
 		       display->name, rc);
 		goto error_phy_disable;
 	}
+	/* Set up DSI ERROR event callback */
+	dsi_display_register_error_handler(display);
 
 	rc = dsi_display_ctrl_host_enable(display);
 	if (rc) {
@@ -4750,6 +4985,9 @@
 		pr_err("[%s] failed to disable Link clocks, rc=%d\n",
 		       display->name, rc);
 
+	/* Free up DSI ERROR event callback */
+	dsi_display_unregister_error_handler(display);
+
 	rc = dsi_display_ctrl_deinit(display);
 	if (rc)
 		pr_err("[%s] failed to deinit controller, rc=%d\n",
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
index 356c7c7..1600add 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
@@ -211,6 +211,11 @@
 
 	bool misr_enable;
 	u32 misr_frame_count;
+	/* multiple dsi error handlers */
+	struct workqueue_struct *err_workq;
+	struct work_struct fifo_underflow_work;
+	struct work_struct fifo_overflow_work;
+	struct work_struct lp_rx_timeout_work;
 };
 
 int dsi_display_dev_probe(struct platform_device *pdev);
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c
index b747d2a..4210f77 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c
@@ -843,6 +843,21 @@
 	return rc;
 }
 
+int dsi_phy_lane_reset(struct msm_dsi_phy *phy)
+{
+	int ret = 0;
+
+	if (!phy)
+		return ret;
+
+	mutex_lock(&phy->phy_lock);
+	if (phy->hw.ops.phy_lane_reset)
+		ret = phy->hw.ops.phy_lane_reset(&phy->hw);
+	mutex_unlock(&phy->phy_lock);
+
+	return ret;
+}
+
 /**
  * dsi_phy_disable() - disable DSI PHY hardware.
  * @phy:        DSI PHY handle.
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h
index d7b9f0a..c462d4b 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h
@@ -225,6 +225,14 @@
 			      u32 *timing, u32 size);
 
 /**
+ * dsi_phy_lane_reset() - Reset DSI PHY lanes in case of error
+ * @phy:	DSI PHY handle
+ *
+ * Return: error code.
+ */
+int dsi_phy_lane_reset(struct msm_dsi_phy *phy);
+
+/**
  * dsi_phy_drv_register() - register platform driver for dsi phy
  */
 void dsi_phy_drv_register(void);
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw.h b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw.h
index 51c2f46..efebd99 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw.h
@@ -233,6 +233,13 @@
 	int (*phy_timing_val)(struct dsi_phy_per_lane_cfgs *timing_val,
 				u32 *timing, u32 size);
 
+	/**
+	 * phy_lane_reset() - Reset dsi phy lanes in case of error.
+	 * @phy:      Pointer to DSI PHY hardware object.
+	 * Return:    error code.
+	 */
+	int (*phy_lane_reset)(struct dsi_phy_hw *phy);
+
 	void *timing_ops;
 	struct phy_ulps_config_ops ulps_ops;
 };
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v3_0.c b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v3_0.c
index 371239d..8d91141 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v3_0.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v3_0.c
@@ -17,6 +17,7 @@
 #include <linux/iopoll.h>
 #include "dsi_hw.h"
 #include "dsi_phy_hw.h"
+#include "dsi_catalog.h"
 
 #define DSIPHY_CMN_CLK_CFG0						0x010
 #define DSIPHY_CMN_CLK_CFG1						0x014
@@ -373,6 +374,29 @@
 		 lanes);
 }
 
+int dsi_phy_hw_v3_0_lane_reset(struct dsi_phy_hw *phy)
+{
+	int ret = 0, loop = 10, u_dly = 200;
+	u32 ln_status = 0;
+
+	while ((ln_status != 0x1f) && loop) {
+		DSI_W32(phy, DSIPHY_CMN_LANE_CTRL3, 0x1f);
+		wmb(); /* ensure register is committed */
+		loop--;
+		udelay(u_dly);
+		ln_status = DSI_R32(phy, DSIPHY_CMN_LANE_STATUS1);
+		pr_debug("trial no: %d\n", loop);
+	}
+
+	if (!loop)
+		pr_debug("could not reset phy lanes\n");
+
+	DSI_W32(phy, DSIPHY_CMN_LANE_CTRL3, 0x0);
+	wmb(); /* ensure register is committed */
+
+	return ret;
+}
+
 void dsi_phy_hw_v3_0_ulps_exit(struct dsi_phy_hw *phy,
 			struct dsi_phy_cfg *cfg, u32 lanes)
 {
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index 0e965d7..975cfdd 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -231,11 +231,13 @@
  * @MSM_ENC_COMMIT_DONE - wait for the driver to flush the registers to HW
  * @MSM_ENC_TX_COMPLETE - wait for the HW to transfer the frame to panel
  * @MSM_ENC_VBLANK - wait for the HW VBLANK event (for driver-internal waiters)
+ * @MSM_ENC_ACTIVE_REGION - wait for the TG to be in active pixel region
  */
 enum msm_event_wait {
 	MSM_ENC_COMMIT_DONE = 0,
 	MSM_ENC_TX_COMPLETE,
 	MSM_ENC_VBLANK,
+	MSM_ENC_ACTIVE_REGION,
 };
 
 /**
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c
index e4b4dd9..131cf85 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.c
+++ b/drivers/gpu/drm/msm/sde/sde_connector.c
@@ -153,7 +153,7 @@
 {
 	struct sde_connector *c_conn;
 	unsigned long irq_flags;
-	void (*cb_func)(uint32_t event_idx,
+	int (*cb_func)(uint32_t event_idx,
 			uint32_t instance_idx, void *usr,
 			uint32_t data0, uint32_t data1,
 			uint32_t data2, uint32_t data3);
@@ -176,7 +176,7 @@
 	spin_unlock_irqrestore(&c_conn->event_lock, irq_flags);
 
 	if (cb_func)
-		cb_func(event_idx, instance_idx, usr,
+		rc = cb_func(event_idx, instance_idx, usr,
 			data0, data1, data2, data3);
 	else
 		rc = -EAGAIN;
@@ -186,7 +186,7 @@
 
 int sde_connector_register_event(struct drm_connector *connector,
 		uint32_t event_idx,
-		void (*cb_func)(uint32_t event_idx,
+		int (*cb_func)(uint32_t event_idx,
 			uint32_t instance_idx, void *usr,
 			uint32_t data0, uint32_t data1,
 			uint32_t data2, uint32_t data3),
@@ -363,6 +363,22 @@
 	return 0;
 }
 
+static int sde_connector_handle_disp_recovery(uint32_t event_idx,
+			uint32_t instance_idx, void *usr,
+			uint32_t data0, uint32_t data1,
+			uint32_t data2, uint32_t data3)
+{
+	struct sde_connector *c_conn = usr;
+	int rc = 0;
+
+	if (!c_conn)
+		return -EINVAL;
+
+	rc = sde_kms_handle_recovery(c_conn->encoder);
+
+	return rc;
+}
+
 int sde_connector_get_info(struct drm_connector *connector,
 		struct msm_display_info *info)
 {
@@ -1660,9 +1676,17 @@
 		}
 	}
 
+	rc = sde_connector_get_info(&c_conn->base, &display_info);
+	if (!rc && (connector_type == DRM_MODE_CONNECTOR_DSI) &&
+			(display_info.capabilities & MSM_DISPLAY_CAP_VID_MODE))
+		sde_connector_register_event(&c_conn->base,
+			SDE_CONN_EVENT_VID_FIFO_OVERFLOW,
+			sde_connector_handle_disp_recovery,
+			c_conn);
+
 	msm_property_install_volatile_range(
-				&c_conn->property_info, "sde_drm_roi_v1", 0x0,
-				0, ~0, 0, CONNECTOR_PROP_ROI_V1);
+			&c_conn->property_info, "sde_drm_roi_v1", 0x0,
+			0, ~0, 0, CONNECTOR_PROP_ROI_V1);
 
 	/* install PP_DITHER properties */
 	_sde_connector_install_dither_property(dev, sde_kms, c_conn);
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.h b/drivers/gpu/drm/msm/sde/sde_connector.h
index efe4d6d..1944e94 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.h
+++ b/drivers/gpu/drm/msm/sde/sde_connector.h
@@ -230,6 +230,8 @@
 enum sde_connector_events {
 	SDE_CONN_EVENT_VID_DONE, /* video mode frame done */
 	SDE_CONN_EVENT_CMD_DONE, /* command mode frame done */
+	SDE_CONN_EVENT_VID_FIFO_OVERFLOW, /* dsi fifo overflow error */
+	SDE_CONN_EVENT_CMD_FIFO_UNDERFLOW, /* dsi fifo underflow error */
 	SDE_CONN_EVENT_COUNT,
 };
 
@@ -237,9 +239,10 @@
  * struct sde_connector_evt - local event registration entry structure
  * @cb_func: Pointer to desired callback function
  * @usr: User pointer to pass to callback on event trigger
+ * Returns: Zero success, negetive for failure
  */
 struct sde_connector_evt {
-	void (*cb_func)(uint32_t event_idx,
+	int (*cb_func)(uint32_t event_idx,
 			uint32_t instance_idx, void *usr,
 			uint32_t data0, uint32_t data1,
 			uint32_t data2, uint32_t data3);
@@ -540,7 +543,7 @@
  */
 int sde_connector_register_event(struct drm_connector *connector,
 		uint32_t event_idx,
-		void (*cb_func)(uint32_t event_idx,
+		int (*cb_func)(uint32_t event_idx,
 			uint32_t instance_idx, void *usr,
 			uint32_t data0, uint32_t data1,
 			uint32_t data2, uint32_t data3),
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index 077e834..ecd7d30 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -4020,6 +4020,9 @@
 		case MSM_ENC_VBLANK:
 			fn_wait = phys->ops.wait_for_vblank;
 			break;
+		case MSM_ENC_ACTIVE_REGION:
+			fn_wait = phys->ops.wait_for_active;
+			break;
 		default:
 			SDE_ERROR_ENC(sde_enc, "unknown wait event %d\n",
 					event);
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
index 061fc4a..b29b5ad 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
@@ -136,6 +136,7 @@
  * @get_line_count:		Obtain current vertical line count
  * @wait_dma_trigger:		Returns true if lut dma has to trigger and wait
  *                              unitl transaction is complete.
+ * @wait_for_active:		Wait for display scan line to be in active area
  */
 
 struct sde_encoder_phys_ops {
@@ -183,6 +184,7 @@
 	bool (*is_autorefresh_enabled)(struct sde_encoder_phys *phys);
 	int (*get_line_count)(struct sde_encoder_phys *phys);
 	bool (*wait_dma_trigger)(struct sde_encoder_phys *phys);
+	int (*wait_for_active)(struct sde_encoder_phys *phys);
 };
 
 /**
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
index 21793fc..ce136b4 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
@@ -1291,6 +1291,7 @@
 			sde_encoder_phys_cmd_is_autorefresh_enabled;
 	ops->handle_post_kickoff = sde_encoder_phys_cmd_handle_post_kickoff;
 	ops->get_line_count = sde_encoder_phys_cmd_get_line_count;
+	ops->wait_for_active = NULL;
 }
 
 struct sde_encoder_phys *sde_encoder_phys_cmd_init(
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
index 131b74f..c16b0cd 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
@@ -36,6 +36,10 @@
 /* maximum number of consecutive kickoff errors */
 #define KICKOFF_MAX_ERRORS	2
 
+/* Poll time to do recovery during active region */
+#define POLL_TIME_USEC_FOR_LN_CNT 500
+#define MAX_POLL_CNT 10
+
 static bool sde_encoder_phys_vid_is_master(
 		struct sde_encoder_phys *phys_enc)
 {
@@ -1003,6 +1007,72 @@
 	return vid_enc->hw_intf->ops.get_line_count(vid_enc->hw_intf);
 }
 
+static int sde_encoder_phys_vid_wait_for_active(
+			struct sde_encoder_phys *phys_enc)
+{
+	struct drm_display_mode mode;
+	struct sde_encoder_phys_vid *vid_enc;
+	u32 ln_cnt, min_ln_cnt, active_lns_cnt;
+	u32 clk_period, time_of_line;
+	u32 delay, retry = MAX_POLL_CNT;
+
+	vid_enc =  to_sde_encoder_phys_vid(phys_enc);
+
+	if (!vid_enc->hw_intf || !vid_enc->hw_intf->ops.get_line_count) {
+		SDE_ERROR_VIDENC(vid_enc, "invalid vid_enc params\n");
+		return -EINVAL;
+	}
+
+	mode = phys_enc->cached_mode;
+
+	/*
+	 * calculate clk_period as pico second to maintain good
+	 * accuracy with high pclk rate and this number is in 17 bit
+	 * range.
+	 */
+	clk_period = DIV_ROUND_UP_ULL(1000000000, mode.clock);
+	if (!clk_period) {
+		SDE_ERROR_VIDENC(vid_enc, "Unable to calculate clock period\n");
+		return -EINVAL;
+	}
+
+	min_ln_cnt = (mode.vtotal - mode.vsync_start) +
+		(mode.vsync_end - mode.vsync_start);
+	active_lns_cnt = mode.vdisplay;
+	time_of_line = mode.htotal * clk_period;
+
+	/* delay in micro seconds */
+	delay = (time_of_line * (min_ln_cnt +
+		(mode.vsync_start - mode.vdisplay))) / 1000000;
+
+	/*
+	 * Wait for max delay before
+	 * polling to check active region
+	 */
+	if (delay > POLL_TIME_USEC_FOR_LN_CNT)
+		delay = POLL_TIME_USEC_FOR_LN_CNT;
+
+	while (retry) {
+		ln_cnt = vid_enc->hw_intf->ops.get_line_count(vid_enc->hw_intf);
+
+		if ((ln_cnt >= min_ln_cnt) &&
+			(ln_cnt < (active_lns_cnt + min_ln_cnt))) {
+			SDE_DEBUG_VIDENC(vid_enc,
+					"Needed lines left line_cnt=%d\n",
+					ln_cnt);
+			return 0;
+		}
+
+		SDE_ERROR_VIDENC(vid_enc, "line count is less. line_cnt = %d\n",
+				ln_cnt);
+		/* Add delay so that line count is in active region */
+		udelay(delay);
+		retry--;
+	}
+
+	return -EINVAL;
+}
+
 static void sde_encoder_phys_vid_init_ops(struct sde_encoder_phys_ops *ops)
 {
 	ops->is_master = sde_encoder_phys_vid_is_master;
@@ -1027,6 +1097,7 @@
 	ops->hw_reset = sde_encoder_helper_hw_reset;
 	ops->get_line_count = sde_encoder_phys_vid_get_line_count;
 	ops->wait_dma_trigger = sde_encoder_phys_vid_wait_dma_trigger;
+	ops->wait_for_active = sde_encoder_phys_vid_wait_for_active;
 }
 
 struct sde_encoder_phys *sde_encoder_phys_vid_init(
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index f94239a..a0c5b77 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -844,6 +844,7 @@
 		.get_dst_format = dsi_display_get_dst_format,
 		.post_kickoff = dsi_conn_post_kickoff,
 		.check_status = dsi_display_check_status,
+		.enable_event = dsi_conn_enable_event
 	};
 	static const struct sde_connector_ops wb_ops = {
 		.post_init =    sde_wb_connector_post_init,
@@ -2999,3 +3000,9 @@
 
 	return ret;
 }
+
+int sde_kms_handle_recovery(struct drm_encoder *encoder)
+{
+	SDE_EVT32(DRMID(encoder), MSM_ENC_ACTIVE_REGION);
+	return sde_encoder_wait_for_event(encoder, MSM_ENC_ACTIVE_REGION);
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.h b/drivers/gpu/drm/msm/sde/sde_kms.h
index ab24c85..26c45e2 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.h
+++ b/drivers/gpu/drm/msm/sde/sde_kms.h
@@ -573,4 +573,11 @@
  */
 void sde_kms_timeline_status(struct drm_device *dev);
 
+/**
+ * sde_kms_handle_recovery - handler function for FIFO overflow issue
+ * @encoder: pointer to drm encoder structure
+ * return: 0 on success; error code otherwise
+ */
+int sde_kms_handle_recovery(struct drm_encoder *encoder);
+
 #endif /* __sde_kms_H__ */