diff --git a/msm/sde/sde_connector.c b/msm/sde/sde_connector.c index 68bb0ed784..f08dae0901 100644 --- a/msm/sde/sde_connector.c +++ b/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) { diff --git a/msm/sde/sde_connector.h b/msm/sde/sde_connector.h index 0b826eb3c7..8e5c55bf79 100644 --- a/msm/sde/sde_connector.h +++ b/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 diff --git a/msm/sde/sde_crtc.c b/msm/sde/sde_crtc.c index 160b25c40a..b910ef8d35 100644 --- a/msm/sde/sde_crtc.c +++ b/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); } diff --git a/msm/sde/sde_crtc.h b/msm/sde/sde_crtc.h index ea19a38ad6..e613f28cb7 100644 --- a/msm/sde/sde_crtc.h +++ b/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; }; diff --git a/msm/sde/sde_encoder.c b/msm/sde/sde_encoder.c index 2d542d3895..4c7309917c 100644 --- a/msm/sde/sde_encoder.c +++ b/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; diff --git a/msm/sde/sde_encoder.h b/msm/sde/sde_encoder.h index 338ff7823f..79f317bfe3 100644 --- a/msm/sde/sde_encoder.h +++ b/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 diff --git a/msm/sde/sde_encoder_phys.h b/msm/sde/sde_encoder_phys.h index ae0bb7da2a..a3952bd174 100644 --- a/msm/sde/sde_encoder_phys.h +++ b/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 diff --git a/msm/sde/sde_encoder_phys_vid.c b/msm/sde/sde_encoder_phys_vid.c index 441a04dea9..b14a2609f9 100644 --- a/msm/sde/sde_encoder_phys_vid.c +++ b/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; } diff --git a/msm/sde/sde_fence.c b/msm/sde/sde_fence.c index 47c57de315..b53984d7e2 100644 --- a/msm/sde/sde_fence.c +++ b/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); diff --git a/msm/sde/sde_fence.h b/msm/sde/sde_fence.h index 127c350fcc..6183463fd6 100644 --- a/msm/sde/sde_fence.h +++ b/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; } diff --git a/msm/sde/sde_plane.c b/msm/sde/sde_plane.c index 2dbe57eafe..bc088ce9fb 100644 --- a/msm/sde/sde_plane.c +++ b/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: diff --git a/msm/sde/sde_plane.h b/msm/sde/sde_plane.h index cc140812ae..43f0da9f7e 100644 --- a/msm/sde/sde_plane.h +++ b/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