diff --git a/msm/sde/sde_encoder.c b/msm/sde/sde_encoder.c index db10fa059c..a1c9182ff8 100644 --- a/msm/sde/sde_encoder.c +++ b/msm/sde/sde_encoder.c @@ -1769,6 +1769,9 @@ static void _sde_encoder_update_vsync_source(struct sde_encoder_virt *sde_enc, else vsync_source = sde_enc->te_source; + SDE_EVT32(DRMID(&sde_enc->base), vsync_source, is_dummy, + disp_info->is_te_using_watchdog_timer); + for (i = 0; i < sde_enc->num_phys_encs; i++) { phys = sde_enc->phys_encs[i]; @@ -1825,7 +1828,8 @@ static void _sde_encoder_dsc_disable(struct sde_encoder_virt *sde_enc) */ } -static int _sde_encoder_switch_to_watchdog_vsync(struct drm_encoder *drm_enc) +int sde_encoder_helper_switch_vsync(struct drm_encoder *drm_enc, + bool watchdog_te) { struct sde_encoder_virt *sde_enc; struct msm_display_info disp_info; @@ -1840,7 +1844,7 @@ static int _sde_encoder_switch_to_watchdog_vsync(struct drm_encoder *drm_enc) sde_encoder_control_te(drm_enc, false); memcpy(&disp_info, &sde_enc->disp_info, sizeof(disp_info)); - disp_info.is_te_using_watchdog_timer = true; + disp_info.is_te_using_watchdog_timer = watchdog_te; _sde_encoder_update_vsync_source(sde_enc, &disp_info, false); sde_encoder_control_te(drm_enc, true); @@ -1901,7 +1905,7 @@ static int _sde_encoder_rsc_client_update_vsync_wait( * by generating the vsync from watchdog timer. */ if (crtc->base.id == wait_vblank_crtc_id) - _sde_encoder_switch_to_watchdog_vsync(drm_enc); + sde_encoder_helper_switch_vsync(drm_enc, true); } } @@ -5696,6 +5700,28 @@ int sde_encoder_wait_for_event(struct drm_encoder *drm_enc, return ret; } +void sde_encoder_helper_get_jitter_bounds_ns(struct drm_encoder *drm_enc, + u64 *l_bound, u64 *u_bound) +{ + struct sde_encoder_virt *sde_enc; + u64 jitter_ns, frametime_ns; + struct msm_mode_info *info; + + if (!drm_enc) { + SDE_ERROR("invalid encoder\n"); + return; + } + + sde_enc = to_sde_encoder_virt(drm_enc); + info = &sde_enc->mode_info; + + frametime_ns = (1 * 1000000000) / info->frame_rate; + jitter_ns = (info->jitter_numer * frametime_ns) / + (info->jitter_denom * 100); + *l_bound = frametime_ns - jitter_ns; + *u_bound = frametime_ns + jitter_ns; +} + u32 sde_encoder_get_fps(struct drm_encoder *drm_enc) { struct sde_encoder_virt *sde_enc; @@ -5993,12 +6019,12 @@ int sde_encoder_display_failure_notification(struct drm_encoder *enc, kthread_flush_work(&sde_enc->esd_trigger_work); } - /** + /* * panel may stop generating te signal (vsync) during esd failure. rsc * hardware may hang without vsync. Avoid rsc hang by generating the * vsync from watchdog timer instead of panel. */ - _sde_encoder_switch_to_watchdog_vsync(enc); + sde_encoder_helper_switch_vsync(enc, true); if (!skip_pre_kickoff) sde_encoder_wait_for_event(enc, MSM_ENC_TX_COMPLETE); diff --git a/msm/sde/sde_encoder_phys.h b/msm/sde/sde_encoder_phys.h index db85d0d7c8..f620ba7bac 100644 --- a/msm/sde/sde_encoder_phys.h +++ b/msm/sde/sde_encoder_phys.h @@ -25,6 +25,7 @@ #define KICKOFF_TIMEOUT_MS 84 #define KICKOFF_TIMEOUT_JIFFIES msecs_to_jiffies(KICKOFF_TIMEOUT_MS) +#define MAX_TE_PROFILE_COUNT 5 /** * enum sde_enc_split_role - Role this physical encoder will play in a * split-panel configuration, where one panel is master, and others slaves. @@ -361,6 +362,17 @@ struct sde_encoder_phys_cmd_autorefresh { wait_queue_head_t kickoff_wq; }; +/** + * struct sde_encoder_phys_cmd_te_timestamp - list node to keep track of + * rd_ptr/TE timestamp + * @list: list node + * @timestamp: TE timestamp + */ +struct sde_encoder_phys_cmd_te_timestamp { + struct list_head list; + ktime_t timestamp; +}; + /** * struct sde_encoder_phys_cmd - sub-class of sde_encoder_phys to handle command * mode specific operations @@ -371,6 +383,8 @@ struct sde_encoder_phys_cmd_autorefresh { * @pending_vblank_cnt: Atomic counter tracking pending wait for VBLANK * @pending_vblank_wq: Wait queue for blocking until VBLANK received * @wr_ptr_wait_success: log wr_ptr_wait success for release fence trigger + * @te_timestamp_list: List head for the TE timestamp list + * @te_timestamp: Array of size MAX_TE_PROFILE_COUNT te_timestamp_list elements */ struct sde_encoder_phys_cmd { struct sde_encoder_phys base; @@ -380,6 +394,9 @@ struct sde_encoder_phys_cmd { atomic_t pending_vblank_cnt; wait_queue_head_t pending_vblank_wq; bool wr_ptr_wait_success; + struct list_head te_timestamp_list; + struct sde_encoder_phys_cmd_te_timestamp + te_timestamp[MAX_TE_PROFILE_COUNT]; }; /** @@ -558,6 +575,21 @@ int sde_encoder_helper_wait_event_timeout( int32_t hw_id, struct sde_encoder_wait_info *info); +/* + * sde_encoder_get_fps - get the allowed panel jitter in nanoseconds + * @encoder: Pointer to drm encoder object + */ +void sde_encoder_helper_get_jitter_bounds_ns(struct drm_encoder *encoder, + u64 *l_bound, u64 *u_bound); + +/** + * sde_encoder_helper_switch_vsync - switch vsync source to WD or default + * @drm_enc: Pointer to drm encoder structure + * @watchdog_te: switch vsync source to watchdog TE + */ +int sde_encoder_helper_switch_vsync(struct drm_encoder *drm_enc, + bool watchdog_te); + /** * sde_encoder_helper_hw_reset - issue ctl hw reset * This helper function may be optionally specified by physical diff --git a/msm/sde/sde_encoder_phys_cmd.c b/msm/sde/sde_encoder_phys_cmd.c index c2becbef81..3294137d4b 100644 --- a/msm/sde/sde_encoder_phys_cmd.c +++ b/msm/sde/sde_encoder_phys_cmd.c @@ -230,6 +230,8 @@ static void sde_encoder_phys_cmd_te_rd_ptr_irq(void *arg, int irq_idx) u32 scheduler_status = INVALID_CTL_STATUS; struct sde_hw_ctl *ctl; struct sde_hw_pp_vsync_info info[MAX_CHANNELS_PER_ENC] = {{0}}; + struct sde_encoder_phys_cmd_te_timestamp *te_timestamp; + unsigned long lock_flags; if (!phys_enc || !phys_enc->hw_pp || !phys_enc->hw_intf) return; @@ -241,6 +243,16 @@ static void sde_encoder_phys_cmd_te_rd_ptr_irq(void *arg, int irq_idx) if (ctl && ctl->ops.get_scheduler_status) scheduler_status = ctl->ops.get_scheduler_status(ctl); + spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags); + te_timestamp = list_first_entry_or_null(&cmd_enc->te_timestamp_list, + struct sde_encoder_phys_cmd_te_timestamp, list); + if (te_timestamp) { + list_del_init(&te_timestamp->list); + te_timestamp->timestamp = ktime_get(); + list_add_tail(&te_timestamp->list, &cmd_enc->te_timestamp_list); + } + spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags); + sde_encoder_helper_get_pp_line_count(phys_enc->parent, info); SDE_EVT32_IRQ(DRMID(phys_enc->parent), info[0].pp_idx, info[0].intf_idx, @@ -1337,11 +1349,54 @@ static int sde_encoder_phys_cmd_prepare_for_kickoff( return ret; } +static bool _sde_encoder_phys_cmd_needs_vsync_change( + struct sde_encoder_phys *phys_enc, ktime_t profile_timestamp) +{ + struct sde_encoder_phys_cmd *cmd_enc; + struct sde_encoder_phys_cmd_te_timestamp *cur; + struct sde_encoder_phys_cmd_te_timestamp *prev = NULL; + ktime_t time_diff; + u64 l_bound = 0, u_bound = 0; + bool ret = false; + unsigned long lock_flags; + + cmd_enc = to_sde_encoder_phys_cmd(phys_enc); + sde_encoder_helper_get_jitter_bounds_ns(phys_enc->parent, + &l_bound, &u_bound); + if (!l_bound || !u_bound) { + SDE_ERROR_CMDENC(cmd_enc, "invalid vsync jitter bounds\n"); + return false; + } + + spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags); + list_for_each_entry_reverse(cur, &cmd_enc->te_timestamp_list, list) { + if (prev && ktime_after(cur->timestamp, profile_timestamp)) { + time_diff = ktime_sub(prev->timestamp, cur->timestamp); + if ((time_diff < l_bound) || (time_diff > u_bound)) { + ret = true; + break; + } + } + prev = cur; + } + spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags); + + if (ret) { + SDE_DEBUG_CMDENC(cmd_enc, + "time_diff:%llu, prev:%llu, cur:%llu, jitter:%llu/%llu\n", + time_diff, prev->timestamp, cur->timestamp, + l_bound, u_bound); + SDE_EVT32(DRMID(phys_enc->parent), + (u32) (l_bound / 1000), (u32) (u_bound / 1000), + (u32) (time_diff / 1000), SDE_EVTLOG_ERROR); + } + + return ret; +} + static int _sde_encoder_phys_cmd_wait_for_wr_ptr( struct sde_encoder_phys *phys_enc) { - struct sde_encoder_phys_cmd *cmd_enc = - to_sde_encoder_phys_cmd(phys_enc); struct sde_encoder_wait_info wait_info = {0}; int ret; bool frame_pending = true; @@ -1369,29 +1424,9 @@ static int _sde_encoder_phys_cmd_wait_for_wr_ptr( if (ctl && ctl->ops.get_start_state) frame_pending = ctl->ops.get_start_state(ctl); - if (frame_pending) - SDE_ERROR_CMDENC(cmd_enc, - "wr_ptrt start interrupt wait failed\n"); - else - ret = 0; - - /* - * Signaling the retire fence at wr_ptr timeout - * to allow the next commit and avoid device freeze. - * As wr_ptr timeout can occurs due to no read ptr, - * updating pending_rd_ptr_cnt here may not cover all - * cases. Hence signaling the retire fence. - */ - if (sde_encoder_phys_cmd_is_master(phys_enc) && - atomic_add_unless(&phys_enc->pending_retire_fence_cnt, - -1, 0)) - phys_enc->parent_ops.handle_frame_done( - phys_enc->parent, phys_enc, - SDE_ENCODER_FRAME_EVENT_SIGNAL_RETIRE_FENCE); + ret = frame_pending ? ret : 0; } - cmd_enc->wr_ptr_wait_success = (ret == 0) ? true : false; - return ret; } @@ -1416,11 +1451,60 @@ static int sde_encoder_phys_cmd_wait_for_tx_complete( return rc; } +static int _sde_encoder_phys_cmd_handle_wr_ptr_timeout( + struct sde_encoder_phys *phys_enc, + ktime_t profile_timestamp) +{ + struct sde_encoder_phys_cmd *cmd_enc = + to_sde_encoder_phys_cmd(phys_enc); + bool switch_te; + int ret = -ETIMEDOUT; + + switch_te = _sde_encoder_phys_cmd_needs_vsync_change( + phys_enc, profile_timestamp); + + SDE_EVT32(DRMID(phys_enc->parent), switch_te, SDE_EVTLOG_FUNC_ENTRY); + + if (switch_te) { + SDE_DEBUG_CMDENC(cmd_enc, + "wr_ptr_irq wait failed, retry with WD TE\n"); + + /* switch to watchdog TE and wait again */ + sde_encoder_helper_switch_vsync(phys_enc->parent, true); + + ret = _sde_encoder_phys_cmd_wait_for_wr_ptr(phys_enc); + + /* switch back to default TE */ + sde_encoder_helper_switch_vsync(phys_enc->parent, false); + } + + /* + * Signaling the retire fence at wr_ptr timeout + * to allow the next commit and avoid device freeze. + */ + if (ret == -ETIMEDOUT) { + SDE_ERROR_CMDENC(cmd_enc, + "wr_ptr_irq wait failed, switch_te:%d\n", switch_te); + SDE_EVT32(DRMID(phys_enc->parent), switch_te, SDE_EVTLOG_ERROR); + + if (sde_encoder_phys_cmd_is_master(phys_enc) && + atomic_add_unless(&phys_enc->pending_retire_fence_cnt, -1, 0)) + phys_enc->parent_ops.handle_frame_done( + phys_enc->parent, phys_enc, + SDE_ENCODER_FRAME_EVENT_SIGNAL_RETIRE_FENCE); + } + + cmd_enc->wr_ptr_wait_success = (ret == 0) ? true : false; + + return ret; +} + static int sde_encoder_phys_cmd_wait_for_commit_done( struct sde_encoder_phys *phys_enc) { int rc = 0, i, pending_cnt; struct sde_encoder_phys_cmd *cmd_enc; + ktime_t profile_timestamp = ktime_get(); if (!phys_enc) return -EINVAL; @@ -1430,8 +1514,18 @@ static int sde_encoder_phys_cmd_wait_for_commit_done( /* only required for master controller */ if (sde_encoder_phys_cmd_is_master(phys_enc)) { rc = _sde_encoder_phys_cmd_wait_for_wr_ptr(phys_enc); - if (rc == -ETIMEDOUT) - goto wait_for_idle; + if (rc == -ETIMEDOUT) { + /* + * Profile all the TE received after profile_timestamp + * and if the jitter is more, switch to watchdog TE + * and wait for wr_ptr again. Finally move back to + * default TE. + */ + rc = _sde_encoder_phys_cmd_handle_wr_ptr_timeout( + phys_enc, profile_timestamp); + if (rc == -ETIMEDOUT) + goto wait_for_idle; + } if (cmd_enc->autorefresh.cfg.enable) rc = _sde_encoder_phys_cmd_wait_for_autorefresh_done( @@ -1753,6 +1847,10 @@ struct sde_encoder_phys *sde_encoder_phys_cmd_init( init_waitqueue_head(&cmd_enc->pending_vblank_wq); atomic_set(&cmd_enc->autorefresh.kickoff_cnt, 0); init_waitqueue_head(&cmd_enc->autorefresh.kickoff_wq); + INIT_LIST_HEAD(&cmd_enc->te_timestamp_list); + for (i = 0; i < MAX_TE_PROFILE_COUNT; i++) + list_add(&cmd_enc->te_timestamp[i].list, + &cmd_enc->te_timestamp_list); SDE_DEBUG_CMDENC(cmd_enc, "created\n");