Selaa lähdekoodia

Merge "disp: msm: sde: enable WB rotation feature for pineapple target"

qctecmdr 2 vuotta sitten
vanhempi
sitoutus
03f6a06942

+ 2 - 0
msm/msm_drv.h

@@ -244,6 +244,8 @@ enum msm_mdp_conn_property {
 	CONNECTOR_PROP_CACHE_STATE,
 	CONNECTOR_PROP_DSC_MODE,
 	CONNECTOR_PROP_WB_USAGE_TYPE,
+	CONNECTOR_PROP_WB_ROT_TYPE,
+	CONNECTOR_PROP_WB_ROT_BYTES_PER_CLK,
 
 	/* total # of properties */
 	CONNECTOR_PROP_COUNT

+ 208 - 27
msm/sde/sde_encoder_phys_wb.c

@@ -31,6 +31,13 @@
 	((SDE_FORMAT_IS_UBWC(fmt) || SDE_FORMAT_IS_YUV(fmt)) ? wb_cfg->sblk->maxlinewidth : \
 	wb_cfg->sblk->maxlinewidth_linear)
 
+/* a5x mini-tile width and height */
+#define MINI_TILE_W 4
+#define MINI_TILE_H 4
+
+#define SDE_WB_ROT_MAX_SRCW 4096
+#define SDE_WB_ROT_MAX_SRCH 4096
+
 static const u32 cwb_irq_tbl[PINGPONG_MAX] = {SDE_NONE, INTR_IDX_PP1_OVFL,
 	INTR_IDX_PP2_OVFL, INTR_IDX_PP3_OVFL, INTR_IDX_PP4_OVFL,
 	INTR_IDX_PP5_OVFL, SDE_NONE, SDE_NONE};
@@ -94,8 +101,7 @@ static void sde_encoder_phys_wb_set_ot_limit(struct sde_encoder_phys *phys_enc)
 	ot_params.num = hw_wb->idx - WB_0;
 	ot_params.width = wb_enc->wb_roi.w;
 	ot_params.height = wb_enc->wb_roi.h;
-	ot_params.is_wfd = ((phys_enc->in_clone_mode) || (usage_type == WB_USAGE_OFFLINE_WB)) ?
-					false : true;
+	ot_params.is_wfd = (usage_type == WB_USAGE_WFD);
 	ot_params.frame_rate = drm_mode_vrefresh(&phys_enc->cached_mode);
 	ot_params.vbif_idx = hw_wb->caps->vbif_idx;
 	ot_params.clk_ctrl = hw_wb->caps->clk_ctrl;
@@ -146,7 +152,7 @@ static void sde_encoder_phys_wb_set_qos_remap(struct sde_encoder_phys *phys_enc)
 	qos_params.num = hw_wb->idx - WB_0;
 	if (phys_enc->in_clone_mode)
 		qos_params.client_type = VBIF_CWB_CLIENT;
-	else if (usage_type == WB_USAGE_OFFLINE_WB)
+	else if (usage_type == WB_USAGE_OFFLINE_WB || usage_type == WB_USAGE_ROT)
 		qos_params.client_type = VBIF_OFFLINE_WB_CLIENT;
 	else
 		qos_params.client_type = VBIF_NRT_CLIENT;
@@ -199,13 +205,19 @@ static void sde_encoder_phys_wb_set_qos(struct sde_encoder_phys *phys_enc)
 	}
 
 	qos_cfg.danger_safe_en = true;
+	if (usage_type == WB_USAGE_ROT) {
+		qos_cfg.danger_safe_en = false;
+		qos_cfg.qos_mode = SDE_WB_QOS_MODE_DYNAMIC;
+		qos_cfg.bytes_per_clk = sde_connector_get_property(conn_state,
+				CONNECTOR_PROP_WB_ROT_BYTES_PER_CLK);
+	}
 
 	if (phys_enc->in_clone_mode)
 		lut_index = (SDE_FORMAT_IS_TILE(wb_enc->wb_fmt)
 				|| SDE_FORMAT_IS_UBWC(wb_enc->wb_fmt)) ?
 					SDE_QOS_LUT_USAGE_CWB_TILE : SDE_QOS_LUT_USAGE_CWB;
 	else
-		lut_index = (usage_type == WB_USAGE_OFFLINE_WB) ?
+		lut_index = (usage_type == WB_USAGE_OFFLINE_WB || usage_type == WB_USAGE_ROT) ?
 					SDE_QOS_LUT_USAGE_OFFLINE_WB : SDE_QOS_LUT_USAGE_NRT;
 
 	creq_index = lut_index * SDE_CREQ_LUT_TYPE_MAX;
