Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3171ff8f authored by Veera Sundaram Sankaran's avatar Veera Sundaram Sankaran
Browse files

drm/msm/sde: add dim layer support



Dim layer is a layer mixer feature added from SDE 4.0, where the
specified rect can be color filled in the LM output. It can be
configured for any stage, with the constraint that no other
pipes should be staged in that stage. Also, it would not work
along with src_split logic, so the dim layer should fall within
the LM boundaries.

Change-Id: I2d7f3d22ed51dbc8d8d8cffc7a12ed81f249ee9f
Signed-off-by: default avatarVeera Sundaram Sankaran <veeras@codeaurora.org>
parent 02dd6acc
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -118,6 +118,8 @@ Optional properties:
				control feature is available on each source pipe.
- qcom,sde-has-src-split:	Boolean property to indicate if source split
				feature is available or not.
- qcom,sde-has-dim-layer:	Boolean property to indicate if mixer has dim layer
				feature is available or not.
- qcom,sde-has-mixer-gc:	Boolean property to indicate if mixer has gamma correction
				feature available or not.
- qcom,sde-has-cdp:		Boolean property to indicate if cdp feature is
@@ -340,6 +342,7 @@ Example:
    qcom,sde-panic-per-pipe;
    qcom,sde-has-cdp;
    qcom,sde-has-src-split;
    qcom,sde-has-dim-layer;
    qcom,sde-sspp-src-size = <0x100>;
    qcom,sde-mixer-size = <0x100>;
    qcom,sde-ctl-size = <0x100>;
+1 −0
Original line number Diff line number Diff line
@@ -124,6 +124,7 @@ enum msm_mdp_crtc_property {
	CRTC_PROP_INPUT_FENCE_TIMEOUT = CRTC_PROP_BLOBCOUNT,
	CRTC_PROP_OUTPUT_FENCE,
	CRTC_PROP_OUTPUT_FENCE_OFFSET,
	CRTC_PROP_DIM_LAYER_V1,

	/* total # of properties */
	CRTC_PROP_COUNT
+195 −4
Original line number Diff line number Diff line
@@ -156,15 +156,78 @@ 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_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)
{
	struct sde_hw_mixer *lm;
	struct sde_rect mixer_rect;
	struct sde_hw_dim_layer split_dim_layer;
	u32 mixer_width, mixer_height;
	int i;

	if (!dim_layer->rect.w || !dim_layer->rect.h) {
		SDE_DEBUG("empty dim layer\n");
		return;
	}

	mixer_width = get_crtc_split_width(crtc);
	mixer_height = get_crtc_mixer_height(crtc);
	mixer_rect = (struct sde_rect) {0, 0, mixer_width, mixer_height};

	split_dim_layer.stage = dim_layer->stage;
	split_dim_layer.color_fill = dim_layer->color_fill;

	/*
	 * traverse through the layer mixers attached to crtc and find the
	 * intersecting dim layer rect in each LM and program accordingly.
	 */
	for (i = 0; i < sde_crtc->num_mixers; i++) {
		split_dim_layer.flags = dim_layer->flags;
		mixer_rect.x = i * mixer_width;

		sde_kms_rect_intersect(&split_dim_layer.rect, &mixer_rect,
					&dim_layer->rect);
		if (!split_dim_layer.rect.w && !split_dim_layer.rect.h) {
			/*
			 * no extra programming required for non-intersecting
			 * layer mixers with INCLUSIVE dim layer
			 */
			if (split_dim_layer.flags
					& SDE_DRM_DIM_LAYER_INCLUSIVE)
				continue;

			/*
			 * program the other non-intersecting layer mixers with
			 * INCLUSIVE dim layer of full size for uniformity
			 * with EXCLUSIVE dim layer config.
			 */
			split_dim_layer.flags &= ~SDE_DRM_DIM_LAYER_EXCLUSIVE;
			split_dim_layer.flags |= SDE_DRM_DIM_LAYER_INCLUSIVE;
			split_dim_layer.rect = (struct sde_rect) {0, 0,
						mixer_width, mixer_height};

		} else {
			split_dim_layer.rect.x = split_dim_layer.rect.x
							- (i * mixer_width);
		}

		lm = mixer[i].hw_lm;
		mixer[i].mixer_op_mode |= 1 << split_dim_layer.stage;
		lm->ops.setup_dim_layer(lm, &split_dim_layer);
	}
}

