Bläddra i källkod

disp: msm: sde: dma fence out of order handling in fence error case

Handle out of order dma fence signalling and propagation of fence
error. Out of order fence signaling is required only in Video mode.
For example, in case of N, N+1, N+2 frames where N, N+2 are good
frames and N+1 is frame with fence error. The release fence signal
sequence in video mode would be N+1, N, N+2.

Change-Id: I8b6f88cfeee945e28571b765f24ffea22fad23b8
Signed-off-by: GG Hou <[email protected]>
GG Hou 2 år sedan
förälder
incheckning
a658fb17b7

+ 20 - 0
msm/sde/sde_connector.c

@@ -1905,6 +1905,26 @@ void sde_conn_timeline_status(struct drm_connector *conn)
 	sde_fence_timeline_status(c_conn->retire_fence, &conn->base);
 }
 
+void sde_connector_fence_error_ctx_signal(struct drm_connector *conn, int input_fence_status,
+	bool is_vid)
+{
+	struct sde_connector *sde_conn;
+	struct sde_fence_context *ctx;
+	ktime_t time_stamp;
+
+	sde_conn = to_sde_connector(conn);
+	if (!sde_conn)
+		return;
+
+	ctx = sde_conn->retire_fence;
+	sde_fence_error_ctx_update(ctx, input_fence_status,
+		is_vid ? SET_ERROR_ONLY_VID : SET_ERROR_ONLY_CMD_RETIRE);
+	time_stamp = ktime_get();
+
+	sde_fence_signal(ctx, time_stamp, SDE_FENCE_SIGNAL, NULL);
+	sde_fence_error_ctx_update(ctx, 0, NO_ERROR);
+}
+
 void sde_connector_prepare_fence(struct drm_connector *connector)
 {
 	if (!connector) {

+ 9 - 0
msm/sde/sde_connector.h

@@ -952,6 +952,15 @@ struct drm_connector *sde_connector_init(struct drm_device *dev,
 		int connector_poll,
 		int connector_type);
 
+/**
+ * sde_connector_fence_error_ctx_signal - sde fence error context update for retire fence
+ * @conn: Pointer to drm connector object
+ * @input_fence_status: input fence status, negative if input fence error
+ * @is_vid:if encoder is video mode
+ */
+void sde_connector_fence_error_ctx_signal(struct drm_connector *conn, int input_fence_status,
+		bool is_vid);
+
 /**
  * sde_connector_prepare_fence - prepare fence support for current commit
  * @connector: Pointer to drm connector object

+ 2 - 2
msm/sde/sde_crtc.c

@@ -3879,7 +3879,7 @@ static int _sde_crtc_fences_wait_list(struct drm_crtc *crtc, bool use_hw_fences,
 	uint32_t wait_ms = 1;
 	struct msm_display_mode *msm_mode;
 	bool mode_switch;
-	int i, rc = 0;
+	int i, status = 0, rc = 0;
 
 	msm_mode = sde_crtc_get_msm_mode(crtc->state);
 	mode_switch = msm_is_mode_seamless_poms(msm_mode);
@@ -3933,7 +3933,7 @@ static int _sde_crtc_fences_wait_list(struct drm_crtc *crtc, bool use_hw_fences,
 			else
 				wait_ms = 0;
 
-			rc = sde_plane_wait_input_fence(plane, wait_ms);
+			rc = sde_plane_wait_input_fence(plane, wait_ms, &status);
 		} while (wait_ms && rc == -ERESTARTSYS);
 	}
 

+ 2 - 0
msm/sde/sde_crtc.h

@@ -375,6 +375,7 @@ enum sde_crtc_hw_fence_flags {
  *                          sde_crtc_hw_fence_flags for available fields.
  * @hwfence_out_fences_skip: number of frames to skip before create a new hw-fence, this can be
  *                   used to slow-down creation of output hw-fences for debugging purposes.
+ * @input_fence_status : input fence status, negative if the fence has been completed with error.
  * @hanle_fence_error_bw_update: bool to indicate if it is fence error and need to avoid bw vote.
  */
 struct sde_crtc {
@@ -490,6 +491,7 @@ struct sde_crtc {
 
 	DECLARE_BITMAP(hwfence_features_mask, HW_FENCE_FEATURES_MAX);
 	u32 hwfence_out_fences_skip;
+	int input_fence_status;
 	bool handle_fence_error_bw_update;
 };
 

+ 118 - 0
msm/sde/sde_encoder.c

@@ -2021,6 +2021,24 @@ static void sde_encoder_misr_configure(struct drm_encoder *drm_enc,
 	sde_enc->misr_reconfigure = false;
 }
 
+void sde_encoder_clear_fence_error_in_progress(struct sde_encoder_phys *phys_enc)
+{
+	struct sde_crtc *sde_crtc;
+
+	if (!phys_enc || !phys_enc->parent || !phys_enc->parent->crtc) {
+		SDE_DEBUG("invalid sde_encoder_phys.\n");
+		return;
+	}
+
+	sde_crtc = to_sde_crtc(phys_enc->parent->crtc);
+
+	if ((!phys_enc->sde_hw_fence_error_status) && (!sde_crtc->input_fence_status) &&
+		phys_enc->fence_error_handle_in_progress) {
+		phys_enc->fence_error_handle_in_progress = false;
+		SDE_EVT32(DRMID(phys_enc->parent), phys_enc->fence_error_handle_in_progress);
+	}
+}
+
 static int sde_encoder_hw_fence_signal(struct sde_encoder_phys *phys_enc)
 {
 	struct sde_hw_ctl *hw_ctl;
@@ -2062,6 +2080,100 @@ static int sde_encoder_hw_fence_signal(struct sde_encoder_phys *phys_enc)
 		SDE_EVT32(DRMID(phys_enc->parent), SDE_EVTLOG_FUNC_CASE2);
 	}
 
+	SDE_EVT32(DRMID(phys_enc->parent), SDE_EVTLOG_FUNC_EXIT);
+	return rc;
+}
+
+int sde_encoder_handle_dma_fence_out_of_order(struct drm_encoder *drm_enc)
+{
+	struct drm_crtc *crtc;
+	struct sde_crtc *sde_crtc;
+	struct sde_crtc_state *cstate;
+	struct sde_encoder_virt *sde_enc;
+	struct sde_encoder_phys *phys_enc;
+	struct sde_fence_context *ctx;
+	struct drm_connector *conn;
+	bool is_vid;
+	int i, fence_status = 0, pending_kickoff_cnt = 0, rc = 0;
+	ktime_t time_stamp;
+
+	crtc = drm_enc->crtc;
+	sde_crtc = to_sde_crtc(crtc);
+	cstate = to_sde_crtc_state(crtc->state);
+
+	sde_enc = to_sde_encoder_virt(drm_enc);
+	if (!sde_enc || !sde_enc->phys_encs[0]) {
+		SDE_ERROR("invalid params\n");
+		return -EINVAL;
+	}
+
+	phys_enc = sde_enc->phys_encs[0];
+
+	ctx = sde_crtc->output_fence;
+	time_stamp = ktime_get();
+	/* out of order sw fence error signal for video panel.
+	 * Hold the last good frame for video mode panel.
+	 */
+	if (phys_enc->sde_hw_fence_error_value) {
+		fence_status = phys_enc->sde_hw_fence_error_value;
+		phys_enc->sde_hw_fence_error_value = 0;
+	} else {
+		fence_status = sde_crtc->input_fence_status;
+	}
+
+	is_vid = sde_encoder_check_curr_mode(drm_enc, MSM_DISPLAY_VIDEO_MODE);
+	SDE_EVT32(is_vid, fence_status, phys_enc->fence_error_handle_in_progress);
+	if (is_vid) {
+		/* update last_good_frame_fence_seqno after at least one good frame */
+		if (!phys_enc->fence_error_handle_in_progress) {
+			ctx->sde_fence_error_ctx.last_good_frame_fence_seqno =
+				ctx->sde_fence_error_ctx.curr_frame_fence_seqno - 1;
+			phys_enc->fence_error_handle_in_progress = true;
+		}
+
+		/* signal release fence for vid panel */
+		sde_fence_error_ctx_update(ctx, fence_status, HANDLE_OUT_OF_ORDER);
+	} else {
+		/*
+		 * out of order sw fence error signal for CMD panel.
+		 * always wait frame done for cmd panel.
+		 * signal the sw fence error release fence for CMD panel.
+		 */
+		pending_kickoff_cnt = atomic_read(&phys_enc->pending_kickoff_cnt);
+
+		if (pending_kickoff_cnt) {
+			SDE_EVT32(DRMID(drm_enc), pending_kickoff_cnt, SDE_EVTLOG_FUNC_CASE1);
+			rc = sde_encoder_wait_for_event(drm_enc, MSM_ENC_TX_COMPLETE);
+			if (rc && rc != -EWOULDBLOCK) {
+				SDE_DEBUG("wait for frame done failed %d\n", rc);
+				SDE_EVT32(DRMID(drm_enc), rc, pending_kickoff_cnt,
+					SDE_EVTLOG_ERROR);
+			}
+		}
+
+		/* update fence error context for cmd panel */
+		sde_fence_error_ctx_update(ctx, fence_status, SET_ERROR_ONLY_CMD_RELEASE);
+	}
+
+	sde_fence_signal(ctx, time_stamp, SDE_FENCE_SIGNAL, NULL);
+
+	/**
+	 * clear flag in sde_fence_error_ctx after fence signal,
+	 * the last_good_frame_fence_seqno is supposed to be updated or cleared after
+	 * at least one good frame in case of constant fence error
+	 */
+	sde_fence_error_ctx_update(ctx, 0, NO_ERROR);
+
+	/* signal retire fence */
+	for (i = 0; i < cstate->num_connectors; ++i) {
+		conn = cstate->connectors[i];
+		sde_connector_fence_error_ctx_signal(conn, fence_status, is_vid);
+	}
+
+	SDE_EVT32(ctx->sde_fence_error_ctx.fence_error_status,
+		ctx->sde_fence_error_ctx.fence_error_state,
+		ctx->sde_fence_error_ctx.last_good_frame_fence_seqno, pending_kickoff_cnt);
+
 	return rc;
 }
 
@@ -2086,6 +2198,12 @@ int sde_encoder_hw_fence_error_handle(struct drm_encoder *drm_enc)
 		SDE_EVT32(DRMID(drm_enc), rc, SDE_EVTLOG_ERROR);
 	}
 
+	rc = sde_encoder_handle_dma_fence_out_of_order(phys_enc->parent);
+	if (rc) {
+		SDE_DEBUG("sde_encoder_handle_dma_fence_out_of_order failed, rc = %d\n", rc);
+		SDE_EVT32(DRMID(phys_enc->parent), rc, SDE_EVTLOG_ERROR);
+	}
+
 	phys_enc->sde_hw_fence_error_status = false;
 	SDE_EVT32(DRMID(drm_enc), SDE_EVTLOG_FUNC_EXIT);
 	return rc;

+ 6 - 0
msm/sde/sde_encoder.h

@@ -777,6 +777,12 @@ void sde_encoder_add_data_to_minidump_va(struct drm_encoder *drm_enc);
  */
 void sde_encoder_misr_sign_event_notify(struct drm_encoder *drm_enc);
 
+/**
+ * sde_encoder_handle_dma_fence_out_of_order - sw dma fence out of order signal
+ * @drm_enc: pointer to drm encoder
+ */
+int sde_encoder_handle_dma_fence_out_of_order(struct drm_encoder *drm_enc);
+
 /**
  * sde_encoder_register_misr_event - register or deregister MISR event
  * @drm_enc: pointer to drm encoder

+ 13 - 0
msm/sde/sde_encoder_phys.h

@@ -342,6 +342,11 @@ struct sde_encoder_irq {
  * @sde_hw_fence_handle:	Hw fence driver client handle, this handle was returned
  *				during the call 'msm_hw_fence_register' to register the
  *				client
+ * @fence_error_handle_in_progress:
+ *				bool to indicate if fence error handling in progress
+ *				This is set once fence error occurs and cleared only when
+ *				good frame is received. Not cleared in continous fence
+ *				error cases
  * @frame_trigger_mode:		frame trigger mode indication for command
  *				mode display
  * @recovered:			flag set to true when recovered from pp timeout
@@ -397,6 +402,7 @@ struct sde_encoder_phys {
 	bool sde_hw_fence_error_status;
 	int sde_hw_fence_error_value;
 	u64 sde_hw_fence_handle;
+	bool fence_error_handle_in_progress;
 	enum frame_trigger_mode_type frame_trigger_mode;
 	bool recovered;
 	bool autorefresh_disable_trans;
@@ -408,6 +414,13 @@ static inline int sde_encoder_phys_inc_pending(struct sde_encoder_phys *phys)
 	return atomic_inc_return(&phys->pending_kickoff_cnt);
 }
 
+/*
+ * sde_encoder_clear_fence_error_in_progress - clear fence_error_handle_in_progress flag
+ *	after good frame
+ * @phys_enc: Pointer to physical encoder structure
+ */
+void sde_encoder_clear_fence_error_in_progress(struct sde_encoder_phys *phys_enc);
+
 /**
  * sde_encoder_hw_fence_signal - hw fence related fence error handing
  * @phys_enc: Pointer to physical encoder structure

+ 3 - 0
msm/sde/sde_encoder_phys_vid.c

@@ -968,6 +968,9 @@ static int _sde_encoder_phys_vid_wait_for_vblank(
 	SDE_EVT32(DRMID(phys_enc->parent), event, notify, timeout, ret,
 			ret ? SDE_EVTLOG_FATAL : 0, SDE_EVTLOG_FUNC_EXIT);
 
+	if (!ret)
+		sde_encoder_clear_fence_error_in_progress(phys_enc);
+
 	return ret;
 }
 

+ 47 - 1
msm/sde/sde_fence.c

@@ -730,6 +730,19 @@ int sde_fence_update_input_hw_fence_signal(struct sde_hw_ctl *hw_ctl, u32 debugf
 	return 0;
 }
 
+void sde_fence_error_ctx_update(struct sde_fence_context *ctx, int input_fence_status,
+	enum sde_fence_error_state sde_fence_error_state)
+{
+	if (!ctx) {
+		SDE_DEBUG("invalid fence\n");
+		SDE_EVT32(input_fence_status, sde_fence_error_state, SDE_EVTLOG_ERROR);
+		return;
+	}
+
+	ctx->sde_fence_error_ctx.fence_error_status = input_fence_status;
+	ctx->sde_fence_error_ctx.fence_error_state = sde_fence_error_state;
+}
+
 void *sde_sync_get(uint64_t fd)
 {
 	/* force signed compare, fdget accepts an int argument */
@@ -779,7 +792,7 @@ static void sde_fence_dump_user_fds_info(struct dma_fence *base_fence)
 	}
 }
 
