Просмотр исходного кода

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

Add line insertion support for sspp, this is used to support
display with external splitter. Line insertion logic checks
the difference between screen logical height and physical
height. If any difference is observed adds dummy and active
lines on screen.

Change-Id: Ieec322273df000a53fb39e05174c2d67c3c2da81
Signed-off-by: Rajesh kv <[email protected]>
Rajesh kv 3 лет назад
Родитель
Сommit
03aad2fdf9

+ 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;
 };
 
 /**