From f28d9e0a6a766c90f0fd221cad7a5ca97ebaf249 Mon Sep 17 00:00:00 2001 From: Shamika Joshi Date: Thu, 6 Oct 2022 15:49:40 -0700 Subject: [PATCH 1/2] disp: msm: sde: add support for INTF WD long term jitter restore from ipc Change adds support for storing the INTF watchdog timer long term jitter curve state. The state before collapse is stored in wd_jitter and restore back during power restore. Change-Id: Id83b5cc754daea89d7844ab67b38e12199525ff8 Signed-off-by: Shamika Joshi --- msm/sde/sde_crtc.c | 14 ++------- msm/sde/sde_encoder.c | 53 ++++++++++++++++++++++++---------- msm/sde/sde_encoder.h | 5 ++-- msm/sde/sde_encoder_phys.h | 4 +++ msm/sde/sde_encoder_phys_cmd.c | 30 +++++++++++++------ msm/sde/sde_hw_catalog.c | 1 + msm/sde/sde_hw_catalog.h | 2 ++ msm/sde/sde_hw_intf.c | 41 ++++++++++++++++++++++++++ msm/sde/sde_hw_intf.h | 13 ++++++++- 9 files changed, 123 insertions(+), 40 deletions(-) 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, From 4cc3c88474ac58832fca3307ae3ec99416fea57c Mon Sep 17 00:00:00 2001 From: Shamika Joshi Date: Tue, 18 Oct 2022 14:08:19 -0700 Subject: [PATCH 2/2] disp: msm: sde: pass sde_enc to sde_encoder_control_te Pass sde_enc to sde_encoder_control_te instead of drm_enc, as all callers have access to sde_enc. Change-Id: Ic61b78c9e8d1ab2ed6e371c19a72367efbb6e5ee Signed-off-by: Shamika Joshi --- msm/sde/sde_encoder.c | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/msm/sde/sde_encoder.c b/msm/sde/sde_encoder.c index 8e91b302c7..17dbf60dc7 100644 --- a/msm/sde/sde_encoder.c +++ b/msm/sde/sde_encoder.c @@ -1453,18 +1453,11 @@ 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) +static void sde_encoder_control_te(struct sde_encoder_virt *sde_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; @@ -1491,13 +1484,13 @@ int sde_encoder_helper_switch_vsync(struct drm_encoder *drm_enc, sde_enc = to_sde_encoder_virt(drm_enc); - sde_encoder_control_te(drm_enc, false); + sde_encoder_control_te(sde_enc, false); memcpy(&disp_info, &sde_enc->disp_info, sizeof(disp_info)); disp_info.is_te_using_watchdog_timer = watchdog_te; _sde_encoder_update_vsync_source(sde_enc, &disp_info); - sde_encoder_control_te(drm_enc, true); + sde_encoder_control_te(sde_enc, true); return 0; } @@ -2206,9 +2199,9 @@ static int _sde_encoder_rc_post_modeset(struct drm_encoder *drm_enc, /* toggle te bit to update vsync source for sim cmd mode panels */ if (sde_encoder_check_curr_mode(&sde_enc->base, MSM_DISPLAY_CMD_MODE) && sde_enc->disp_info.is_te_using_watchdog_timer) { - sde_encoder_control_te(drm_enc, false); + sde_encoder_control_te(sde_enc, false); _sde_encoder_update_vsync_source(sde_enc, &sde_enc->disp_info); - sde_encoder_control_te(drm_enc, true); + sde_encoder_control_te(sde_enc, true); } _sde_encoder_update_rsc_client(drm_enc, true); @@ -2759,6 +2752,7 @@ void sde_encoder_idle_pc_enter(struct drm_encoder *drm_enc) SDE_ERROR("invalid encoder\n"); return; } + sde_enc = to_sde_encoder_virt(drm_enc); /* * disable the vsync source after updating the * rsc state. rsc state update might have vsync wait @@ -2768,9 +2762,8 @@ void sde_encoder_idle_pc_enter(struct drm_encoder *drm_enc) * limitation and should not be removed without * checking the updated design. */ - sde_encoder_control_te(drm_enc, false); + sde_encoder_control_te(sde_enc, false); - sde_enc = to_sde_encoder_virt(drm_enc); 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); } @@ -3045,7 +3038,7 @@ void sde_encoder_virt_restore(struct drm_encoder *drm_enc) sde_enc->cur_master->ops.restore(sde_enc->cur_master); _sde_encoder_virt_enable_helper(drm_enc); - sde_encoder_control_te(drm_enc, true); + sde_encoder_control_te(sde_enc, true); /* * During IPC misr ctl register is reset. @@ -3203,11 +3196,11 @@ static void sde_encoder_virt_enable(struct drm_encoder *drm_enc) sizeof(sde_enc->cur_master->intf_cfg_v1)); /* turn off vsync_in to update tear check configuration */ - sde_encoder_control_te(drm_enc, false); + sde_encoder_control_te(sde_enc, false); sde_encoder_populate_encoder_phys(drm_enc, sde_enc, msm_mode); _sde_encoder_virt_enable_helper(drm_enc); - sde_encoder_control_te(drm_enc, true); + sde_encoder_control_te(sde_enc, true); } void sde_encoder_virt_reset(struct drm_encoder *drm_enc)