static void _sde_crtc_blend_setup_mixer(struct drm_crtc *crtc,
	struct sde_crtc *sde_crtc, struct sde_crtc_mixer *mixer)
{
	struct drm_plane *plane;

	struct sde_crtc_state *cstate;
	struct sde_plane_state *pstate = NULL;
	struct sde_format *format;
	struct sde_hw_ctl *ctl = mixer->hw_ctl;
	struct sde_hw_stage_cfg *stage_cfg = &sde_crtc->stage_cfg;
	struct sde_hw_ctl *ctl;
	struct sde_hw_mixer *lm;
	struct sde_hw_stage_cfg *stage_cfg;

	u32 flush_mask = 0, crtc_split_width;
	uint32_t lm_idx = LEFT_MIXER, idx;
@@ -172,7 +235,16 @@ static void _sde_crtc_blend_setup_mixer(struct drm_crtc *crtc,
	bool lm_right = false;
	int left_crtc_zpos_cnt[SDE_STAGE_MAX + 1] = {0};
	int right_crtc_zpos_cnt[SDE_STAGE_MAX + 1] = {0};
	int i;

	if (!sde_crtc || !mixer) {
		SDE_ERROR("invalid sde_crtc or mixer\n");
		return;
	}

	ctl = mixer->hw_ctl;
	lm = mixer->hw_lm;
	stage_cfg = &sde_crtc->stage_cfg;
	crtc_split_width = get_crtc_split_width(crtc);

	drm_atomic_crtc_for_each_plane(plane, crtc) {
@@ -246,6 +318,13 @@ static void _sde_crtc_blend_setup_mixer(struct drm_crtc *crtc,
			}
		}
	}

	if (lm && lm->ops.setup_dim_layer) {
		cstate = to_sde_crtc_state(crtc->state);
		for (i = 0; i < cstate->num_dim_layers; i++)
			_sde_crtc_setup_dim_layer_cfg(crtc, sde_crtc,
					mixer, &cstate->dim_layer[i]);
	}
}

/**
@@ -278,6 +357,11 @@ static void _sde_crtc_blend_setup(struct drm_crtc *crtc)
		if (mixer[i].hw_ctl->ops.clear_all_blendstages)
			mixer[i].hw_ctl->ops.clear_all_blendstages(
					mixer[i].hw_ctl);

		/* clear dim_layer settings */
		lm = mixer[i].hw_lm;
		if (lm->ops.clear_dim_layer)
			lm->ops.clear_dim_layer(lm);
	}

	/* initialize stage cfg */
@@ -432,6 +516,62 @@ static void _sde_crtc_set_input_fence_timeout(struct sde_crtc_state *cstate)
	cstate->input_fence_timeout_ns *= NSEC_PER_MSEC;
}

/**
 * _sde_crtc_set_dim_layer_v1 - copy dim layer settings from userspace
 * @cstate:      Pointer to sde crtc state
 * @user_ptr:    User ptr for sde_drm_dim_layer_v1 struct
 */
static void _sde_crtc_set_dim_layer_v1(struct sde_crtc_state *cstate,
		void *usr_ptr)
{
	struct sde_drm_dim_layer_v1 dim_layer_v1;
	struct sde_drm_dim_layer_cfg *user_cfg;
	u32 count, i;

	if (!cstate) {
		SDE_ERROR("invalid cstate\n");
		return;
	}

	if (!usr_ptr) {
		SDE_DEBUG("dim layer data removed\n");
		return;
	}

	if (copy_from_user(&dim_layer_v1, usr_ptr, sizeof(dim_layer_v1))) {
		SDE_ERROR("failed to copy dim layer data\n");
		return;
	}

	count = dim_layer_v1.num_layers;
	if (!count || (count > SDE_MAX_DIM_LAYERS)) {
		SDE_ERROR("invalid number of Dim Layers:%d", count);
		return;
	}

	/* populate from user space */
	cstate->num_dim_layers = count;
	for (i = 0; i < count; i++) {
		user_cfg = &dim_layer_v1.layer_cfg[i];
		cstate->dim_layer[i].flags = user_cfg->flags;
		cstate->dim_layer[i].stage = user_cfg->stage + SDE_STAGE_0;

		cstate->dim_layer[i].rect.x = user_cfg->rect.x1;
		cstate->dim_layer[i].rect.y = user_cfg->rect.y1;
		cstate->dim_layer[i].rect.w = user_cfg->rect.x2 -
						user_cfg->rect.x1 + 1;
		cstate->dim_layer[i].rect.h = user_cfg->rect.y2 -
						user_cfg->rect.y1 + 1;

		cstate->dim_layer[i].color_fill = (struct sde_mdss_color) {
				user_cfg->color_fill.color_0,
				user_cfg->color_fill.color_1,
				user_cfg->color_fill.color_2,
				user_cfg->color_fill.color_3,
		};
	}
}