@@ -336,9 +348,11 @@ static void _sde_enc_phys_wb_get_out_resolution(struct drm_crtc_state *crtc_stat
 	const struct drm_display_mode *mode = &crtc_state->mode;
 	struct sde_io_res ds_res = {0, }, dnsc_blur_res = {0, };
 	u32 ds_tap_pt = sde_crtc_get_property(cstate, CRTC_PROP_CAPTURE_OUTPUT);
+	enum sde_wb_rot_type rotation_type;
 
 	sde_crtc_get_ds_io_res(crtc_state, &ds_res);
 	sde_connector_get_dnsc_blur_io_res(conn_state, &dnsc_blur_res);
+	rotation_type = sde_connector_get_property(conn_state, CONNECTOR_PROP_WB_ROT_TYPE);
 
 	if (dnsc_blur_res.enabled) {
 		*out_width = dnsc_blur_res.dst_w;
@@ -354,10 +368,13 @@ static void _sde_enc_phys_wb_get_out_resolution(struct drm_crtc_state *crtc_stat
 			*out_width = mode->hdisplay;
 			*out_height = mode->vdisplay;
 		}
-	}  else {
+	} else {
 		*out_width = mode->hdisplay;
 		*out_height = mode->vdisplay;
 	}
+
+	if (rotation_type != WB_ROT_NONE)
+		swap(*out_width, *out_height);
 }
 
 static void _sde_encoder_phys_wb_setup_cdp(struct sde_encoder_phys *phys_enc,
@@ -469,9 +486,10 @@ static void sde_encoder_phys_wb_setup_fb(struct sde_encoder_phys *phys_enc,
 	struct sde_hw_wb *hw_wb;
 	struct sde_hw_wb_cfg *wb_cfg;
 	const struct msm_format *format;
-	int ret;
+	enum sde_wb_rot_type rotation_type;
 	struct msm_gem_address_space *aspace;
 	u32 fb_mode;
+	int ret;
 
 	if (!phys_enc || !phys_enc->sde_kms || !phys_enc->sde_kms->catalog ||
 			!phys_enc->connector) {
@@ -514,6 +532,17 @@ static void sde_encoder_phys_wb_setup_fb(struct sde_encoder_phys *phys_enc,
 		return;
 	}
 
+	rotation_type = sde_connector_get_property(phys_enc->connector->state,
+			CONNECTOR_PROP_WB_ROT_TYPE);
+	wb_cfg->rotate_90 = (rotation_type != WB_ROT_NONE);
+
+	SDE_DEBUG("[enc:%d wb:%d] conn:%d rotation_type:%d format %4.4s and modifier 0x%llX\n",
+			DRMID(phys_enc->parent), WBID(wb_enc), DRMID(phys_enc->connector),
+			rotation_type, (char *)&format->pixel_format, fb->modifier);
+
+	SDE_EVT32(DRMID(phys_enc->parent), WBID(wb_enc), rotation_type, out_width, out_height,
+			fb->width, fb->height);
+
 	wb_cfg->dest.format = sde_get_sde_format_ext(format->pixel_format, fb->modifier);
 	if (!wb_cfg->dest.format) {
 		/* this error should be detected during atomic_check */
@@ -803,13 +832,16 @@ static int _sde_enc_phys_wb_validate_dnsc_blur_ds(struct drm_crtc_state *crtc_st
 	struct sde_crtc_state *cstate = to_sde_crtc_state(crtc_state);
 	const struct drm_display_mode *mode = &crtc_state->mode;
 	struct sde_io_res ds_res = {0, }, dnsc_blur_res = {0, };
+	enum sde_wb_rot_type rotation_type;
 	u32 ds_tap_pt = sde_crtc_get_property(cstate, CRTC_PROP_CAPTURE_OUTPUT);
 
 	sde_crtc_get_ds_io_res(crtc_state, &ds_res);
 	sde_connector_get_dnsc_blur_io_res(conn_state, &dnsc_blur_res);
+	rotation_type = sde_connector_get_property(conn_state, CONNECTOR_PROP_WB_ROT_TYPE);
 
 	/* wb_roi should match with mode w/h if none of these features are enabled */
-	if ((!ds_res.enabled && !dnsc_blur_res.enabled && !cstate->cwb_enc_mask)
+	if ((rotation_type == WB_ROT_NONE) &&
+			(!ds_res.enabled && !dnsc_blur_res.enabled && !cstate->cwb_enc_mask)
 			&& ((wb_roi->w && (wb_roi->w != mode->hdisplay))
 				|| (wb_roi->h && (wb_roi->h != mode->vdisplay)))) {
 		SDE_ERROR("invalid wb-roi {%u,%u,%u,%u} mode:%ux%u\n",
@@ -849,8 +881,16 @@ static int _sde_enc_phys_wb_validate_dnsc_blur_ds(struct drm_crtc_state *crtc_st
 	} else if (SDE_FORMAT_IS_YUV(fmt)) {
 		SDE_ERROR("YUV output not supported with dnsc_blur\n");
 		return -EINVAL;
-	} else if ((wb_roi->w && (wb_roi->w != dnsc_blur_res.dst_w)) ||
-			(wb_roi->h && (wb_roi->h != dnsc_blur_res.dst_h))) {
+	} else if ((rotation_type != WB_ROT_NONE) &&
+			((wb_roi->w && (wb_roi->w != dnsc_blur_res.dst_h)) ||
+			 (wb_roi->h && (wb_roi->h != dnsc_blur_res.dst_w)))) {
+		SDE_ERROR("invalid WB ROI for dnsc and rotate, roi:{%d,%d,%d,%d}, dnsc dst:%ux%u\n",
+				wb_roi->x, wb_roi->y, wb_roi->w, wb_roi->h,
+				dnsc_blur_res.dst_w, dnsc_blur_res.dst_h);
+		return -EINVAL;
+	} else if ((rotation_type == WB_ROT_NONE) &&
+			((wb_roi->w && (wb_roi->w != dnsc_blur_res.dst_w)) ||
+			 (wb_roi->h && (wb_roi->h != dnsc_blur_res.dst_h)))) {
 		SDE_ERROR("invalid WB ROI with dnsc_blur, roi:{%d,%d,%d,%d}, dnsc_blur dst:%ux%u\n",
 				wb_roi->x, wb_roi->y, wb_roi->w, wb_roi->h,
 				dnsc_blur_res.dst_w, dnsc_blur_res.dst_h);
@@ -966,6 +1006,138 @@ static int _sde_enc_phys_wb_validate_cwb(struct sde_encoder_phys *phys_enc,
 	return ret;
 }
 
+static int _sde_encoder_phys_wb_validate_rotation(struct sde_encoder_phys *phys_enc,
+		struct drm_crtc_state *crtc_state, struct drm_connector_state *conn_state)
+{
+	enum sde_wb_rot_type rotation_type;
+	int ret = 0;
+	u32 src_w, src_h;
+	u32 bytes_per_clk;
+	struct sde_rect wb_src, wb_roi = {0,};
+	struct sde_io_res dnsc_res = {0,};
+	const struct sde_rect *crtc_roi = NULL;
+	struct drm_display_mode *mode;
+	enum sde_wb_usage_type usage_type;
+	struct sde_encoder_phys_wb *wb_enc = to_sde_encoder_phys_wb(phys_enc);
+
+	rotation_type = sde_connector_get_property(conn_state, CONNECTOR_PROP_WB_ROT_TYPE);
+	if (rotation_type == WB_ROT_NONE)
+		return ret;
+
+	usage_type = sde_connector_get_property(conn_state, CONNECTOR_PROP_WB_USAGE_TYPE);
+	if (usage_type != WB_USAGE_ROT) {
+		SDE_ERROR("[enc:%d wb:%d] invalid WB usage_ype:%d for rotation_type:%d\n",
+				DRMID(phys_enc->parent), WBID(wb_enc), usage_type, rotation_type);
+		return -EINVAL;
+	}
+
+	bytes_per_clk = sde_connector_get_property(conn_state, CONNECTOR_PROP_WB_ROT_BYTES_PER_CLK);
+	if (!bytes_per_clk) {
+		SDE_ERROR("[enc:%d wb:%d] WB output bytes per XO clock is must for rotation\n",
+				DRMID(phys_enc->parent), WBID(wb_enc));
+		return -EINVAL;
+	}
+
+	ret = sde_wb_connector_state_get_output_roi(conn_state, &wb_roi);
+	if (ret) {
+		SDE_ERROR("[enc:%d wb:%d] failed to get WB output roi, ret:%d\n",
+				DRMID(phys_enc->parent), WBID(wb_enc), ret);
+		return ret;
+	}
+
+	sde_crtc_get_crtc_roi(crtc_state, &crtc_roi);
+	if (!crtc_roi) {
+		SDE_ERROR("[enc:%d wb:%d] could not get crtc roi\n",
+				DRMID(phys_enc->parent), WBID(wb_enc));
+		return -EINVAL;
+	} else if (!sde_kms_rect_is_null(crtc_roi)) {
+		SDE_ERROR("[enc:%d wb:%d] not supporting pu scenario on wb\n",
+				DRMID(phys_enc->parent), WBID(wb_enc));
+		return -EINVAL;
+	}
+
+	mode = &crtc_state->mode;
+	sde_crtc_get_resolution(crtc_state->crtc, crtc_state, mode, &src_w, &src_h);
+	if (!src_w || !src_h) {
+		SDE_ERROR("[enc:%d wb:%d] invalid wb input dimensions src_w:%d src_h:%d\n",
+				 DRMID(phys_enc->parent), WBID(wb_enc), src_w, src_h);
+		return -EINVAL;
+	}
+
+	sde_connector_get_dnsc_blur_io_res(conn_state, &dnsc_res);
+	wb_src.w = dnsc_res.enabled ? dnsc_res.dst_w : src_w;
+	wb_src.h = dnsc_res.enabled ? dnsc_res.dst_h : src_h;
+
+	SDE_DEBUG("[enc:%d wb:%d] wb_src=[%dx%d] dnsc_dst=[%dx%d] wb_roi=[%dx%d]\n",
+			DRMID(phys_enc->parent), WBID(wb_enc), wb_src.w, wb_src.h,
+			dnsc_res.dst_w, dnsc_res.dst_h, wb_roi.w, wb_roi.h);
+
+	if (((wb_src.w != wb_roi.h) || (wb_src.h != wb_roi.w))) {
+		SDE_ERROR("[enc:%d wb:%d] invalid dimension for rotation src:%dx%d vs out:%dx%d\n",
+				 DRMID(phys_enc->parent), WBID(wb_enc), wb_src.w, wb_src.h,
+				 wb_roi.w, wb_roi.h);
+		return -EINVAL;
+	} else if ((wb_roi.x % MINI_TILE_W) || (wb_roi.y % MINI_TILE_H)) {
+		SDE_ERROR("[enc:%d wb:%d] unaligned x,y offsets for rotation:%d x:%d y:%d\n",
+				 DRMID(phys_enc->parent), WBID(wb_enc), rotation_type,
+				 wb_roi.x, wb_roi.y);
+		return -EINVAL;
+	} else if ((rotation_type == WB_ROT_JOB1) && (wb_roi.h % MINI_TILE_H)) {
+		SDE_ERROR("[enc:%d wb:%d] job1 rotation height:%d is not tile aligned\n",
+				 DRMID(phys_enc->parent), WBID(wb_enc), wb_roi.h);
+		return -EINVAL;
+	} else if (wb_src.w > SDE_WB_ROT_MAX_SRCW || wb_src.h > SDE_WB_ROT_MAX_SRCH) {
+	       SDE_ERROR("[enc:%d wb:%d] rotate limit exceeded srcw:[%d vs %d], srch:[%d vs %d]\n",
+			       DRMID(phys_enc->parent), WBID(wb_enc), wb_src.w, SDE_WB_ROT_MAX_SRCW,
+			       wb_src.h, SDE_WB_ROT_MAX_SRCH);
+	       return -EINVAL;
+	}
+
+	return ret;
+}
+
+static int _sde_encoder_phys_wb_validate_output_fmt(struct sde_encoder_phys *phys_enc,
+		struct drm_framebuffer *fb, enum sde_wb_rot_type rotation_type)
+{
+	int ret = 0;
+	struct sde_encoder_phys_wb *wb_enc = to_sde_encoder_phys_wb(phys_enc);
+	const struct sde_format *fmt;
+	const struct sde_format_extended *format_list;
+	struct sde_hw_wb *hw_wb = wb_enc->hw_wb;
+	const struct sde_wb_cfg *wb_cfg = hw_wb->caps;
+	struct sde_kms *sde_kms = phys_enc->sde_kms;
+
+	fmt = sde_get_sde_format_ext(fb->format->format, fb->modifier);
+	if (!fmt) {
+		SDE_ERROR("[enc:%d wb:%d] invalid output pixel format:0x%x mod:0x%x\n",
+				DRMID(phys_enc->parent), WBID(wb_enc),
+				fb->format->format, fb->modifier);
+		return -EINVAL;
+	}
+
+	/* find if sde format is listed as supported format on WB */
+	format_list = (rotation_type != WB_ROT_NONE) ?
+			wb_cfg->rot_format_list : wb_cfg->format_list;
+
+	ret = sde_format_validate_fmt(&sde_kms->base, fmt, format_list);
+	if (ret) {
+		SDE_ERROR("[enc:%d wb:%d] unsupported format for wb rotate:%d fmt:0x%x mod:0x%x\n",
+				DRMID(phys_enc->parent), WBID(wb_enc), rotation_type,
+				fb->format->format, fb->modifier);
+		return ret;
+	} else if (fmt->chroma_sample == SDE_CHROMA_H2V1 || fmt->chroma_sample == SDE_CHROMA_H1V2) {
+		SDE_ERROR("[enc:%d wb:%d] invalid chroma sample type in output format:%x\n",
+			DRMID(phys_enc->parent), WBID(wb_enc), fmt->base.pixel_format);
+		return -EINVAL;
+	} else if (SDE_FORMAT_IS_UBWC(fmt) && !(wb_cfg->features & BIT(SDE_WB_UBWC))) {
+		SDE_ERROR("[enc:%d wb:%d] invalid output format:%x\n",
+				DRMID(phys_enc->parent), WBID(wb_enc), fmt->base.pixel_format);
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
 /**
  * sde_encoder_phys_wb_atomic_check - verify and fixup given atomic states
  * @phys_enc:	Pointer to physical encoder
@@ -987,6 +1159,7 @@ static int sde_encoder_phys_wb_atomic_check(struct sde_encoder_phys *phys_enc,
 	const struct drm_display_mode *mode = &crtc_state->mode;
 	int rc;
 	bool clone_mode_curr = false;
+	enum sde_wb_rot_type rotation_type;
 
 	SDE_DEBUG("[enc:%d wb:%d] atomic_check:\"%s\",%d,%d]\n", DRMID(phys_enc->parent),
 			WBID(wb_enc), mode->name, mode->hdisplay, mode->vdisplay);
@@ -1028,28 +1201,27 @@ static int sde_encoder_phys_wb_atomic_check(struct sde_encoder_phys *phys_enc,
 		return -EINVAL;
 	}
 
+	rotation_type = sde_connector_get_property(conn_state, CONNECTOR_PROP_WB_ROT_TYPE);
+
 	fmt = sde_get_sde_format_ext(fb->format->format, fb->modifier);
 	if (!fmt) {
-		SDE_ERROR("[enc:%d wb:%d] unsupported output pixel format:%x\n",
-				DRMID(phys_enc->parent), WBID(wb_enc), fb->format->format);
+		SDE_ERROR("[enc:%d wb:%d] invalid output pixel format:0x%x mod:0x%x\n",
+				DRMID(phys_enc->parent), WBID(wb_enc),
+				fb->format->format, fb->modifier);
 		return -EINVAL;
 	}
 
-	SDE_DEBUG("[enc:%d enc:%d] fb_id:%u, wxh:%ux%u, fb_fmt:%x,%llx, roi:{%d,%d,%d,%d}\n",
+	SDE_DEBUG("[enc:%d wb:%d] fb_id:%u, wxh:%ux%u, fb_fmt:%x,%llx, roi:{%d,%d,%d,%d}, rot:%d\n",
 		DRMID(phys_enc->parent), WBID(wb_enc), fb->base.id, fb->width, fb->height,
-		fb->format->format, fb->modifier, wb_roi.x, wb_roi.y, wb_roi.w, wb_roi.h);
+		fb->format->format, fb->modifier, wb_roi.x, wb_roi.y, wb_roi.w, wb_roi.h,
+		rotation_type);
 
-	if (fmt->chroma_sample == SDE_CHROMA_H2V1 ||
-		fmt->chroma_sample == SDE_CHROMA_H1V2) {
-		SDE_ERROR("[enc:%d wb:%d] invalid chroma sample type in output format:%x\n",
-			DRMID(phys_enc->parent), WBID(wb_enc), fmt->base.pixel_format);
-		return -EINVAL;
-	}
-
-	if (SDE_FORMAT_IS_UBWC(fmt) && !(wb_cfg->features & BIT(SDE_WB_UBWC))) {
-		SDE_ERROR("[enc:%d wb:%d] invalid output format:%x\n",
-				DRMID(phys_enc->parent), WBID(wb_enc), fmt->base.pixel_format);
-		return -EINVAL;
+        rc = _sde_encoder_phys_wb_validate_output_fmt(phys_enc, fb, rotation_type);
+	if (rc) {
+		SDE_ERROR("[enc:%d wb:%d] output fmt validation failed fb:%u fmt:0x%x mod:0x%x\n",
+				 DRMID(phys_enc->parent), WBID(wb_enc), fb->base.id,
+				 fb->format->format, fb->modifier, rotation_type);
+		return rc;
 	}
 
 	if (SDE_FORMAT_IS_YUV(fmt) != !!phys_enc->hw_cdm)
@@ -1072,6 +1244,15 @@ static int sde_encoder_phys_wb_atomic_check(struct sde_encoder_phys *phys_enc,
 		return rc;
 	}
 
+	if (rotation_type != WB_ROT_NONE) {
+		rc = _sde_encoder_phys_wb_validate_rotation(phys_enc, crtc_state, conn_state);
+		if (rc) {
+			SDE_ERROR("[enc:%d wb:%d] failed in WB rotation validation %d\n",
+					DRMID(phys_enc->parent), WBID(wb_enc), rc);
+			return rc;
+		}
+	}
+
 	_sde_enc_phys_wb_get_out_resolution(crtc_state, conn_state, &out_width, &out_height);
 	if (!wb_roi.w || !wb_roi.h) {
 		wb_roi.x = 0;
@@ -1080,17 +1261,17 @@ static int sde_encoder_phys_wb_atomic_check(struct sde_encoder_phys *phys_enc,
 		wb_roi.h = out_height;
 	}
 
-	if ((wb_roi.x + wb_roi.w > fb->width) || (wb_roi.x + wb_roi.w > out_width)) {
+	if ((wb_roi.x + wb_roi.w > fb->width) || (wb_roi.w > out_width)) {
 		SDE_ERROR("[enc:%d wb:%d] invalid roi x:%d, w:%d, fb_w:%d, mode_w:%d, out_w:%d\n",
 				DRMID(phys_enc->parent), WBID(wb_enc), wb_roi.x, wb_roi.w,
 				fb->width, mode->hdisplay, out_width);
 		return -EINVAL;
-	} else if ((wb_roi.y + wb_roi.h > fb->height) || (wb_roi.y + wb_roi.h > out_height)) {
+	} else if ((wb_roi.y + wb_roi.h > fb->height) || (wb_roi.h > out_height)) {
 		SDE_ERROR("[enc:%d wb:%d] invalid roi y:%d, h:%d, fb_h:%d, mode_h%d, out_h:%d\n",
 				DRMID(phys_enc->parent), WBID(wb_enc), wb_roi.y, wb_roi.h,
 				fb->height, mode->vdisplay, out_height);
 		return -EINVAL;
-	} else if ((out_width > mode->hdisplay) || (out_height > mode->vdisplay)) {
+	} else if ((rotation_type == WB_ROT_NONE) && ((out_width > mode->hdisplay) || (out_height > mode->vdisplay))) {
 		SDE_ERROR("[enc:%d wb:%d] invalid o w/h o_w:%d, mode_w:%d, o_h:%d, mode_h:%d\n",
 				DRMID(phys_enc->parent), WBID(wb_enc), out_width, mode->hdisplay,
 				out_height, mode->vdisplay);

+ 23 - 2
msm/sde/sde_hw_catalog.c

@@ -2550,6 +2550,11 @@ static int sde_wb_parse_dt(struct device_node *np, struct sde_mdss_cfg *sde_cfg)
 					sde_cfg->mdp[0].clk_ctrls[wb->clk_ctrl].bit_off);
 		}
 
+		if (test_bit(SDE_FEATURE_WB_ROTATION, sde_cfg->features)) {
+			set_bit(SDE_WB_LINEAR_ROTATION, &wb->features);
+			wb->rot_format_list = sde_cfg->wb_rot_formats;
+		}
+
 		wb->format_list = sde_cfg->wb_formats;
 	}
 
@@ -4665,7 +4670,7 @@ static int sde_hardware_format_caps(struct sde_mdss_cfg *sde_cfg,
 	uint32_t hw_rev)
 {
 	int rc = 0;
-	uint32_t dma_list_size, vig_list_size, wb2_list_size;
+	uint32_t dma_list_size, vig_list_size, wb2_list_size, wb_rot_fmt_list_size;
 	uint32_t virt_vig_list_size, in_rot_list_size = 0;
 	uint32_t index = 0;
 	uint32_t in_rot_restricted_list_size = 0;
@@ -4746,6 +4751,18 @@ static int sde_hardware_format_caps(struct sde_mdss_cfg *sde_cfg,
 	index = sde_copy_formats(sde_cfg->wb_formats, wb2_list_size,
 			0, wb2_formats, ARRAY_SIZE(wb2_formats));
 
+	/* WB rotation output formats */
+	wb_rot_fmt_list_size = ARRAY_SIZE(wb_rot_formats);
+	sde_cfg->wb_rot_formats = kcalloc(wb_rot_fmt_list_size,
+			sizeof(struct sde_format_extended), GFP_KERNEL);
+	if (!sde_cfg->wb_rot_formats) {
+		rc = -ENOMEM;
+		goto free_wb;
+	}
+
+	index = sde_copy_formats(sde_cfg->wb_rot_formats, wb_rot_fmt_list_size,
+			0, wb_rot_formats, ARRAY_SIZE(wb_rot_formats));
+
 	/* Rotation enabled input formats */
 	if (IS_SDE_INLINE_ROT_REV_100(sde_cfg->true_inline_rot_rev)) {
 		inline_fmt_tbl = true_inline_rot_v1_fmts;
@@ -4766,7 +4783,7 @@ static int sde_hardware_format_caps(struct sde_mdss_cfg *sde_cfg,
 		if (!sde_cfg->inline_rot_formats) {
 			SDE_ERROR("failed to alloc inline rot format list\n");
 			rc = -ENOMEM;
-			goto free_wb;
+			goto free_wb_rot;
 		}
 
 		index = sde_copy_formats(sde_cfg->inline_rot_formats,
@@ -4790,6 +4807,8 @@ static int sde_hardware_format_caps(struct sde_mdss_cfg *sde_cfg,
 	return 0;
 free_in_rot:
 	kfree(sde_cfg->inline_rot_formats);
+free_wb_rot:
+	kfree(sde_cfg->wb_rot_formats);
 free_wb:
 	kfree(sde_cfg->wb_formats);
 free_virt:
@@ -5234,6 +5253,7 @@ static int _sde_hardware_pre_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev)
 		set_bit(SDE_FEATURE_TRUSTED_VM, sde_cfg->features);
 		set_bit(SDE_SYS_CACHE_DISP, sde_cfg->sde_sys_cache_type_map);
 		set_bit(SDE_SYS_CACHE_DISP_WB, sde_cfg->sde_sys_cache_type_map);
+		set_bit(SDE_FEATURE_WB_ROTATION, sde_cfg->features);
 		sde_cfg->allowed_dsc_reservation_switch = SDE_DP_DSC_RESERVATION_SWITCH;
 		sde_cfg->autorefresh_disable_seq = AUTOREFRESH_DISABLE_SEQ2;
 		sde_cfg->perf.min_prefill_lines = 40;
@@ -5418,6 +5438,7 @@ void sde_hw_catalog_deinit(struct sde_mdss_cfg *sde_cfg)
 	kfree(sde_cfg->dma_formats);
 	kfree(sde_cfg->vig_formats);
 	kfree(sde_cfg->wb_formats);
+	kfree(sde_cfg->wb_rot_formats);
 	kfree(sde_cfg->virt_vig_formats);
 	kfree(sde_cfg->inline_rot_formats);
 

+ 9 - 2
msm/sde/sde_hw_catalog.h

@@ -637,6 +637,7 @@ enum {
  * @SDE_WB_DCWB_CTRL        Separate DCWB control is available for configuring
  * @SDE_WB_CWB_DITHER_CTRL  CWB dither is available for configuring
  * @SDE_WB_PROG_LINE        Writeback block supports programmable line ptr
+ * @SDE_WB_LINEAR_ROTATION  Writeback block supports line mode image rotation
  * @SDE_WB_MAX              maximum value
  */
 enum {
@@ -661,6 +662,7 @@ enum {
 	SDE_WB_DCWB_CTRL,
 	SDE_WB_CWB_DITHER_CTRL,
 	SDE_WB_PROG_LINE,
+	SDE_WB_LINEAR_ROTATION,
 	SDE_WB_MAX
 };
 
@@ -708,7 +710,7 @@ enum {
  * @SDE_FEATURE_CWB_DITHER     CWB dither is supported
  * @SDE_FEATURE_DEDICATED_CWB  Dedicated-CWB supported
  * @SDE_FEATURE_DUAL_DEDICATED_CWB   Dual Dedicated-CWB supported
- * @SDE_FEATURE_IDLE_PC        Idle Power Collapse supported
+ * @SDE_FEATURE_WB_ROTATION    Support for image rotation through WB block
  * @SDE_FEATURE_3D_MERGE_RESET 3D merge reset supported
  * @SDE_FEATURE_DECIMATION     Decimation supported
  * @SDE_FEATURE_COMBINED_ALPHA Combined Alpha supported
@@ -751,6 +753,7 @@ enum sde_mdss_features {
 	SDE_FEATURE_CWB_DITHER,
 	SDE_FEATURE_DEDICATED_CWB,
 	SDE_FEATURE_DUAL_DEDICATED_CWB,
+	SDE_FEATURE_WB_ROTATION,
 	SDE_FEATURE_IDLE_PC,
 	SDE_FEATURE_3D_MERGE_RESET,
 	SDE_FEATURE_DECIMATION,
@@ -1484,7 +1487,8 @@ struct sde_intf_cfg  {
  * @base               register offset of this block
  * @features           bit mask identifying sub-blocks/features
  * @sblk               sub-block information
- * @format_list: Pointer to list of supported formats
+ * @format_list:       Pointer to list of supported output formats
+ * @rot_format_list:   Pointer to list of supported output formats in WB rotation
  * @vbif_idx           vbif identifier
  * @xin_id             client interface identifier
  * @clk_ctrl           clock control identifier
@@ -1493,6 +1497,7 @@ struct sde_wb_cfg {
 	SDE_HW_BLK_INFO;
 	const struct sde_wb_sub_blocks *sblk;
 	const struct sde_format_extended *format_list;
+	const struct sde_format_extended *rot_format_list;
 	u32 vbif_idx;
 	u32 xin_id;
 	enum sde_clk_ctrl_type clk_ctrl;
@@ -1864,6 +1869,7 @@ struct sde_perf_cfg {
  * @dma_formats         supported formats for dma pipe
  * @vig_formats         supported formats for vig pipe
  * @wb_formats          supported formats for wb
+ * @wb_rot_formats      supported output formats for wb rotation operation
  * @virt_vig_formats    supported formats for virtual vig pipe
  * @inline_rot_formats  supported formats for inline rotation
  * @inline_rot_restricted_formats       restricted formats for inline rotation
@@ -1982,6 +1988,7 @@ struct sde_mdss_cfg {
 	struct sde_format_extended *dma_formats;
 	struct sde_format_extended *vig_formats;
 	struct sde_format_extended *wb_formats;
+	struct sde_format_extended *wb_rot_formats;
 	struct sde_format_extended *virt_vig_formats;
 	struct sde_format_extended *inline_rot_formats;
 	struct sde_format_extended *inline_rot_restricted_formats;

+ 12 - 0
msm/sde/sde_hw_catalog_format.h

@@ -94,6 +94,10 @@ static const struct sde_format_extended wb2_formats[] = {
 	{DRM_FORMAT_ARGB8888, 0},
 	{DRM_FORMAT_RGBA8888, 0},
 	{DRM_FORMAT_ABGR8888, DRM_FORMAT_MOD_QCOM_COMPRESSED},
+	{DRM_FORMAT_ABGR8888, DRM_FORMAT_MOD_QCOM_TILE},
+	{DRM_FORMAT_XBGR8888, DRM_FORMAT_MOD_QCOM_TILE},
+	{DRM_FORMAT_ABGR2101010, DRM_FORMAT_MOD_QCOM_TILE},
+	{DRM_FORMAT_XBGR2101010, DRM_FORMAT_MOD_QCOM_TILE},
 	{DRM_FORMAT_XRGB8888, 0},
 	{DRM_FORMAT_RGBX8888, 0},
 	{DRM_FORMAT_XBGR8888, DRM_FORMAT_MOD_QCOM_COMPRESSED},
@@ -131,6 +135,14 @@ static const struct sde_format_extended wb2_formats[] = {
 	{0, 0},
 };
 
+static const struct sde_format_extended wb_rot_formats[] = {
+	{DRM_FORMAT_ABGR8888, DRM_FORMAT_MOD_QCOM_TILE},
+	{DRM_FORMAT_XBGR8888, DRM_FORMAT_MOD_QCOM_TILE},
+	{DRM_FORMAT_ABGR2101010, DRM_FORMAT_MOD_QCOM_TILE},
+	{DRM_FORMAT_XBGR2101010, DRM_FORMAT_MOD_QCOM_TILE},
+	{0, 0},
+};
+
 static const struct sde_format_extended p010_ubwc_formats[] = {
 	P010_UBWC_FMTS,
 };

+ 16 - 0
msm/sde/sde_hw_mdss.h

@@ -503,11 +503,27 @@ enum sde_sys_cache_state {
  * WB_USAGE_WFD: WB connector used for WFD
  * WB_USAGE_CWB: WB connector used for concurrent writeback
  * WB_USAGE_OFFLINE_WB: WB connector used for 2-pass composition
+ * WB_USAGE_ROT: WB connector used for image rotation for 2 pass composition
  */
 enum sde_wb_usage_type {
 	WB_USAGE_WFD,
 	WB_USAGE_CWB,
 	WB_USAGE_OFFLINE_WB,
+	WB_USAGE_ROT,
+};
+
+/**
+ * enum sde_wb_rot_type: Type of rotation use case of the WB connector
+ * WB_ROT_NONE : WB Rotation not in use
+ * WB_ROT_SINGLE: WB Rotation used in single job mode for full image rotation
+ * WB_ROT_JOB1: WB Rotation used for rotating half image as first-job
+ * WB_ROT_JOB2: WB Rotation used for rotating half image as second-job
+ */
+enum sde_wb_rot_type {
+	WB_ROT_NONE,
+	WB_ROT_SINGLE,
+	WB_ROT_JOB1,
+	WB_ROT_JOB2,
 };
 
 /** struct sde_format - defines the format configuration which

+ 13 - 0
msm/sde/sde_hw_wb.c

@@ -23,6 +23,7 @@
 #define WB_DST3_ADDR			0x018
 #define WB_DST_YSTRIDE0			0x01C
 #define WB_DST_YSTRIDE1			0x020
+#define WB_TS_WR_CLIENT	                0x040
 #define WB_DST_WRITE_CONFIG		0x048
 #define WB_OUT_SIZE			0x074
 #define WB_ALPHA_X_VALUE		0x078
@@ -200,6 +201,13 @@ static void sde_hw_wb_setup_format(struct sde_hw_wb *ctx,
 	if (SDE_FORMAT_IS_DX(fmt))
 		dst_format |= BIT(21);
 
+	/* Set A5x tile bit for uncompressed tile formats also */
+	if (SDE_FORMAT_IS_TILE(fmt))
+		dst_format |= BIT(31);
+
+	if (data->rotate_90)
+		dst_format |= BIT(11);
+
 	pattern = (fmt->element[3] << 24) |
 			(fmt->element[2] << 16) |
 			(fmt->element[1] << 8)  |
@@ -300,6 +308,11 @@ static void sde_hw_wb_setup_qos_lut(struct sde_hw_wb *ctx,
 	if (cfg->danger_safe_en)
 		qos_ctrl |= WB_QOS_CTRL_DANGER_SAFE_EN;
 
+	if (test_bit(SDE_WB_LINEAR_ROTATION, &ctx->caps->features)) {
+		SDE_REG_WRITE(c, WB_TS_WR_CLIENT, cfg->bytes_per_clk & 0xFF);
+		qos_ctrl |= (cfg->qos_mode << 1);
+	}
+
 	SDE_REG_WRITE(c, WB_QOS_CTRL, qos_ctrl);
 }
 

+ 15 - 0
msm/sde/sde_hw_wb.h

@@ -22,6 +22,7 @@ struct sde_hw_wb_cfg {
 	struct sde_rect roi;
 	struct sde_rect crop;
 	bool is_secure;
+	bool rotate_90;
 };
 
 /**
@@ -48,17 +49,31 @@ struct sde_hw_wb_cdp_cfg {
 	u32 preload_ahead;
 };
 
+/**
+ * enum sde_hw_wb_qos_mode: enumeration of available QOS modes for WB
+ * @SDE_WB_QOS_MODE_STATIC: static qos mode same as existing NRT qos mode
+ * @SDE_WB_QOS_MODE_DYNAMIC: new qos mode to support rotation for real time
+ */
+enum sde_hw_wb_qos_mode {
+	SDE_WB_QOS_MODE_STATIC,
+	SDE_WB_QOS_MODE_DYNAMIC,
+};
+
 /**
  * struct sde_hw_wb_qos_cfg : Writeback pipe QoS configuration
  * @danger_lut: LUT for generate danger level based on fill level
  * @safe_lut: LUT for generate safe level based on fill level
  * @creq_lut: LUT for generate creq level based on fill level
+ * @bytes_per_clk: WB output bytes per XO clock value used in rotation
+ * @qos_mode: enum value mapped for selecting WB QOS mode
  * @danger_safe_en: enable danger safe generation
  */
 struct sde_hw_wb_qos_cfg {
 	u32 danger_lut;
 	u32 safe_lut;
 	u64 creq_lut;
+	u32 bytes_per_clk;
+	enum sde_hw_wb_qos_mode qos_mode;
 	bool danger_safe_en;
 };
 

+ 29 - 0
msm/sde/sde_wb.c

@@ -532,6 +532,18 @@ int sde_wb_connector_set_info_blob(struct drm_connector *connector,
 		sde_kms_info_stop(info);
 	}
 
+	/* Populate info buffer with WB rotation output formats */
+	format_list = wb_dev->wb_cfg->rot_format_list;
+	if (format_list) {
+		sde_kms_info_start(info, "rot_output_formats");
+		while (format_list->fourcc_format) {
+			sde_kms_info_append_format(info, format_list->fourcc_format,
+					format_list->modifier);
+			++format_list;
+		}
+		sde_kms_info_stop(info);
+	}
+
 	sde_kms_info_add_keyint(info, "wb_intf_index", wb_dev->wb_idx - WB_0);
 	sde_kms_info_add_keyint(info, "maxlinewidth", wb_dev->wb_cfg->sblk->maxlinewidth);
 	sde_kms_info_add_keyint(info, "maxlinewidth_linear",
@@ -609,6 +621,14 @@ int sde_wb_connector_post_init(struct drm_connector *connector, void *display)
 		{WB_USAGE_WFD, "wb_usage_wfd"},
 		{WB_USAGE_CWB, "wb_usage_cwb"},
 		{WB_USAGE_OFFLINE_WB, "wb_usage_offline_wb"},
+		{WB_USAGE_ROT, "wb_usage_rot"},
+	};
+
+	static const struct drm_prop_enum_list e_wb_rotate_type[] = {
+		{WB_ROT_NONE, "wb_rot_none"},
+		{WB_ROT_SINGLE, "wb_rot_single"},
+		{WB_ROT_JOB1, "wb_rot_job1"},
+		{WB_ROT_JOB2, "wb_rot_job2"},
 	};
 
 	if (!connector || !display || !wb_dev->wb_cfg || !wb_dev->drm_dev->dev_private) {
@@ -662,6 +682,15 @@ int sde_wb_connector_post_init(struct drm_connector *connector, void *display)
 		msm_property_install_range(&c_conn->property_info, "dnsc_blur",
 			0x0, 0, ~0, 0, CONNECTOR_PROP_DNSC_BLUR);
 
+	if (wb_dev->wb_cfg->features & BIT(SDE_WB_LINEAR_ROTATION)) {
+		msm_property_install_enum(&c_conn->property_info, "wb_rotate_type",
+			0x0, 0, e_wb_rotate_type, ARRAY_SIZE(e_wb_rotate_type),
+			0, CONNECTOR_PROP_WB_ROT_TYPE);
+
+		msm_property_install_range(&c_conn->property_info, "wb_rot_bytes_per_clk",
+			0x0, 0, UINT_MAX, 0, CONNECTOR_PROP_WB_ROT_BYTES_PER_CLK);
+	}
+
 	msm_property_install_enum(&c_conn->property_info, "wb_usage_type",
 			0x0, 0, e_wb_usage_type, ARRAY_SIZE(e_wb_usage_type),
 			0, CONNECTOR_PROP_WB_USAGE_TYPE);