diff --git a/msm/sde/sde_crtc.c b/msm/sde/sde_crtc.c index 64ef082e7f..a87ed19e80 100644 --- a/msm/sde/sde_crtc.c +++ b/msm/sde/sde_crtc.c @@ -2499,23 +2499,23 @@ u32 sde_crtc_get_dfps_maxfps(struct drm_crtc *crtc) return 0; } -static void sde_crtc_vblank_cb(void *data) +static void sde_crtc_vblank_cb(void *data, ktime_t ts) { struct drm_crtc *crtc = (struct drm_crtc *)data; struct sde_crtc *sde_crtc = to_sde_crtc(crtc); /* keep statistics on vblank callback - with auto reset via debugfs */ if (ktime_compare(sde_crtc->vblank_cb_time, ktime_set(0, 0)) == 0) - sde_crtc->vblank_cb_time = ktime_get(); + sde_crtc->vblank_cb_time = ts; else sde_crtc->vblank_cb_count++; - sde_crtc->vblank_last_cb_time = ktime_get(); + sde_crtc->vblank_last_cb_time = ts; sysfs_notify_dirent(sde_crtc->vsync_event_sf); drm_crtc_handle_vblank(crtc); - DRM_DEBUG_VBL("crtc%d\n", crtc->base.id); - SDE_EVT32_VERBOSE(DRMID(crtc)); + DRM_DEBUG_VBL("crtc%d, ts:%llu\n", crtc->base.id, ktime_to_us(ts)); + SDE_EVT32_VERBOSE(DRMID(crtc), ktime_to_us(ts)); } static void _sde_crtc_retire_event(struct drm_connector *connector, @@ -4400,6 +4400,8 @@ static void sde_crtc_enable(struct drm_crtc *crtc, if (!sde_crtc->enabled) { /* cache the encoder mask now for vblank work */ sde_crtc->cached_encoder_mask = crtc->state->encoder_mask; + /* max possible vsync_cnt(atomic_t) soft counter */ + drm_crtc_set_max_vblank_count(crtc, INT_MAX); drm_crtc_vblank_on(crtc); } @@ -5269,6 +5271,47 @@ int sde_crtc_vblank(struct drm_crtc *crtc, bool en) return 0; } +static u32 sde_crtc_get_vblank_counter(struct drm_crtc *crtc) +{ + struct drm_encoder *encoder; + struct sde_crtc *sde_crtc; + + if (!crtc) + return 0; + + sde_crtc = to_sde_crtc(crtc); + + drm_for_each_encoder_mask(encoder, crtc->dev, sde_crtc->cached_encoder_mask) { + if (sde_encoder_in_clone_mode(encoder)) + continue; + + return sde_encoder_get_frame_count(encoder); + } + + return 0; +} + +static bool sde_crtc_get_vblank_timestamp(struct drm_crtc *crtc, int *max_error, + ktime_t *tvblank, bool in_vblank_irq) +{ + struct drm_encoder *encoder; + struct sde_crtc *sde_crtc; + + if (!crtc) + return false; + + sde_crtc = to_sde_crtc(crtc); + + drm_for_each_encoder_mask(encoder, crtc->dev, sde_crtc->cached_encoder_mask) { + if (sde_encoder_in_clone_mode(encoder)) + continue; + + return sde_encoder_get_vblank_timestamp(encoder, tvblank); + } + + return false; +} + static void sde_crtc_install_dest_scale_properties(struct sde_crtc *sde_crtc, struct sde_mdss_cfg *catalog, struct sde_kms_info *info) { @@ -6519,6 +6562,23 @@ static const struct drm_crtc_funcs sde_crtc_funcs = { .early_unregister = sde_crtc_early_unregister, }; +static const struct drm_crtc_funcs sde_crtc_funcs_v1 = { + .set_config = drm_atomic_helper_set_config, + .destroy = sde_crtc_destroy, + .enable_vblank = sde_crtc_enable_vblank, + .disable_vblank = sde_crtc_disable_vblank, + .page_flip = drm_atomic_helper_page_flip, + .atomic_set_property = sde_crtc_atomic_set_property, + .atomic_get_property = sde_crtc_atomic_get_property, + .reset = sde_crtc_reset, + .atomic_duplicate_state = sde_crtc_duplicate_state, + .atomic_destroy_state = sde_crtc_destroy_state, + .late_register = sde_crtc_late_register, + .early_unregister = sde_crtc_early_unregister, + .get_vblank_timestamp = sde_crtc_get_vblank_timestamp, + .get_vblank_counter = sde_crtc_get_vblank_counter, +}; + static const struct drm_crtc_helper_funcs sde_crtc_helper_funcs = { .mode_fixup = sde_crtc_mode_fixup, .disable = sde_crtc_disable, @@ -6794,6 +6854,7 @@ struct drm_crtc *sde_crtc_init(struct drm_device *dev, struct drm_plane *plane) struct sde_crtc *sde_crtc = NULL; struct msm_drm_private *priv = NULL; struct sde_kms *kms = NULL; + const struct drm_crtc_funcs *crtc_funcs; int i, rc; priv = dev->dev_private; @@ -6833,9 +6894,8 @@ struct drm_crtc *sde_crtc_init(struct drm_device *dev, struct drm_plane *plane) sde_crtc_frame_event_work); } - drm_crtc_init_with_planes(dev, crtc, plane, NULL, &sde_crtc_funcs, - NULL); - + crtc_funcs = kms->catalog->has_precise_vsync_ts ? &sde_crtc_funcs_v1 : &sde_crtc_funcs; + drm_crtc_init_with_planes(dev, crtc, plane, NULL, crtc_funcs, NULL); drm_crtc_helper_add(crtc, &sde_crtc_helper_funcs); /* save user friendly CRTC name for later */ diff --git a/msm/sde/sde_encoder.c b/msm/sde/sde_encoder.c index 4c1e913db9..27cc33b046 100644 --- a/msm/sde/sde_encoder.c +++ b/msm/sde/sde_encoder.c @@ -3133,6 +3133,7 @@ static void sde_encoder_vblank_callback(struct drm_encoder *drm_enc, { struct sde_encoder_virt *sde_enc = NULL; unsigned long lock_flags; + ktime_t ts = 0; if (!drm_enc || !phy_enc) return; @@ -3140,16 +3141,26 @@ static void sde_encoder_vblank_callback(struct drm_encoder *drm_enc, SDE_ATRACE_BEGIN("encoder_vblank_callback"); sde_enc = to_sde_encoder_virt(drm_enc); + /* + * calculate accurate vsync timestamp when available + * set current time otherwise + */ + if (phy_enc->sde_kms && phy_enc->sde_kms->catalog->has_precise_vsync_ts) + ts = sde_encoder_calc_last_vsync_timestamp(drm_enc); + if (!ts) + ts = ktime_get(); + spin_lock_irqsave(&sde_enc->enc_spinlock, lock_flags); + phy_enc->last_vsync_timestamp = ts; + atomic_inc(&phy_enc->vsync_cnt); if (sde_enc->crtc_vblank_cb) - sde_enc->crtc_vblank_cb(sde_enc->crtc_vblank_cb_data); + sde_enc->crtc_vblank_cb(sde_enc->crtc_vblank_cb_data, ts); spin_unlock_irqrestore(&sde_enc->enc_spinlock, lock_flags); if (phy_enc->sde_kms && phy_enc->sde_kms->catalog->uidle_cfg.debugfs_perf) sde_encoder_perf_uidle_status(phy_enc->sde_kms, sde_enc->crtc); - atomic_inc(&phy_enc->vsync_cnt); SDE_ATRACE_END("encoder_vblank_callback"); } @@ -3178,7 +3189,7 @@ static void sde_encoder_underrun_callback(struct drm_encoder *drm_enc, } void sde_encoder_register_vblank_callback(struct drm_encoder *drm_enc, - void (*vbl_cb)(void *), void *vbl_data) + void (*vbl_cb)(void *, ktime_t), void *vbl_data) { struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc); unsigned long lock_flags; @@ -5209,6 +5220,43 @@ enum sde_intf_mode sde_encoder_get_intf_mode(struct drm_encoder *encoder) return INTF_MODE_NONE; } +u32 sde_encoder_get_frame_count(struct drm_encoder *encoder) +{ + struct sde_encoder_virt *sde_enc = NULL; + struct sde_encoder_phys *phys; + + if (!encoder) { + SDE_ERROR("invalid encoder\n"); + return 0; + } + sde_enc = to_sde_encoder_virt(encoder); + + phys = sde_enc->cur_master; + + return phys ? atomic_read(&phys->vsync_cnt) : 0; +} + +bool sde_encoder_get_vblank_timestamp(struct drm_encoder *encoder, + ktime_t *tvblank) +{ + struct sde_encoder_virt *sde_enc = NULL; + struct sde_encoder_phys *phys; + + if (!encoder) { + SDE_ERROR("invalid encoder\n"); + return false; + } + sde_enc = to_sde_encoder_virt(encoder); + + phys = sde_enc->cur_master; + if (!phys) + return false; + + *tvblank = phys->last_vsync_timestamp; + + return *tvblank ? true : false; +} + static void _sde_encoder_cache_hw_res_cont_splash( struct drm_encoder *encoder, struct sde_kms *sde_kms) diff --git a/msm/sde/sde_encoder.h b/msm/sde/sde_encoder.h index d82775267b..2051e5732e 100644 --- a/msm/sde/sde_encoder.h +++ b/msm/sde/sde_encoder.h @@ -220,7 +220,7 @@ struct sde_encoder_virt { bool intfs_swapped; bool qdss_status; - void (*crtc_vblank_cb)(void *data); + void (*crtc_vblank_cb)(void *data, ktime_t ts); void *crtc_vblank_cb_data; struct dentry *debugfs_root; @@ -290,7 +290,7 @@ void sde_encoder_early_wakeup(struct drm_encoder *drm_enc); * @data: user data provided to callback */ void sde_encoder_register_vblank_callback(struct drm_encoder *encoder, - void (*cb)(void *), void *data); + void (*cb)(void *, ktime_t), void *data); /** * sde_encoder_register_frame_event_callback - provide callback to encoder that @@ -386,6 +386,20 @@ u32 sde_encoder_get_fps(struct drm_encoder *encoder); */ enum sde_intf_mode sde_encoder_get_intf_mode(struct drm_encoder *encoder); +/* + * sde_encoder_get_frame_count - get hardware frame count of the given encoder + * @encoder: Pointer to drm encoder object + */ +u32 sde_encoder_get_frame_count(struct drm_encoder *encoder); + +/* + * sde_encoder_get_vblank_timestamp - get the last vsync timestamp + * @encoder: Pointer to drm encoder object + * @tvblank: vblank timestamp + */ +bool sde_encoder_get_vblank_timestamp(struct drm_encoder *encoder, + ktime_t *tvblank); + /** * sde_encoder_control_te - control enabling/disabling VSYNC_IN_EN * @encoder: encoder pointer diff --git a/msm/sde/sde_encoder_phys.h b/msm/sde/sde_encoder_phys.h index 8bff1bcfaf..093082a721 100644 --- a/msm/sde/sde_encoder_phys.h +++ b/msm/sde/sde_encoder_phys.h @@ -279,6 +279,7 @@ struct sde_encoder_irq { * @vblank_cached_refcount: Reference count of vblank cached request * @wbirq_refcount: Reference count of wb irq request * @vsync_cnt: Vsync count for the physical encoder + * @last_vsync_timestamp: store last vsync timestamp * @underrun_cnt: Underrun count for the physical encoder * @pending_kickoff_cnt: Atomic counter tracking the number of kickoffs * vs. the number of done/vblank irqs. Should hover @@ -329,6 +330,7 @@ struct sde_encoder_phys { atomic_t vblank_cached_refcount; atomic_t wbirq_refcount; atomic_t vsync_cnt; + ktime_t last_vsync_timestamp; atomic_t underrun_cnt; atomic_t pending_kickoff_cnt; atomic_t pending_retire_fence_cnt; diff --git a/msm/sde/sde_hw_catalog.h b/msm/sde/sde_hw_catalog.h index 5285ab9490..298c83679e 100644 --- a/msm/sde/sde_hw_catalog.h +++ b/msm/sde/sde_hw_catalog.h @@ -1511,6 +1511,7 @@ struct sde_perf_cfg { * @has_cursor indicates if hardware cursor is supported * @has_vig_p010 indicates if vig pipe supports p010 format * @has_fp16 indicates if FP16 format is supported on SSPP pipes + * @has_precise_vsync_ts indicates if HW has vsyc timestamp logging capability * @mdss_hw_block_size Max offset of MDSS_HW block (0 offset), used for debug * @inline_rot_formats formats supported by the inline rotator feature * @irq_offset_list list of sde_intr_irq_offsets to initialize irq table @@ -1592,6 +1593,7 @@ struct sde_mdss_cfg { bool has_cursor; bool has_vig_p010; bool has_fp16; + bool has_precise_vsync_ts; u32 mdss_hw_block_size; u32 mdss_count; struct sde_mdss_base_cfg mdss[MAX_BLOCKS]; diff --git a/msm/sde/sde_kms.c b/msm/sde/sde_kms.c index 069e52c4c6..914ab15323 100644 --- a/msm/sde/sde_kms.c +++ b/msm/sde/sde_kms.c @@ -4398,6 +4398,14 @@ static int _sde_kms_hw_init_blocks(struct sde_kms *sde_kms, goto perf_err; } + /* + * set the disable_immediate flag when driver supports the precise vsync + * timestamp as the DRM hooks for vblank timestamp/counters would be set + * based on the feature + */ + if (sde_kms->catalog->has_precise_vsync_ts) + dev->vblank_disable_immediate = true; + /* * _sde_kms_drm_obj_init should create the DRM related objects * i.e. CRTCs, planes, encoders, connectors and so forth