/**
 * _sde_crtc_wait_for_fences - wait for incoming framebuffer sync fences
 * @crtc: Pointer to CRTC object
@@ -889,6 +1029,7 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc,
{
	struct sde_crtc *sde_crtc;
	struct plane_state pstates[SDE_STAGE_MAX * 2];
	struct sde_crtc_state *cstate;

	const struct drm_plane_state *pstate;
	struct drm_plane *plane;
@@ -910,6 +1051,7 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc,
	}

	sde_crtc = to_sde_crtc(crtc);
	cstate = to_sde_crtc_state(state);
	mode = &state->adjusted_mode;
	SDE_DEBUG("%s: check", sde_crtc->name);

@@ -930,6 +1072,18 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc,
		pstates[cnt].drm_pstate = pstate;
		pstates[cnt].stage = sde_plane_get_property(
				pstates[cnt].sde_pstate, PLANE_PROP_ZPOS);

		/* check dim layer stage with every plane */
		for (i = 0; i < cstate->num_dim_layers; i++) {
			if (pstates[cnt].stage == cstate->dim_layer[i].stage) {
				SDE_ERROR("plane%d/dimlayer in same stage:%d\n",
						plane->base.id,
						cstate->dim_layer[i].stage);
				rc = -EINVAL;
				goto end;
			}
		}

		cnt++;

		if (CHECK_LAYER_BOUNDS(pstate->crtc_y, pstate->crtc_h,
@@ -945,6 +1099,28 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc,
		}
	}

	/* Check dim layer rect bounds and stage */
	for (i = 0; i < cstate->num_dim_layers; i++) {
		if ((CHECK_LAYER_BOUNDS(cstate->dim_layer[i].rect.y,
			cstate->dim_layer[i].rect.h, mode->vdisplay)) ||
		    (CHECK_LAYER_BOUNDS(cstate->dim_layer[i].rect.x,
			cstate->dim_layer[i].rect.w, mode->hdisplay)) ||
		    (cstate->dim_layer[i].stage >= SDE_STAGE_MAX) ||
		    (!cstate->dim_layer[i].rect.w) ||
		    (!cstate->dim_layer[i].rect.h)) {
			SDE_ERROR("invalid dim_layer:{%d,%d,%d,%d}, stage:%d\n",
					cstate->dim_layer[i].rect.x,
					cstate->dim_layer[i].rect.y,
					cstate->dim_layer[i].rect.w,
					cstate->dim_layer[i].rect.h,
					cstate->dim_layer[i].stage);
			SDE_ERROR("display: %dx%d\n", mode->hdisplay,
					mode->vdisplay);
			rc = -E2BIG;
			goto end;
		}
	}

	if (!sde_is_custom_client()) {
		int stage_old = pstates[0].stage;

@@ -1080,6 +1256,12 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc,

	msm_property_install_blob(&sde_crtc->property_info, "capabilities",
		DRM_MODE_PROP_IMMUTABLE, CRTC_PROP_INFO);

	if (catalog->has_dim_layer) {
		msm_property_install_volatile_range(&sde_crtc->property_info,
			"dim_layer_v1", 0x0, 0, ~0, 0, CRTC_PROP_DIM_LAYER_V1);
	}

	sde_kms_info_reset(info);

	sde_kms_info_add_keyint(info, "hw_version", catalog->hwversion);
@@ -1126,8 +1308,17 @@ static int sde_crtc_atomic_set_property(struct drm_crtc *crtc,
		if (!ret) {
			idx = msm_property_index(&sde_crtc->property_info,
					property);
			if (idx == CRTC_PROP_INPUT_FENCE_TIMEOUT)
			switch (idx) {
			case CRTC_PROP_INPUT_FENCE_TIMEOUT:
				_sde_crtc_set_input_fence_timeout(cstate);
				break;
			case CRTC_PROP_DIM_LAYER_V1:
				_sde_crtc_set_dim_layer_v1(cstate, (void *)val);
				break;
			default:
				/* nothing to do */
				break;
			}
		} else {
			ret = sde_cp_crtc_set_property(crtc,
					property, val);
+16 −1
Original line number Diff line number Diff line
@@ -112,6 +112,8 @@ struct sde_crtc {
 * @property_values: Current crtc property values
 * @input_fence_timeout_ns : Cached input fence timeout, in ns
 * @property_blobs: Reference pointers for blob properties
 * @num_dim_layers: Number of dim layers
 * @dim_layer: Dim layer configs
 */
struct sde_crtc_state {
	struct drm_crtc_state base;
@@ -123,6 +125,8 @@ struct sde_crtc_state {
	uint64_t property_values[CRTC_PROP_COUNT];
	uint64_t input_fence_timeout_ns;
	struct drm_property_blob *property_blobs[CRTC_PROP_COUNT];
	uint32_t num_dim_layers;
	struct sde_hw_dim_layer dim_layer[SDE_MAX_DIM_LAYERS];
};

#define to_sde_crtc_state(x) \
@@ -152,7 +156,7 @@ static inline uint32_t get_crtc_split_width(struct drm_crtc *crtc)
	struct drm_display_mode *mode;
	struct sde_crtc *sde_crtc;

	if (!crtc)
	if (!crtc || !crtc->state)
		return 0;

	sde_crtc = to_sde_crtc(crtc);
@@ -160,6 +164,17 @@ static inline uint32_t get_crtc_split_width(struct drm_crtc *crtc)
	return sde_crtc_mixer_width(sde_crtc, mode);
}

static inline uint32_t get_crtc_mixer_height(struct drm_crtc *crtc)
{
	struct drm_display_mode *mode;

	if (!crtc || !crtc->state)
		return 0;

	mode = &crtc->state->adjusted_mode;
	return mode->vdisplay;
}

/**
 * sde_crtc_vblank - enable or disable vblanks for this crtc
 * @crtc: Pointer to drm crtc object
+5 −0
Original line number Diff line number Diff line
@@ -105,6 +105,7 @@ enum sde_prop {
	PANIC_PER_PIPE,
	CDP,
	SRC_SPLIT,
	DIM_LAYER,
	SDE_PROP_MAX,
};

@@ -268,6 +269,7 @@ static struct sde_prop_type sde_prop[] = {
	{PANIC_PER_PIPE, "qcom,sde-panic-per-pipe", false, PROP_TYPE_BOOL},
	{CDP, "qcom,sde-has-cdp", false, PROP_TYPE_BOOL},
	{SRC_SPLIT, "qcom,sde-has-src-split", false, PROP_TYPE_BOOL},
	{DIM_LAYER, "qcom,sde-has-dim-layer", false, PROP_TYPE_BOOL},
};

static struct sde_prop_type sspp_prop[] = {
@@ -1089,6 +1091,8 @@ static int sde_mixer_parse_dt(struct device_node *np,
			ARRAY_SIZE(blend_reg_base), max_blendstages)));
		if (sde_cfg->has_src_split)
			set_bit(SDE_MIXER_SOURCESPLIT, &mixer->features);
		if (sde_cfg->has_dim_layer)
			set_bit(SDE_DIM_LAYER, &mixer->features);

		if ((i < ROT_LM_OFFSET) || (i >= LINE_LM_OFFSET)) {
			mixer->pingpong = pp_count > 0 ? pp_idx + PINGPONG_0
@@ -1832,6 +1836,7 @@ static int sde_parse_dt(struct device_node *np, struct sde_mdss_cfg *cfg)
		cfg->csc_type = SDE_SSPP_CSC_10BIT;

	cfg->has_src_split = PROP_VALUE_ACCESS(prop_value, SRC_SPLIT, 0);
	cfg->has_dim_layer = PROP_VALUE_ACCESS(prop_value, DIM_LAYER, 0);
end:
	kfree(prop_value);
	return rc;
Loading