diff --git a/msm/sde/sde_crtc.c b/msm/sde/sde_crtc.c index 35645ea2d2..d91242b85a 100644 --- a/msm/sde/sde_crtc.c +++ b/msm/sde/sde_crtc.c @@ -5040,18 +5040,8 @@ static void sde_crtc_handle_power_event(u32 event_type, void *arg) break; case SDE_POWER_EVENT_PRE_DISABLE: drm_for_each_encoder_mask(encoder, crtc->dev, - crtc->state->encoder_mask) { - /* - * disable the vsync source after updating the - * rsc state. rsc state update might have vsync wait - * and vsync source must be disabled after it. - * It will avoid generating any vsync from this point - * till mode-2 entry. It is SW workaround for HW - * limitation and should not be removed without - * checking the updated design. - */ - sde_encoder_control_te(encoder, false); - } + crtc->state->encoder_mask) + sde_encoder_idle_pc_enter(encoder); spin_lock_irqsave(&sde_crtc->spin_lock, flags); node = NULL; diff --git a/msm/sde/sde_encoder.c b/msm/sde/sde_encoder.c index 16653db9bc..8e91b302c7 100644 --- a/msm/sde/sde_encoder.c +++ b/msm/sde/sde_encoder.c @@ -1453,6 +1453,29 @@ static void _sde_encoder_update_vsync_source(struct sde_encoder_virt *sde_enc, } } +static void sde_encoder_control_te(struct drm_encoder *drm_enc, bool enable) +{ + struct sde_encoder_virt *sde_enc; + struct sde_encoder_phys *phys; + int i; + + if (!drm_enc) { + SDE_ERROR("invalid parameters\n"); + return; + } + + sde_enc = to_sde_encoder_virt(drm_enc); + if (!sde_enc) { + SDE_ERROR("invalid sde encoder\n"); + return; + } + + for (i = 0; i < sde_enc->num_phys_encs; i++) { + phys = sde_enc->phys_encs[i]; + if (phys && phys->ops.control_te) + phys->ops.control_te(phys, enable); + } +} int sde_encoder_helper_switch_vsync(struct drm_encoder *drm_enc, @@ -2728,28 +2751,28 @@ static void sde_encoder_virt_mode_set(struct drm_encoder *drm_enc, sde_encoder_virt_modeset_rc(drm_enc, adj_mode, msm_mode, false); } -void sde_encoder_control_te(struct drm_encoder *drm_enc, bool enable) +void sde_encoder_idle_pc_enter(struct drm_encoder *drm_enc) { - struct sde_encoder_virt *sde_enc; - struct sde_encoder_phys *phys; - int i; + struct sde_encoder_virt *sde_enc = NULL; if (!drm_enc) { - SDE_ERROR("invalid parameters\n"); + SDE_ERROR("invalid encoder\n"); return; } + /* + * disable the vsync source after updating the + * rsc state. rsc state update might have vsync wait + * and vsync source must be disabled after it. + * It will avoid generating any vsync from this point + * till mode-2 entry. It is SW workaround for HW + * limitation and should not be removed without + * checking the updated design. + */ + sde_encoder_control_te(drm_enc, false); sde_enc = to_sde_encoder_virt(drm_enc); - if (!sde_enc) { - SDE_ERROR("invalid sde encoder\n"); - return; - } - - for (i = 0; i < sde_enc->num_phys_encs; i++) { - phys = sde_enc->phys_encs[i]; - if (phys && phys->ops.control_te) - phys->ops.control_te(phys, enable); - } + if (sde_enc->cur_master && sde_enc->cur_master->ops.idle_pc_cache_display_status) + sde_enc->cur_master->ops.idle_pc_cache_display_status(sde_enc->cur_master); } static int _sde_encoder_input_connect(struct input_handler *handler, diff --git a/msm/sde/sde_encoder.h b/msm/sde/sde_encoder.h index c19f888120..9a1c9e2f15 100644 --- a/msm/sde/sde_encoder.h +++ b/msm/sde/sde_encoder.h @@ -396,11 +396,10 @@ bool sde_encoder_get_vblank_timestamp(struct drm_encoder *encoder, ktime_t *tvblank); /** - * sde_encoder_control_te - control enabling/disabling VSYNC_IN_EN + * sde_encoder_idle_pc_enter - control enable/disable VSYNC_IN_EN & cache display status at ipc * @encoder: encoder pointer - * @enable: boolean to indicate enable/disable */ -void sde_encoder_control_te(struct drm_encoder *encoder, bool enable); +void sde_encoder_idle_pc_enter(struct drm_encoder *encoder); /** * sde_encoder_virt_restore - restore the encoder configs diff --git a/msm/sde/sde_encoder_phys.h b/msm/sde/sde_encoder_phys.h index a48650cc34..e58df1cbc4 100644 --- a/msm/sde/sde_encoder_phys.h +++ b/msm/sde/sde_encoder_phys.h @@ -140,6 +140,7 @@ struct sde_encoder_virt_ops { * count and underrun line count * @add_to_minidump: Add this phys_enc data to minidumps * @disable_autorefresh: Disable autorefresh + * @idle_pc_cache_display_status: caches display status at idle power collapse */ struct sde_encoder_phys_ops { @@ -194,6 +195,7 @@ struct sde_encoder_phys_ops { u32 (*get_underrun_line_count)(struct sde_encoder_phys *phys); void (*add_to_minidump)(struct sde_encoder_phys *phys); void (*disable_autorefresh)(struct sde_encoder_phys *phys); + void (*idle_pc_cache_display_status)(struct sde_encoder_phys *phys); }; /** @@ -275,6 +277,7 @@ struct sde_encoder_irq { * @hw_dnsc_blur: Hardware interface to the downscale blur registers * @sde_kms: Pointer to the sde_kms top level * @cached_mode: DRM mode cached at mode_set time, acted on in enable + * @wd_jitter : Pointer to watchdog jitter prams * @enabled: Whether the encoder has enabled and running a mode * @split_role: Role to play in a split-panel configuration * @intf_mode: Interface mode @@ -333,6 +336,7 @@ struct sde_encoder_phys { struct sde_hw_dnsc_blur *hw_dnsc_blur; struct sde_kms *sde_kms; struct drm_display_mode cached_mode; + struct intf_wd_jitter_params wd_jitter; enum sde_enc_split_role split_role; enum sde_intf_mode intf_mode; enum sde_intf intf_idx; diff --git a/msm/sde/sde_encoder_phys_cmd.c b/msm/sde/sde_encoder_phys_cmd.c index 378b59fcad..106a928abf 100644 --- a/msm/sde/sde_encoder_phys_cmd.c +++ b/msm/sde/sde_encoder_phys_cmd.c @@ -2037,30 +2037,42 @@ static void sde_encoder_phys_cmd_trigger_start( cmd_enc->wr_ptr_wait_success = false; } -static void _sde_encoder_phys_cmd_calculate_wd_params(struct sde_encoder_phys *phys_enc, - struct intf_wd_jitter_params *wd_jitter) +static void _sde_encoder_phys_cmd_calculate_wd_params(struct sde_encoder_phys *phys_enc) { u32 nominal_te_value; struct sde_encoder_virt *sde_enc; struct msm_mode_info *mode_info; const u32 multiplier = 1 << 10; + struct intf_wd_jitter_params wd_jtr; sde_enc = to_sde_encoder_virt(phys_enc->parent); mode_info = &sde_enc->mode_info; - if (mode_info->wd_jitter.jitter_type & MSM_DISPLAY_WD_INSTANTANEOUS_JITTER) - wd_jitter->jitter = mult_frac(multiplier, mode_info->wd_jitter.inst_jitter_numer, + if (mode_info->wd_jitter.jitter_type & MSM_DISPLAY_WD_INSTANTANEOUS_JITTER) { + wd_jtr.jitter = mult_frac(multiplier, + mode_info->wd_jitter.inst_jitter_numer, (mode_info->wd_jitter.inst_jitter_denom * 100)); + phys_enc->wd_jitter.jitter = wd_jtr.jitter; + } if (mode_info->wd_jitter.jitter_type & MSM_DISPLAY_WD_LTJ_JITTER) { nominal_te_value = CALCULATE_WD_LOAD_VALUE(mode_info->frame_rate) * MDP_TICK_COUNT; - wd_jitter->ltj_max = mult_frac(nominal_te_value, mode_info->wd_jitter.ltj_max_numer, + wd_jtr.ltj_max = mult_frac(nominal_te_value, + mode_info->wd_jitter.ltj_max_numer, (mode_info->wd_jitter.ltj_max_denom) * 100); - wd_jitter->ltj_slope = mult_frac((1 << 16), wd_jitter->ltj_max, + wd_jtr.ltj_slope = mult_frac((1 << 16), wd_jtr.ltj_max, (mode_info->wd_jitter.ltj_time_sec * mode_info->frame_rate)); + phys_enc->wd_jitter.ltj_max = wd_jtr.ltj_max; + phys_enc->wd_jitter.ltj_slope = wd_jtr.ltj_slope; } - phys_enc->hw_intf->ops.configure_wd_jitter(phys_enc->hw_intf, wd_jitter); + phys_enc->hw_intf->ops.configure_wd_jitter(phys_enc->hw_intf, &phys_enc->wd_jitter); +} + +static void sde_encoder_phys_cmd_store_ltj_values(struct sde_encoder_phys *phys_enc) +{ + if (phys_enc && phys_enc->hw_intf->ops.get_wd_ltj_status) + phys_enc->hw_intf->ops.get_wd_ltj_status(phys_enc->hw_intf, &phys_enc->wd_jitter); } static void sde_encoder_phys_cmd_setup_vsync_source(struct sde_encoder_phys *phys_enc, @@ -2068,7 +2080,6 @@ static void sde_encoder_phys_cmd_setup_vsync_source(struct sde_encoder_phys *phy { struct sde_encoder_virt *sde_enc; struct sde_connector *sde_conn; - struct intf_wd_jitter_params wd_jitter = {0, 0}; if (!phys_enc || !phys_enc->hw_intf) return; @@ -2083,7 +2094,7 @@ static void sde_encoder_phys_cmd_setup_vsync_source(struct sde_encoder_phys *phy phys_enc->hw_intf->ops.setup_vsync_source) { vsync_source = SDE_VSYNC_SOURCE_WD_TIMER_0; if (phys_enc->hw_intf->ops.configure_wd_jitter) - _sde_encoder_phys_cmd_calculate_wd_params(phys_enc, &wd_jitter); + _sde_encoder_phys_cmd_calculate_wd_params(phys_enc); phys_enc->hw_intf->ops.setup_vsync_source(phys_enc->hw_intf, sde_enc->mode_info.frame_rate); } else { @@ -2136,6 +2147,7 @@ static void sde_encoder_phys_cmd_init_ops(struct sde_encoder_phys_ops *ops) ops->collect_misr = sde_encoder_helper_collect_misr; ops->add_to_minidump = sde_encoder_phys_cmd_add_enc_to_minidump; ops->disable_autorefresh = _sde_encoder_phys_disable_autorefresh; + ops->idle_pc_cache_display_status = sde_encoder_phys_cmd_store_ltj_values; } static inline bool sde_encoder_phys_cmd_intf_te_supported( diff --git a/msm/sde/sde_hw_catalog.c b/msm/sde/sde_hw_catalog.c index e66e682231..ef0b0e8af7 100644 --- a/msm/sde/sde_hw_catalog.c +++ b/msm/sde/sde_hw_catalog.c @@ -2387,6 +2387,7 @@ static int sde_intf_parse_dt(struct device_node *np, set_bit(SDE_INTF_MDP_VSYNC_FC, &intf->features); set_bit(SDE_INTF_TE_32BIT, &intf->features); set_bit(SDE_INTF_TE_SINGLE_UPDATE, &intf->features); + set_bit(SDE_INTF_WD_LTJ_CTL, &intf->features); } } diff --git a/msm/sde/sde_hw_catalog.h b/msm/sde/sde_hw_catalog.h index 216372cd7c..e2863555c3 100644 --- a/msm/sde/sde_hw_catalog.h +++ b/msm/sde/sde_hw_catalog.h @@ -596,6 +596,7 @@ enum { * @SDE_INTF_MDP_VSYNC_FC INTF block has mdp vsync frame counter * @SDE_INTF_AVR_STATUS INTF block has AVR_STATUS field in AVR_CONTROL register * @SDE_INTF_WD_JITTER INTF block has WD timer jitter support + * @SDE_INTF_WD_LTJ_CTL INTF block has WD long term jitter control support * @SDE_INTF_MAX */ enum { @@ -612,6 +613,7 @@ enum { SDE_INTF_MDP_VSYNC_FC, SDE_INTF_AVR_STATUS, SDE_INTF_WD_JITTER, + SDE_INTF_WD_LTJ_CTL, SDE_INTF_MAX }; diff --git a/msm/sde/sde_hw_intf.c b/msm/sde/sde_hw_intf.c index bb2ab03ba2..e2bb33de3a 100644 --- a/msm/sde/sde_hw_intf.c +++ b/msm/sde/sde_hw_intf.c @@ -65,6 +65,8 @@ #define INTF_MISR_CTRL 0x180 #define INTF_MISR_SIGNATURE 0x184 +#define INTF_WD_TIMER_0_LTJ_CTL 0x200 +#define INTF_WD_TIMER_0_LTJ_CTL1 0x204 #define INTF_VSYNC_TIMESTAMP_CTRL 0x210 #define INTF_VSYNC_TIMESTAMP0 0x214 #define INTF_VSYNC_TIMESTAMP1 0x218 @@ -76,6 +78,8 @@ #define INTF_WD_TIMER_0_CTL 0x230 #define INTF_WD_TIMER_0_CTL2 0x234 #define INTF_WD_TIMER_0_LOAD_VALUE 0x238 +#define INTF_WD_TIMER_0_LTJ_INT_STATUS 0x240 +#define INTF_WD_TIMER_0_LTJ_FRAC_STATUS 0x244 #define INTF_MUX 0x25C #define INTF_UNDERRUN_COUNT 0x268 #define INTF_STATUS 0x26C @@ -496,6 +500,40 @@ static void sde_hw_intf_configure_wd_timer_jitter(struct sde_hw_intf *intf, if (wd_jitter->ltj_max) reg |= BIT(30); SDE_REG_WRITE(c, INTF_WD_TIMER_0_JITTER_CTL, reg); + + if (intf->cap->features & BIT(SDE_INTF_WD_LTJ_CTL)) { + if (wd_jitter->ltj_step_dir && wd_jitter->ltj_initial_val) { + reg = ((wd_jitter->ltj_step_dir & 0x1) << 31) | + (wd_jitter->ltj_initial_val & 0x1FFFFF); + SDE_REG_WRITE(c, INTF_WD_TIMER_0_LTJ_CTL, reg); + wd_jitter->ltj_step_dir = 0; + wd_jitter->ltj_initial_val = 0; + } + + if (wd_jitter->ltj_fractional_val) { + SDE_REG_WRITE(c, INTF_WD_TIMER_0_LTJ_CTL1, wd_jitter->ltj_fractional_val); + wd_jitter->ltj_fractional_val = 0; + } + } + +} + +static void sde_hw_intf_read_wd_ltj_ctl(struct sde_hw_intf *intf, + struct intf_wd_jitter_params *wd_jitter) +{ + struct sde_hw_blk_reg_map *c; + u32 reg; + + c = &intf->hw; + + if (intf->cap->features & BIT(SDE_INTF_WD_LTJ_CTL)) { + reg = SDE_REG_READ(c, INTF_WD_TIMER_0_LTJ_INT_STATUS); + wd_jitter->ltj_step_dir = reg & BIT(31); + wd_jitter->ltj_initial_val = (reg & 0x1FFFFF); + + reg = SDE_REG_READ(c, INTF_WD_TIMER_0_LTJ_FRAC_STATUS); + wd_jitter->ltj_fractional_val = (reg & 0xFFFF); + } } static void sde_hw_intf_setup_vsync_source(struct sde_hw_intf *intf, u32 frame_rate) @@ -1039,6 +1077,9 @@ static void _setup_intf_ops(struct sde_hw_intf_ops *ops, if (cap & BIT(SDE_INTF_WD_JITTER)) ops->configure_wd_jitter = sde_hw_intf_configure_wd_timer_jitter; + + if (cap & BIT(SDE_INTF_WD_LTJ_CTL)) + ops->get_wd_ltj_status = sde_hw_intf_read_wd_ltj_ctl; } struct sde_hw_blk_reg_map *sde_hw_intf_init(enum sde_intf idx, diff --git a/msm/sde/sde_hw_intf.h b/msm/sde/sde_hw_intf.h index 698832126e..828351128d 100644 --- a/msm/sde/sde_hw_intf.h +++ b/msm/sde/sde_hw_intf.h @@ -73,13 +73,18 @@ struct intf_avr_params { * jitter : max instantaneous jitter. * ltj_max : max long term jitter value. * ltj_slope : slope of long term jitter. + *ltj_step_dir: direction of the step in LTJ + *ltj_initial_val: LTJ initial value + *ltj_fractional_val: LTJ fractional initial value */ struct intf_wd_jitter_params { u32 jitter; u32 ltj_max; u32 ltj_slope; + u8 ltj_step_dir; + u32 ltj_initial_val; + u32 ltj_fractional_val; }; - /** * struct sde_hw_intf_ops : Interface to the interface Hw driver functions * Assumption is these functions will be called after clocks are enabled @@ -95,6 +100,8 @@ struct intf_wd_jitter_params { * converts it into line count * @setup_vsync_source: Configure vsync source selection for intf * @configure_wd_jitter: Configure WD jitter. + * @ write_wd_ltj: Write WD long term jitter. + * @get_wd_ltj_status: Read WD long term jitter status. * @bind_pingpong_blk: enable/disable the connection with pingpong which will * feed pixels to this interface */ @@ -132,6 +139,10 @@ struct sde_hw_intf_ops { void (*setup_vsync_source)(struct sde_hw_intf *intf, u32 frame_rate); void (*configure_wd_jitter)(struct sde_hw_intf *intf, struct intf_wd_jitter_params *wd_jitter); + void (*write_wd_ltj)(struct sde_hw_intf *intf, + struct intf_wd_jitter_params *wd_jitter); + void (*get_wd_ltj_status)(struct sde_hw_intf *intf, + struct intf_wd_jitter_params *wd_jitter); void (*bind_pingpong_blk)(struct sde_hw_intf *intf, bool enable,