-signed long sde_sync_wait(void *fnc, long timeout_ms)
+signed long sde_sync_wait(void *fnc, long timeout_ms, int *error_status)
 {
 	struct dma_fence *fence = fnc;
 	int rc, status = 0;
@@ -808,6 +821,8 @@ signed long sde_sync_wait(void *fnc, long timeout_ms)
 		}
 	}
 
+	if (error_status)
+		*error_status = fence->error;
 	return rc;
 }
 
@@ -955,6 +970,8 @@ static int _sde_fence_create_fd(void *fence_ctx, uint32_t val, struct sde_hw_ctl
 		ctx->context, val);
 	kref_get(&ctx->kref);
 
+	ctx->sde_fence_error_ctx.curr_frame_fence_seqno = val;
+
 	/* create fd */
 	fd = get_unused_fd_flags(0);
 	if (fd < 0) {
@@ -1045,6 +1062,8 @@ static void _sde_fence_trigger(struct sde_fence_context *ctx, bool error, ktime_
 	unsigned long flags;
 	struct sde_fence *fc, *next;
 	bool is_signaled = false;
+	enum sde_fence_error_state fence_error_state = 0;
+	struct sde_fence_error_ctx *fence_error_ctx;
 
 	kref_get(&ctx->kref);
 
@@ -1054,10 +1073,37 @@ static void _sde_fence_trigger(struct sde_fence_context *ctx, bool error, ktime_
 		goto end;
 	}
 
+	fence_error_ctx = &ctx->sde_fence_error_ctx;
+
 	list_for_each_entry_safe(fc, next, &ctx->fence_list_head, fence_list) {
 		spin_lock_irqsave(&ctx->lock, flags);
 		if (error)
 			dma_fence_set_error(&fc->base, -EBUSY);
+
+		fence_error_state = fence_error_ctx->fence_error_state;
+		if (fence_error_state) {
+			if (fence_error_state == HANDLE_OUT_OF_ORDER &&
+				fence_error_ctx->last_good_frame_fence_seqno == fc->base.seqno) {
+				SDE_EVT32(fence_error_ctx->last_good_frame_fence_seqno,
+					fence_error_state, SDE_EVTLOG_FUNC_CASE1);
+				spin_unlock_irqrestore(&ctx->lock, flags);
+				continue;
+			} else if (((fence_error_state == HANDLE_OUT_OF_ORDER) ||
+				(fence_error_state == SET_ERROR_ONLY_VID) ||
+				(fence_error_state == SET_ERROR_ONLY_CMD_RELEASE))
+				&& (fence_error_ctx->fence_error_status < 0)) {
+				dma_fence_set_error(&fc->base, fence_error_ctx->fence_error_status);
+				dma_fence_signal_timestamp_locked(&fc->base, ts);
+				spin_unlock_irqrestore(&ctx->lock, flags);
+
+				SDE_EVT32(fence_error_state, fence_error_ctx->fence_error_status,
+					ktime_to_us(ts), fc->base.seqno, SDE_EVTLOG_FUNC_CASE2);
+				list_del_init(&fc->fence_list);
+				dma_fence_put(&fc->base);
+				continue;
+			}
+		}
+
 		is_signaled = sde_fence_signaled(&fc->base);
 		if (is_signaled)
 			dma_fence_signal_timestamp_locked(&fc->base, ts);

+ 47 - 2
msm/sde/sde_fence.h

@@ -26,6 +26,37 @@
 #define SDE_FENCE_NAME_SIZE	24
 
 #define MAX_SDE_HFENCE_OUT_SIGNAL_PING_PONG 2
+
+/**
+ * enum sde_fence_error_state - fence error state handled in _sde_fence_trigger
+ * @NO_ERROR: no fence error
+ * @SET_ERROR_ONLY_CMD_RELEASE: cmd panel release fence error state
+ * @SET_ERROR_ONLY_CMD_RETIRE: cmd panel retire fence error state
+ * @SET_ERROR_ONLY_VID: vid panel fence error state
+ * @HANDLE_OUT_OF_ORDER: vid panel out of order handle
+ */
+enum sde_fence_error_state {
+	NO_ERROR,
+	SET_ERROR_ONLY_CMD_RELEASE,
+	SET_ERROR_ONLY_CMD_RETIRE,
+	SET_ERROR_ONLY_VID,
+	HANDLE_OUT_OF_ORDER,
+};
+
+/**
+ * sde_fence_error_ctx - reserve frame info for fence error handing
+ * @last_good_frame_fence_seqno: last good frame fence seqno
+ * @curr_frame_fence_seqno: currently frame fence seqno
+ * @fence_error_status: fence error status
+ * @sde_fence_error_state: fence error state
+ */
+struct sde_fence_error_ctx {
+	u32 last_good_frame_fence_seqno;
+	u32 curr_frame_fence_seqno;
+	int fence_error_status;
+	enum sde_fence_error_state fence_error_state;
+};
+
 /**
  * struct sde_fence_context - release/retire fence context/timeline structure
  * @commit_count: Number of detected commits since bootup
@@ -35,6 +66,7 @@
  * @lock: spinlock for fence counter protection
  * @list_lock: spinlock for timeline protection
  * @context: fence context
+ * @sde_fence_error_ctx: sde fence error context
  * @list_head: fence list to hold all the fence created on this context
  * @name: name of fence context/timeline
  */
@@ -46,6 +78,7 @@ struct sde_fence_context {
 	spinlock_t lock;
 	spinlock_t list_lock;
 	u64 context;
+	struct sde_fence_error_ctx sde_fence_error_ctx;
 	struct list_head fence_list_head;
 	char name[SDE_FENCE_NAME_SIZE];
 };
@@ -135,13 +168,14 @@ void sde_sync_put(void *fence);
  *
  * @fence: Pointer to sync fence
  * @timeout_ms: Time to wait, in milliseconds. Waits forever if timeout_ms < 0
+ * @error_status: status of fence
  *
  * Return:
  * Zero if timed out
  * -ERESTARTSYS if wait interrupted
  * remaining jiffies in all other success cases.
  */
-signed long sde_sync_wait(void *fence, long timeout_ms);
+signed long sde_sync_wait(void *fence, long timeout_ms, int *error_status);
 
 /**
  * sde_sync_get_name_prefix - get integer representation of fence name prefix
@@ -229,6 +263,17 @@ int sde_fence_update_hw_fences_txq(struct sde_fence_context *ctx, bool vid_mode,
 int sde_fence_update_input_hw_fence_signal(struct sde_hw_ctl *ctl, u32 debugfs_hw_fence,
 	struct sde_hw_mdp *hw_mdp, bool disable);
 
+/**
+ * sde_fence_error_ctx_update - update fence_error_state and fence_error_status in
+ *                              sde_fence_error_ctx.
+ *
+ * @ctx: sde_fence_context
+ * @input_fence_status: input fence status, negative if input fence error
+ * @sde_fence_error_state: sde fence error state
+ */
+void sde_fence_error_ctx_update(struct sde_fence_context *ctx, int input_fence_status,
+	enum sde_fence_error_state sde_fence_error_state);
+
 /**
  * sde_fence_deinit - deinit fence container
  * @fence: Pointer fence container
@@ -302,7 +347,7 @@ static inline void sde_sync_put(void *fence)
 {
 }
 
-static inline signed long sde_sync_wait(void *fence, long timeout_ms)
+static inline signed long sde_sync_wait(void *fence, long timeout_ms, int *error_status)
 {
 	return 0;
 }

+ 2 - 2
msm/sde/sde_plane.c

@@ -637,7 +637,7 @@ bool sde_plane_is_sw_fence_signaled(struct drm_plane *plane)
 	return false;
 }
 
-int sde_plane_wait_input_fence(struct drm_plane *plane, uint32_t wait_ms)
+int sde_plane_wait_input_fence(struct drm_plane *plane, uint32_t wait_ms, int *error_status)
 {
 	struct sde_plane *psde;
 	struct sde_plane_state *pstate;
@@ -657,7 +657,7 @@ int sde_plane_wait_input_fence(struct drm_plane *plane, uint32_t wait_ms)
 
 		if (input_fence) {
 			prefix = sde_sync_get_name_prefix(input_fence);
-			rc = sde_sync_wait(input_fence, wait_ms);
+			rc = sde_sync_wait(input_fence, wait_ms, error_status);
 
 			switch (rc) {
 			case 0:

+ 2 - 1
msm/sde/sde_plane.h

@@ -336,9 +336,10 @@ int sde_plane_validate_src_addr(struct drm_plane *plane,
  * sde_plane_wait_input_fence - wait for input fence object
  * @plane:   Pointer to DRM plane object
  * @wait_ms: Wait timeout value
+ * @error_status: Status of input fence
  * Returns: Zero on success
  */
-int sde_plane_wait_input_fence(struct drm_plane *plane, uint32_t wait_ms);
+int sde_plane_wait_input_fence(struct drm_plane *plane, uint32_t wait_ms, int *error_status);
 
 /**
  * sde_plane_color_fill - enables color fill on plane