Переглянути джерело

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 <[email protected]>
Shamika Joshi 2 роки тому
батько
коміт
f28d9e0a6a

+ 2 - 12
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;

+ 38 - 15
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,

+ 2 - 3
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

+ 4 - 0
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;

+ 21 - 9
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(

+ 1 - 0
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);
 		}
 	}
 

+ 2 - 0
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
 };
 

+ 41 - 0
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,

+ 12 - 1
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,