Forráskód Böngészése

Merge "disp: msm: sde: add line insertion support for sspp"

qctecmdr 3 éve
szülő
commit
04ddb3a852

+ 4 - 0
msm/dsi/dsi_defs.h

@@ -499,6 +499,8 @@ struct dsi_split_link_config {
  *			 cmd it points to the line after TE.
  * @dma_sched_window:	 Determines the width of the window during the
  *			 DSI command will be sent by the HW.
+ * @vpadding:			 panel stacking height.
+ * @line_insertion_enable: line insertion support enable.
  */
 struct dsi_host_common_cfg {
 	enum dsi_pixel_format dst_format;
@@ -526,6 +528,8 @@ struct dsi_host_common_cfg {
 	u32 byte_intf_clk_div;
 	u32 dma_sched_line;
 	u32 dma_sched_window;
+	u32 vpadding;
+	bool line_insertion_enable;
 };
 
 /**

+ 6 - 0
msm/dsi/dsi_drm.c

@@ -641,6 +641,12 @@ int dsi_conn_get_mode_info(struct drm_connector *connector,
 	mode_info->qsync_min_fps = dsi_mode->timing.qsync_min_fps;
 	mode_info->wd_jitter = dsi_mode->priv_info->wd_jitter;
 
+	if (dsi_display->panel)
+		mode_info->vpadding = dsi_display->panel->host_config.vpadding;
+	if (mode_info->vpadding < drm_mode->vdisplay) {
+		mode_info->vpadding = 0;
+		dsi_display->panel->host_config.line_insertion_enable = 0;
+	}
 	memcpy(&mode_info->topology, &dsi_mode->priv_info->topology,
 			sizeof(struct msm_display_topology));
 

+ 2 - 1
msm/dsi/dsi_panel.c

@@ -1179,7 +1179,8 @@ static int dsi_panel_parse_misc_host_config(struct dsi_host_common_cfg *host,
 		host->dma_sched_window = 0;
 	else
 		host->dma_sched_window = window;
-
+	rc = utils->read_u32(utils->data, "qcom,vert-padding-value", &host->vpadding);
+	host->line_insertion_enable = (rc || host->vpadding <= 0) ? false : true;
 	DSI_DEBUG("[%s] DMA scheduling parameters Line: %d Window: %d\n", name,
 			host->dma_sched_line, host->dma_sched_window);
 	return 0;

+ 2 - 0
msm/msm_drv.h

@@ -800,6 +800,7 @@ struct msm_display_wd_jitter_config {
  * @dyn_clk_list: List of dynamic clock rates for RFI.
  * @qsync_min_fps: qsync min fps rate
  * @wd_jitter:         Info for WD jitter.
+ * @vpadding:        panel stacking height
  */
 struct msm_mode_info {
 	uint32_t frame_rate;
@@ -822,6 +823,7 @@ struct msm_mode_info {
 	struct msm_dyn_clk_list dyn_clk_list;
 	u32 qsync_min_fps;
 	struct msm_display_wd_jitter_config wd_jitter;
+	u32 vpadding;
 };
 
 /**

+ 17 - 0
msm/sde/sde_connector.c

@@ -3461,3 +3461,20 @@ int sde_connector_event_notify(struct drm_connector *connector, uint32_t type,
 
 	return ret;
 }
+
+bool sde_connector_is_line_insertion_supported(struct sde_connector *sde_conn)
+{
+	struct dsi_display *display = NULL;
+
+	if (!sde_conn)
+		return false;
+
+	if (sde_conn->connector_type != DRM_MODE_CONNECTOR_DSI)
+		return false;
+
+	display = (struct dsi_display *)sde_conn->display;
+	if (!display || !display->panel)
+		return false;
+
+	return display->panel->host_config.line_insertion_enable;
+}

+ 8 - 0
msm/sde/sde_connector.h

@@ -1275,4 +1275,12 @@ int sde_connector_esd_status(struct drm_connector *connector);
 const char *sde_conn_get_topology_name(struct drm_connector *conn,
 		struct msm_display_topology topology);
 
+/*
+ * sde_connector_is_line_insertion_supported - get line insertion
+ * feature bit value from panel
+ * @sde_conn:    Pointer to sde connector structure
+ * @Return: line insertion support status
+ */
+bool sde_connector_is_line_insertion_supported(struct sde_connector *sde_conn);
+
 #endif /* _SDE_CONNECTOR_H_ */

+ 187 - 0
msm/sde/sde_crtc.c

@@ -795,6 +795,22 @@ static void _sde_crtc_setup_blend_cfg(struct sde_crtc_mixer *mixer,
 		format->alpha_enable, fg_alpha, bg_alpha, blend_op);
 }
 
+static void _sde_crtc_calc_split_dim_layer_yh_param(struct drm_crtc *crtc, u16 *y, u16 *h)
+{
+	u32 padding_y = 0, padding_start = 0, padding_height = 0;
+	struct sde_crtc_state *cstate;
+
+	cstate = to_sde_crtc_state(crtc->state);
+	if (!cstate->line_insertion.panel_line_insertion_enable)
+		return;
+
+	sde_crtc_calc_vpadding_param(crtc->state, *y, *h, &padding_y,
+				     &padding_start, &padding_height);
+
+	*y = padding_y;
+	*h = padding_height;
+}
+
 static void _sde_crtc_setup_dim_layer_cfg(struct drm_crtc *crtc,
 		struct sde_crtc *sde_crtc, struct sde_crtc_mixer *mixer,
 		struct sde_hw_dim_layer *dim_layer)
@@ -853,6 +869,11 @@ static void _sde_crtc_setup_dim_layer_cfg(struct drm_crtc *crtc,
 						cstate->lm_roi[i].y;
 		}
 
+		/* update dim layer rect for panel stacking crtc */
+		if (cstate->line_insertion.padding_height)
+			_sde_crtc_calc_split_dim_layer_yh_param(crtc, &split_dim_layer.rect.y,
+								&split_dim_layer.rect.h);
+
 		SDE_EVT32(DRMID(crtc), dim_layer->stage,
 				cstate->lm_roi[i].x,
 				cstate->lm_roi[i].y,
@@ -1394,6 +1415,100 @@ static int _sde_crtc_check_rois(struct drm_crtc *crtc,
 	return 0;
 }
 
+static u32 _sde_crtc_calc_gcd(u32 a, u32 b)
+{
+	if (b == 0)
+		return a;
+
+	return _sde_crtc_calc_gcd(b, a % b);
+}
+
+static int _sde_crtc_check_panel_stacking(struct drm_crtc *crtc, struct drm_crtc_state *state)
+{
+	struct sde_kms *kms;
+	struct sde_crtc *sde_crtc;
+	struct sde_crtc_state *sde_crtc_state;
+	struct drm_connector *conn;
+	struct msm_mode_info mode_info;
+	struct drm_display_mode *adj_mode = &state->adjusted_mode;
+	struct msm_sub_mode sub_mode;
+	u32 gcd = 0, num_of_active_lines = 0, num_of_dummy_lines = 0;
+	int rc;
+	struct drm_encoder *encoder;
+	const u32 max_encoder_cnt = 1;
+	u32 encoder_cnt = 0;
+
+	kms = _sde_crtc_get_kms(crtc);
+	if (!kms || !kms->catalog) {
+		SDE_ERROR("invalid kms\n");
+		return -EINVAL;
+	}
+
+	sde_crtc = to_sde_crtc(crtc);
+	sde_crtc_state = to_sde_crtc_state(state);
+	/* panel stacking only support single connector */
+	drm_for_each_encoder_mask(encoder, crtc->dev, state->encoder_mask)
+		encoder_cnt++;
+
+	if (!kms->catalog->has_line_insertion || !state->mode_changed ||
+	    encoder_cnt > max_encoder_cnt) {
+		SDE_DEBUG("no line insertion support mode change %d enc cnt %d\n",
+			  state->mode_changed, encoder_cnt);
+		sde_crtc_state->line_insertion.padding_height = 0;
+		return 0;
+	}
+
+	conn = sde_crtc_state->connectors[0];
+	rc = sde_connector_get_mode_info(conn, adj_mode, &sub_mode, &mode_info);
+	if (rc) {
+		SDE_ERROR("failed to get mode info %d\n", rc);
+		return -EINVAL;
+	}
+
+	if (!mode_info.vpadding) {
+		sde_crtc_state->line_insertion.padding_height = 0;
+		return 0;
+	}
+
+	if (mode_info.vpadding < state->mode.vdisplay) {
+		SDE_ERROR("padding height %d is less than vdisplay %d\n",
+			  mode_info.vpadding, state->mode.vdisplay);
+		return -EINVAL;
+	} else if (mode_info.vpadding == state->mode.vdisplay) {
+		SDE_DEBUG("padding height %d is equal to the vdisplay %d\n",
+			  mode_info.vpadding, state->mode.vdisplay);
+		sde_crtc_state->line_insertion.padding_height = 0;
+		return 0;
+	} else if (mode_info.vpadding == sde_crtc_state->line_insertion.padding_height) {
+		return 0;   /* skip calculation if already cached */
+	}
+
+	gcd = _sde_crtc_calc_gcd(mode_info.vpadding, state->mode.vdisplay);
+	if (!gcd) {
+		SDE_ERROR("zero gcd found for padding height %d %d\n",
+			  mode_info.vpadding, state->mode.vdisplay);
+		return -EINVAL;
+	}
+	num_of_active_lines = state->mode.vdisplay;
+	do_div(num_of_active_lines, gcd);
+	num_of_dummy_lines = mode_info.vpadding;
+	do_div(num_of_dummy_lines, gcd);
+	num_of_dummy_lines = num_of_dummy_lines - num_of_active_lines;
+
+	if (num_of_active_lines > MAX_VPADDING_RATIO_M ||
+	    num_of_dummy_lines > MAX_VPADDING_RATIO_N) {
+		SDE_ERROR("unsupported panel stacking pattern %d:%d", num_of_active_lines,
+			  num_of_dummy_lines);
+		return -EINVAL;
+	}
+
+	sde_crtc_state->line_insertion.padding_active = num_of_active_lines;
+	sde_crtc_state->line_insertion.padding_dummy = num_of_dummy_lines;
+	sde_crtc_state->line_insertion.padding_height = mode_info.vpadding;
+
+	return 0;
+}
+
 static void _sde_crtc_program_lm_output_roi(struct drm_crtc *crtc)
 {
 	struct sde_crtc *sde_crtc;
@@ -3587,6 +3702,26 @@ static void _sde_crtc_setup_mixer_for_encoder(
 	}
 }
 
+bool sde_crtc_is_line_insertion_supported(struct drm_crtc *crtc)
+{
+	struct drm_encoder *enc = NULL;
+	struct sde_kms *kms;
+
+	if (!crtc)
+		return false;
+
+	kms = _sde_crtc_get_kms(crtc);
+	if (!kms->catalog->has_line_insertion)
+		return false;
+
+	list_for_each_entry(enc, &crtc->dev->mode_config.encoder_list, head) {
+		if (enc->crtc == crtc)
+			return sde_encoder_is_line_insertion_supported(enc);
+	}
+
+	return false;
+}
+
 static void _sde_crtc_setup_mixers(struct drm_crtc *crtc)
 {
 	struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
@@ -4821,6 +4956,8 @@ static void sde_crtc_enable(struct drm_crtc *crtc,
 	SDE_DEBUG("crtc%d\n", crtc->base.id);
 	SDE_EVT32_VERBOSE(DRMID(crtc));
 	sde_crtc = to_sde_crtc(crtc);
+	cstate->line_insertion.panel_line_insertion_enable =
+			sde_crtc_is_line_insertion_supported(crtc);
 
 	/*
 	 * Avoid drm_crtc_vblank_on during seamless DMS case
@@ -5639,6 +5776,13 @@ static int _sde_crtc_atomic_check(struct drm_crtc *crtc,
 				crtc->base.id, rc);
 		goto end;
 	}
+
+	rc = _sde_crtc_check_panel_stacking(crtc, state);
+	if (rc) {
+		SDE_ERROR("crtc%d failed panel stacking check %d\n",
+			  crtc->base.id, rc);
+		goto end;
+	}
 end:
 	kfree(pstates);
 	kfree(multirect_plane);
@@ -7876,3 +8020,46 @@ void _sde_crtc_vm_release_notify(struct drm_crtc *crtc)
 
 	sde_crtc_event_notify(crtc, DRM_EVENT_VM_RELEASE, &val, sizeof(uint32_t));
 }
+
+void sde_crtc_calc_vpadding_param(struct drm_crtc_state *state, u32 crtc_y, uint32_t crtc_h,
+				  u32 *padding_y, u32 *padding_start, u32 *padding_height)
+{
+	struct sde_kms *kms;
+	struct sde_crtc_state *cstate = to_sde_crtc_state(state);
+	u32 y_remain, y_start, y_end;
+	u32 m, n;
+
+	kms = _sde_crtc_get_kms(state->crtc);
+	if (!kms || !kms->catalog) {
+		SDE_ERROR("invalid kms or catalog\n");
+		return;
+	}
+
+	if (!kms->catalog->has_line_insertion)
+		return;
+
+	if (!cstate->line_insertion.padding_active) {
+		SDE_ERROR("zero padding active value\n");
+		return;
+	}
+
+	/*
+	 * Computation logic to add number of dummy and active line at
+	 * precise position on display
+	 */
+	m = cstate->line_insertion.padding_active;
+	n = m + cstate->line_insertion.padding_dummy;
+	if (m == 0)
+		return;
+
+	y_remain = crtc_y % m;
+	y_start = y_remain + crtc_y / m * n;
+	y_end = (((crtc_y + crtc_h - 1) / m) * n) + ((crtc_y + crtc_h - 1) % m);
+	*padding_y = y_start;
+	*padding_start = m - y_remain;
+	*padding_height = y_end - y_start + 1;
+	SDE_EVT32(DRMID(cstate->base.crtc), y_remain, y_start, y_end, *padding_y, *padding_start,
+		  *padding_height);
+	SDE_DEBUG("crtc:%d padding_y:%d padding_start:%d padding_height:%d\n",
+		  DRMID(cstate->base.crtc), *padding_y, *padding_start, *padding_height);
+}

+ 36 - 0
msm/sde/sde_crtc.h

@@ -432,6 +432,20 @@ enum sde_crtc_dirty_flags {
 
 #define to_sde_crtc(x) container_of(x, struct sde_crtc, base)
 
+/**
+ * struct sde_line_insertion_param - sde line insertion parameters
+ * @panel_line_insertion_enable: line insertion support status
+ * @padding_height: panel height after line padding
+ * @padding_active: active lines in panel stacking pattern
+ * @padding_dummy: dummy lines in panel stacking pattern
+ */
+struct sde_line_insertion_param {
+	bool panel_line_insertion_enable;
+	u32 padding_height;
+	u32 padding_active;
+	u32 padding_dummy;
+};
+
 /**
  * struct sde_crtc_state - sde container for atomic crtc state
  * @base: Base drm crtc state structure
@@ -467,6 +481,7 @@ enum sde_crtc_dirty_flags {
  * @cp_dirty_list: array tracking features that are dirty
  * @cp_range_payload: array storing state user_data passed via range props
  * @cont_splash_populated: State was populated as part of cont. splash
+ * @param: sde line insertion parameters
  */
 struct sde_crtc_state {
 	struct drm_crtc_state base;
@@ -506,6 +521,7 @@ struct sde_crtc_state {
 	struct sde_cp_crtc_range_prop_payload
 		cp_range_payload[SDE_CP_CRTC_MAX_FEATURES];
 	bool cont_splash_populated;
+	struct sde_line_insertion_param line_insertion;
 };
 
 enum sde_crtc_irq_state {
@@ -1073,4 +1089,24 @@ struct drm_encoder *sde_crtc_get_src_encoder_of_clone(struct drm_crtc *crtc);
  */
 void _sde_crtc_vm_release_notify(struct drm_crtc *crtc);
 
+/*
+ * sde_crtc_is_line_insertion_supported - get lineinsertion
+ * feature bit value from panel
+ * @drm_crtc:    Pointer to drm crtc structure
+ * @Return: line insertion support status
+ */
+bool sde_crtc_is_line_insertion_supported(struct drm_crtc *crtc);
+
+/**
+ * sde_crtc_calc_vpadding_param - calculate vpadding parameters
+ * @state: Pointer to DRM crtc state object
+ * @crtc_y: Plane's CRTC_Y offset
+ * @crtc_h: Plane's CRTC_H size
+ * @padding_y: Padding Y offset
+ * @padding_start: Padding start offset
+ * @padding_height: Padding height in total
+ */
+void sde_crtc_calc_vpadding_param(struct drm_crtc_state *state, u32 crtc_y, u32 crtc_h,
+				  u32 *padding_y, u32 *padding_start, u32 *padding_height);
+
 #endif /* _SDE_CRTC_H_ */

+ 26 - 0
msm/sde/sde_encoder.c

@@ -1069,6 +1069,32 @@ static int _sde_encoder_atomic_check_reserve(struct drm_encoder *drm_enc,
 	return ret;
 }
 
+bool sde_encoder_is_line_insertion_supported(struct drm_encoder *drm_enc)
+{
+	struct sde_connector *sde_conn = NULL;
+	struct sde_kms *sde_kms = NULL;
+	struct drm_connector *conn = NULL;
+
+	if (!drm_enc) {
+		SDE_ERROR("invalid drm encoder\n");
+		return false;
+	}
+
+	sde_kms = sde_encoder_get_kms(drm_enc);
+	if (!sde_kms)
+		return false;
+
+	conn = sde_encoder_get_connector(sde_kms->dev, drm_enc);
+	if (!conn || !conn->state)
+		return false;
+
+	sde_conn = to_sde_connector(conn);
+	if (!sde_conn)
+		return false;
+
+	return sde_connector_is_line_insertion_supported(sde_conn);
+}
+
 static void _sde_encoder_get_qsync_fps_callback(struct drm_encoder *drm_enc,
 			u32 *qsync_fps, struct drm_connector_state *conn_state)
 {

+ 8 - 0
msm/sde/sde_encoder.h

@@ -683,5 +683,13 @@ static inline bool sde_encoder_is_widebus_enabled(struct drm_encoder *drm_enc)
 	return sde_enc->mode_info.wide_bus_en;
 }
 
+/*
+ * sde_encoder_is_line_insertion_supported - get line insertion
+ * feature bit value from panel
+ * @drm_enc:    Pointer to drm encoder structure
+ * @Return: line insertion support status
+ */
+bool sde_encoder_is_line_insertion_supported(struct drm_encoder *drm_enc);
+
 void sde_encoder_add_data_to_minidump_va(struct drm_encoder *drm_enc);
 #endif /* __SDE_ENCODER_H__ */

+ 3 - 0
msm/sde/sde_hw_catalog.c

@@ -1820,6 +1820,8 @@ static void sde_sspp_set_features(struct sde_mdss_cfg *sde_cfg,
 
 		sblk->maxlinewidth = sde_cfg->max_sspp_linewidth;
 
+		if (sde_cfg->has_line_insertion)
+			set_bit(SDE_SSPP_LINE_INSERTION, &sspp->features);
 		sblk->smart_dma_priority =
 			PROP_VALUE_ACCESS(props->values, SSPP_SMART_DMA, i);
 		if (sblk->smart_dma_priority && sde_cfg->smart_dma_rev)
@@ -5161,6 +5163,7 @@ static int _sde_hardware_pre_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev)
 		sde_cfg->demura_supported[SSPP_DMA1][1] = 1;
 		sde_cfg->demura_supported[SSPP_DMA3][0] = 0;
 		sde_cfg->demura_supported[SSPP_DMA3][1] = 1;
+		sde_cfg->has_line_insertion = true;
 	} else {
 		SDE_ERROR("unsupported chipset id:%X\n", hw_rev);
 		sde_cfg->perf.min_prefill_lines = 0xffff;

+ 8 - 0
msm/sde/sde_hw_catalog.h

@@ -199,6 +199,10 @@ enum {
 #define SYS_CACHE_OP_TYPE	BIT(3)
 #define SYS_CACHE_NO_ALLOC	BIT(4)
 
+/* default line padding ratio limitation */
+#define MAX_VPADDING_RATIO_M	93
+#define MAX_VPADDING_RATIO_N	45
+
 /**
  * sde_sys_cache_type: Types of system cache supported
  * SDE_SYS_CACHE_DISP: Static img system cache
@@ -305,6 +309,7 @@ enum {
  * @SDE_SSPP_FP16_UNMULT     FP16 alpha unmult color processing block support
  * @SDE_SSPP_UBWC_STATS:     Support for ubwc stats
  * @SDE_SSPP_SCALER_DE_LPF_BLEND:     Support for detail enhancer
+ * @SDE_SSPP_LINE_INSERTION  Line insertion support
  * @SDE_SSPP_MAX             maximum value
  */
 enum {
@@ -341,6 +346,7 @@ enum {
 	SDE_SSPP_FP16_UNMULT,
 	SDE_SSPP_UBWC_STATS,
 	SDE_SSPP_SCALER_DE_LPF_BLEND,
+	SDE_SSPP_LINE_INSERTION,
 	SDE_SSPP_MAX
 };
 
@@ -1826,6 +1832,7 @@ struct sde_perf_cfg {
  * @perf                performance control settings
  * @uidle_cfg           settings for uidle feature
  * @irq_offset_list     list of sde_intr_irq_offsets to initialize irq table
+ * @has_line_insertion  line insertion support status
  * @features            bitmap of supported SDE_FEATUREs
  * @dma_formats         supported formats for dma pipe
  * @vig_formats         supported formats for vig pipe
@@ -1940,6 +1947,7 @@ struct sde_mdss_cfg {
 	struct sde_uidle_cfg uidle_cfg;
 	struct list_head irq_offset_list;
 	DECLARE_BITMAP(features, SDE_FEATURE_MAX);
+	bool has_line_insertion;
 
 	/* Supported Pixel Format Lists */
 	struct sde_format_extended *dma_formats;

+ 37 - 0
msm/sde/sde_hw_sspp.c

@@ -44,6 +44,8 @@
 #define SSPP_SRC_CONSTANT_COLOR_REC1       0x180
 #define SSPP_EXCL_REC_SIZE_REC1            0x184
 #define SSPP_EXCL_REC_XY_REC1              0x188
+#define SSPP_LINE_INSERTION_CTRL_REC1      0x1E4
+#define SSPP_LINE_INSERTION_OUT_SIZE_REC1  0x1EC
 
 #define SSPP_UIDLE_CTRL_VALUE              0x1f0
 #define SSPP_UIDLE_CTRL_VALUE_REC1         0x1f4
@@ -116,6 +118,8 @@
 #define SSPP_TRAFFIC_SHAPER_BPC_MAX        0xFF
 #define SSPP_CLK_CTRL                      0x330
 #define SSPP_CLK_STATUS                    0x334
+#define SSPP_LINE_INSERTION_CTRL           0x1E0
+#define SSPP_LINE_INSERTION_OUT_SIZE       0x1E8
 
 /* SSPP_QOS_CTRL */
 #define SSPP_QOS_CTRL_VBLANK_EN            BIT(16)
@@ -1442,6 +1446,37 @@ static int sde_hw_sspp_get_clk_ctrl_status(struct sde_hw_blk_reg_map *hw,
 	return 0;
 }
 
+static void sde_hw_sspp_setup_line_insertion(struct sde_hw_pipe *ctx,
+					     enum sde_sspp_multirect_index rect_index,
+					     struct sde_hw_pipe_line_insertion_cfg *cfg)
+{
+	struct sde_hw_blk_reg_map *c;
+	u32 ctl_off = 0, size_off = 0, ctl_val = 0;
+	u32 idx;
+
+	if (_sspp_subblk_offset(ctx, SDE_SSPP_SRC, &idx) || !cfg)
+		return;
+
+	c = &ctx->hw;
+
+	if (rect_index == SDE_SSPP_RECT_SOLO || rect_index == SDE_SSPP_RECT_0) {
+		ctl_off = SSPP_LINE_INSERTION_CTRL;
+		size_off = SSPP_LINE_INSERTION_OUT_SIZE;
+	} else {
+		ctl_off = SSPP_LINE_INSERTION_CTRL_REC1;
+		size_off = SSPP_LINE_INSERTION_OUT_SIZE_REC1;
+	}
+
+	if (cfg->enable)
+		ctl_val = BIT(31) |
+			(cfg->dummy_lines << 16) |
+			(cfg->first_active_lines << 8) |
+			(cfg->active_lines);
+
+	SDE_REG_WRITE(c, ctl_off, ctl_val);
+	SDE_REG_WRITE(c, size_off, cfg->dst_h << 16);
+}
+
 static void _setup_layer_ops(struct sde_hw_pipe *c,
 		unsigned long features, unsigned long perf_features,
 		bool is_virtual_pipe)
@@ -1536,6 +1571,8 @@ static void _setup_layer_ops(struct sde_hw_pipe *c,
 		c->ops.set_ubwc_stats_roi = sde_hw_sspp_ubwc_stats_set_roi;
 		c->ops.get_ubwc_stats_data = sde_hw_sspp_ubwc_stats_get_data;
 	}
+	if (test_bit(SDE_SSPP_LINE_INSERTION, &features))
+		c->ops.setup_line_insertion = sde_hw_sspp_setup_line_insertion;
 }
 
 static struct sde_sspp_cfg *_sspp_offset(enum sde_sspp sspp,

+ 25 - 0
msm/sde/sde_hw_sspp.h

@@ -291,6 +291,22 @@ struct sde_hw_pipe_ts_cfg {
  */
 #define SDE_PIPE_SBUF_PLANE_NUM	2
 
+/**
+ * struct sde_hw_pipe_line_insertion_cfg - line insertion config
+ * @enable: line insertion is enabled
+ * @dummy_lines: dummy lines before active lines
+ * @first_active_lines: number of active lines before first dummy lines
+ * @active_lines: active lines
+ * @dst_h: total active lines plus dummy lines
+ */
+struct sde_hw_pipe_line_insertion_cfg {
+	bool enable;
+	u32 dummy_lines;
+	u32 first_active_lines;
+	u32 active_lines;
+	u32 dst_h;
+};
+
 /**
  * struct sde_hw_sspp_ops - interface to the SSPP Hw driver functions
  * Caller must call the init function to get the pipe context for each pipe
@@ -688,6 +704,15 @@ struct sde_hw_sspp_ops {
 	 */
 	void (*setup_fp16_unmult)(struct sde_hw_pipe *ctx,
 		enum sde_sspp_multirect_index index, void *data);
+
+	/**
+	 * setup_line_insertion - setup line insertion
+	 * @ctx: Pointer to pipe context
+	 * @cfg: Pointer to line insertion configuration
+	 */
+	void (*setup_line_insertion)(struct sde_hw_pipe *ctx,
+				     enum sde_sspp_multirect_index index,
+				     struct sde_hw_pipe_line_insertion_cfg *cfg);
 };
 
 /**

+ 46 - 0
msm/sde/sde_plane.c

@@ -188,6 +188,45 @@ static struct sde_hw_ctl *_sde_plane_get_hw_ctl(const struct drm_plane *plane)
 	return ctl;
 }
 
+static void _sde_plane_setup_panel_stacking(struct sde_plane *psde,
+					    struct sde_plane_state *pstate)
+{
+	struct sde_hw_pipe_line_insertion_cfg *cfg;
+	struct sde_crtc_state *cstate;
+	u32 h_start = 0, h_total = 0, y_start = 0;
+	struct drm_plane_state *dpstate = NULL;
+	struct drm_crtc *drm_crtc = NULL;
+
+	if (!psde || !psde->base.state || !psde->base.state->crtc) {
+		SDE_ERROR("Invalid plane psde %p or drm plane state or drm crtc\n", psde);
+		return;
+	}
+
+	dpstate = psde->base.state;
+	drm_crtc = dpstate->crtc;
+	cstate = to_sde_crtc_state(drm_crtc->state);
+	pstate->lineinsertion_feature = cstate->line_insertion.panel_line_insertion_enable;
+
+	if ((!test_bit(SDE_SSPP_LINE_INSERTION, (unsigned long *)&psde->features)) ||
+	    !cstate->line_insertion.panel_line_insertion_enable)
+		return;
+
+	cfg = &pstate->line_insertion_cfg;
+	memset(cfg, 0, sizeof(*cfg));
+	if (!cstate->line_insertion.padding_height)
+		return;
+
+	sde_crtc_calc_vpadding_param(psde->base.state->crtc->state,
+				     pstate->base.crtc_y, pstate->base.crtc_h,
+				     &y_start, &h_start, &h_total);
+	cfg->enable = true;
+	cfg->dummy_lines = cstate->line_insertion.padding_dummy;
+	cfg->active_lines = cstate->line_insertion.padding_active;
+	cfg->first_active_lines = h_start;
+	cfg->dst_h = h_total;
+	psde->pipe_cfg.dst_rect.y += y_start - pstate->base.crtc_y;
+}
+
 static bool sde_plane_enabled(const struct drm_plane_state *state)
 {
 	return state && state->fb && state->crtc;
@@ -3094,6 +3133,8 @@ static void _sde_plane_update_roi_config(struct drm_plane *plane,
 
 	_sde_plane_setup_scaler(psde, pstate, fmt, false);
 
+	_sde_plane_setup_panel_stacking(psde, pstate);
+
 	/* check for color fill */
 	psde->color_fill = (uint32_t)sde_plane_get_property(pstate,
 			PLANE_PROP_COLOR_FILL);
@@ -3137,6 +3178,11 @@ static void _sde_plane_update_roi_config(struct drm_plane *plane,
 				true,
 				pstate->multirect_index,
 				pstate->multirect_mode);
+	/* update line insertion */
+	if (pstate->lineinsertion_feature && psde->pipe_hw->ops.setup_line_insertion)
+		psde->pipe_hw->ops.setup_line_insertion(psde->pipe_hw,
+				pstate->multirect_index,
+				&pstate->line_insertion_cfg);
 }
 
 static void _sde_plane_update_format_and_rects(struct sde_plane *psde,

+ 5 - 0
msm/sde/sde_plane.h

@@ -1,4 +1,5 @@
 /*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
  * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
  * Copyright (C) 2013 Red Hat
  * Author: Rob Clark <[email protected]>
@@ -112,6 +113,8 @@ enum sde_plane_sclcheck_state {
  * @cdp_cfg:	CDP configuration
  * @cont_splash_populated: State was populated as part of cont. splash
  * @ubwc_stats_roi: cached roi for ubwc stats
+ * @line_insertion_cfg: line insertion configuration
+ * @lineinsertion_feature:	panel line insertion feature
  */
 struct sde_plane_state {
 	struct drm_plane_state base;
@@ -147,6 +150,8 @@ struct sde_plane_state {
 	bool cont_splash_populated;
 
 	struct sde_drm_ubwc_stats_roi ubwc_stats_roi;
+	struct sde_hw_pipe_line_insertion_cfg line_insertion_cfg;
+	bool lineinsertion_feature;
 };
 
 /**