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

Commit 8ba47039 authored by Lloyd Atkinson's avatar Lloyd Atkinson
Browse files

drm/msm/sde: partial update of display output



Analyze the connector and crtc regions of interest and modify
the layer mixer output to match the user's desired output roi.
This patch adds SDE support for ROIs that are centered and
symmetric around the layer mixer split point.

Change-Id: I681a8e191a542b74b84fdb0d6d3542de3f332355
Signed-off-by: default avatarLloyd Atkinson <latkinso@codeaurora.org>
parent 925b2207
Loading
Loading
Loading
Loading
+42 −9
Original line number Diff line number Diff line
@@ -138,6 +138,7 @@ enum msm_mdp_crtc_property {
	CRTC_PROP_MEM_IB,
	CRTC_PROP_ROT_PREFILL_BW,
	CRTC_PROP_ROT_CLK,
	CRTC_PROP_ROI_V1,

	/* total # of properties */
	CRTC_PROP_COUNT
@@ -158,6 +159,7 @@ enum msm_mdp_conn_property {
	CONNECTOR_PROP_DST_Y,
	CONNECTOR_PROP_DST_W,
	CONNECTOR_PROP_DST_H,
	CONNECTOR_PROP_ROI_V1,

	/* enum/bitmask properties */
	CONNECTOR_PROP_TOPOLOGY_NAME,
@@ -199,6 +201,38 @@ enum msm_display_caps {
	MSM_DISPLAY_CAP_EDID		= BIT(3),
};

/**
 * struct msm_roi_alignment - region of interest alignment restrictions
 * @xstart_pix_align: left x offset alignment restriction
 * @width_pix_align: width alignment restriction
 * @ystart_pix_align: top y offset alignment restriction
 * @height_pix_align: height alignment restriction
 * @min_width: minimum width restriction
 * @min_height: minimum height restriction
 */
struct msm_roi_alignment {
	uint32_t xstart_pix_align;
	uint32_t width_pix_align;
	uint32_t ystart_pix_align;
	uint32_t height_pix_align;
	uint32_t min_width;
	uint32_t min_height;
};

/**
 * struct msm_roi_caps - display's region of interest capabilities
 * @enabled: true if some region of interest is supported
 * @merge_rois: merge rois before sending to display
 * @num_roi: maximum number of rois supported
 * @align: roi alignment restrictions
 */
struct msm_roi_caps {
	bool enabled;
	bool merge_rois;
	uint32_t num_roi;
	struct msm_roi_alignment align;
};

/**
 * struct msm_display_dsc_info - defines dsc configuration
 * @version:                 DSC version.
@@ -338,6 +372,7 @@ struct msm_compression_info {
 * @vtotal:		display vertical total
 * @jitter:		display jitter configuration
 * @comp_info:          Compression supported by the display
 * @roi_caps:           Region of interest capability info
 */
struct msm_display_info {
	int intf_type;
@@ -361,21 +396,19 @@ struct msm_display_info {
	uint32_t jitter;

	struct msm_compression_info comp_info;
	struct msm_roi_caps roi_caps;
};

#define MSM_MAX_ROI	4

/**
 * struct msm_roi_mapping - Regions of interest structure for mapping CRTC to
 *	Connector output
 * @num_rects: number of valid rectangles in src and dst arrays
 * @src: source roi rectangle
 * @dst: destination roi rectangle
 * struct msm_roi_list - list of regions of interest for a drm object
 * @num_rects: number of valid rectangles in the roi array
 * @roi: list of roi rectangles
 */
struct msm_roi_mapping {
struct msm_roi_list {
	uint32_t num_rects;
	struct drm_clip_rect src[MSM_MAX_ROI];
	struct drm_clip_rect dst[MSM_MAX_ROI];
	struct drm_clip_rect roi[MSM_MAX_ROI];
};

/**
@@ -383,7 +416,7 @@ struct msm_roi_mapping {
 * @rois: Regions of interest structure for mapping CRTC to Connector output
 */
struct msm_display_kickoff_params {
	struct msm_roi_mapping *rois;
	struct msm_roi_list *rois;
};

/**
+136 −0
Original line number Diff line number Diff line
@@ -21,6 +21,12 @@

#define BL_NODE_NAME_SIZE 32

#define SDE_DEBUG_CONN(c, fmt, ...) SDE_DEBUG("conn%d " fmt,\
		(c) ? (c)->base.base.id : -1, ##__VA_ARGS__)

#define SDE_ERROR_CONN(c, fmt, ...) SDE_ERROR("conn%d " fmt,\
		(c) ? (c)->base.base.id : -1, ##__VA_ARGS__)

static const struct drm_prop_enum_list e_topology_name[] = {
	{SDE_RM_TOPOLOGY_UNKNOWN,	"sde_unknown"},
	{SDE_RM_TOPOLOGY_SINGLEPIPE,	"sde_singlepipe"},
@@ -396,6 +402,122 @@ sde_connector_atomic_duplicate_state(struct drm_connector *connector)
	return &c_state->base;
}

static int _sde_connector_roi_v1_check_roi(
		struct sde_connector *c_conn,
		struct drm_clip_rect *roi_conn,
		const struct msm_roi_caps *caps)
{
	const struct msm_roi_alignment *align = &caps->align;
	int w = roi_conn->x2 - roi_conn->x1;
	int h = roi_conn->y2 - roi_conn->y1;

	if (w <= 0 || h <= 0) {
		SDE_ERROR_CONN(c_conn, "invalid conn roi w %d h %d\n", w, h);
		return -EINVAL;
	}

	if (w < align->min_width || w % align->width_pix_align) {
		SDE_ERROR_CONN(c_conn,
				"invalid conn roi width %d min %d align %d\n",
				w, align->min_width, align->width_pix_align);
		return -EINVAL;
	}

	if (h < align->min_height || h % align->height_pix_align) {
		SDE_ERROR_CONN(c_conn,
				"invalid conn roi height %d min %d align %d\n",
				h, align->min_height, align->height_pix_align);
		return -EINVAL;
	}

	if (roi_conn->x1 % align->xstart_pix_align) {
		SDE_ERROR_CONN(c_conn, "invalid conn roi x1 %d align %d\n",
				roi_conn->x1, align->xstart_pix_align);
		return -EINVAL;
	}

	if (roi_conn->y1 % align->ystart_pix_align) {
		SDE_ERROR_CONN(c_conn, "invalid conn roi y1 %d align %d\n",
				roi_conn->y1, align->ystart_pix_align);
		return -EINVAL;
	}

	return 0;
}

static int _sde_connector_set_roi_v1(
		struct sde_connector *c_conn,
		struct sde_connector_state *c_state,
		void *usr_ptr)
{
	struct sde_drm_roi_v1 roi_v1;
	struct msm_display_info display_info;
	struct msm_roi_caps *caps;
	int i, rc;

	if (!c_conn || !c_state) {
		SDE_ERROR("invalid args\n");
		return -EINVAL;
	}

	rc = sde_connector_get_info(&c_conn->base, &display_info);
	if (rc) {
		SDE_ERROR_CONN(c_conn, "display get info error: %d\n", rc);
		return rc;
	}

	caps = &display_info.roi_caps;
	if (!caps->enabled) {
		SDE_ERROR_CONN(c_conn, "display roi capability is disabled\n");
		return -ENOTSUPP;
	}

	memset(&c_state->rois, 0, sizeof(c_state->rois));

	if (!usr_ptr) {
		SDE_DEBUG_CONN(c_conn, "rois cleared\n");
		return 0;
	}

	if (copy_from_user(&roi_v1, usr_ptr, sizeof(roi_v1))) {
		SDE_ERROR_CONN(c_conn, "failed to copy roi_v1 data\n");
		return -EINVAL;
	}

	SDE_DEBUG_CONN(c_conn, "num_rects %d\n", roi_v1.num_rects);

	if (roi_v1.num_rects == 0) {
		SDE_DEBUG_CONN(c_conn, "rois cleared\n");
		return 0;
	}

	if (roi_v1.num_rects > SDE_MAX_ROI_V1 ||
			roi_v1.num_rects > caps->num_roi) {
		SDE_ERROR_CONN(c_conn, "too many rects specified: %d\n",
				roi_v1.num_rects);
		return -EINVAL;
	}

	c_state->rois.num_rects = roi_v1.num_rects;
	for (i = 0; i < roi_v1.num_rects; ++i) {
		int rc;

		rc = _sde_connector_roi_v1_check_roi(c_conn, &roi_v1.roi[i],
				caps);
		if (rc)
			return rc;

		c_state->rois.roi[i] = roi_v1.roi[i];
		SDE_DEBUG_CONN(c_conn, "roi%d: roi 0x%x 0x%x 0x%x 0x%x\n", i,
				c_state->rois.roi[i].x1,
				c_state->rois.roi[i].y1,
				c_state->rois.roi[i].x2,
				c_state->rois.roi[i].y2);
	}

	return 0;
}

static int sde_connector_atomic_set_property(struct drm_connector *connector,
		struct drm_connector_state *state,
		struct drm_property *property,
@@ -461,6 +583,12 @@ static int sde_connector_atomic_set_property(struct drm_connector *connector,
			SDE_ERROR("invalid topology_control: 0x%llX\n", val);
	}

	if (idx == CONNECTOR_PROP_ROI_V1) {
		rc = _sde_connector_set_roi_v1(c_conn, c_state, (void *)val);
		if (rc)
			SDE_ERROR_CONN(c_conn, "invalid roi_v1, rc: %d\n", rc);
	}

	/* check for custom property handling */
	if (!rc && c_conn->ops.set_property) {
		rc = c_conn->ops.set_property(connector,
@@ -705,6 +833,7 @@ struct drm_connector *sde_connector_init(struct drm_device *dev,
	struct sde_kms_info *info;
	struct sde_connector *c_conn = NULL;
	struct dsi_display *dsi_display;
	struct msm_display_info display_info;
	int rc;

	if (!dev || !dev->dev_private || !encoder) {
@@ -837,6 +966,13 @@ struct drm_connector *sde_connector_init(struct drm_device *dev,
		}
	}

	rc = sde_connector_get_info(&c_conn->base, &display_info);
	if (!rc && display_info.roi_caps.enabled) {
		msm_property_install_volatile_range(
				&c_conn->property_info, "sde_drm_roi_v1", 0x0,
				0, ~0, 0, CONNECTOR_PROP_ROI_V1);
	}

	msm_property_install_range(&c_conn->property_info, "RETIRE_FENCE",
			0x0, 0, INR_OPEN_MAX, 0, CONNECTOR_PROP_RETIRE_FENCE);

+1 −1
Original line number Diff line number Diff line
@@ -273,7 +273,7 @@ struct sde_connector_state {
	int mmu_id;
	uint64_t property_values[CONNECTOR_PROP_COUNT];

	struct msm_roi_mapping rois;
	struct msm_roi_list rois;
};

/**
+359 −16
Original line number Diff line number Diff line
@@ -656,21 +656,344 @@ static void _sde_crtc_setup_dim_layer_cfg(struct drm_crtc *crtc,
	}
}

void sde_crtc_get_crtc_roi(struct drm_crtc_state *state,
		const struct sde_rect **crtc_roi)
{
	struct sde_crtc_state *crtc_state;

	if (!state || !crtc_roi)
		return;

	crtc_state = to_sde_crtc_state(state);
	*crtc_roi = &crtc_state->crtc_roi;
}

static int _sde_crtc_set_roi_v1(struct drm_crtc_state *state,
		void *usr_ptr)
{
	struct drm_crtc *crtc;
	struct sde_crtc_state *cstate;
	struct sde_drm_roi_v1 roi_v1;
	int i;

	if (!state) {
		SDE_ERROR("invalid args\n");
		return -EINVAL;
	}

	cstate = to_sde_crtc_state(state);
	crtc = cstate->base.crtc;

	memset(&cstate->user_roi_list, 0, sizeof(cstate->user_roi_list));

	if (!usr_ptr) {
		SDE_DEBUG("crtc%d: rois cleared\n", DRMID(crtc));
		return 0;
	}

	if (copy_from_user(&roi_v1, usr_ptr, sizeof(roi_v1))) {
		SDE_ERROR("crtc%d: failed to copy roi_v1 data\n", DRMID(crtc));
		return -EINVAL;
	}

	SDE_DEBUG("crtc%d: num_rects %d\n", DRMID(crtc), roi_v1.num_rects);

	if (roi_v1.num_rects == 0) {
		SDE_DEBUG("crtc%d: rois cleared\n", DRMID(crtc));
		return 0;
	}

	if (roi_v1.num_rects > SDE_MAX_ROI_V1) {
		SDE_ERROR("crtc%d: too many rects specified: %d\n", DRMID(crtc),
				roi_v1.num_rects);
		return -EINVAL;
	}

	cstate->user_roi_list.num_rects = roi_v1.num_rects;
	for (i = 0; i < roi_v1.num_rects; ++i) {
		cstate->user_roi_list.roi[i] = roi_v1.roi[i];
		SDE_DEBUG("crtc%d: roi%d: roi (%d,%d) (%d,%d)\n",
				DRMID(crtc), i,
				cstate->user_roi_list.roi[i].x1,
				cstate->user_roi_list.roi[i].y1,
				cstate->user_roi_list.roi[i].x2,
				cstate->user_roi_list.roi[i].y2);
	}

	return 0;
}

static int _sde_crtc_set_crtc_roi(struct drm_crtc *crtc,
		struct drm_crtc_state *state)
{
	struct drm_connector *conn;
	struct drm_connector_state *conn_state;
	struct sde_crtc *sde_crtc;
	struct sde_crtc_state *crtc_state;
	struct sde_rect *crtc_roi;
	struct drm_clip_rect crtc_clip, *user_rect;
	int i, num_attached_conns = 0;

	if (!crtc || !state)
		return -EINVAL;

	sde_crtc = to_sde_crtc(crtc);
	crtc_state = to_sde_crtc_state(state);
	crtc_roi = &crtc_state->crtc_roi;

	/* init to invalid range maxes */
	crtc_clip.x1 = ~0;
	crtc_clip.y1 = ~0;
	crtc_clip.x2 = 0;
	crtc_clip.y2 = 0;

	for_each_connector_in_state(state->state, conn, conn_state, i) {
		struct sde_connector_state *sde_conn_state;

		if (!conn_state || conn_state->crtc != crtc)
			continue;

		if (num_attached_conns) {
			SDE_ERROR(
				"crtc%d: unsupported: roi on crtc w/ >1 connectors\n",
					DRMID(crtc));
			return -EINVAL;
		}
		++num_attached_conns;

		sde_conn_state = to_sde_connector_state(conn_state);

		if (memcmp(&sde_conn_state->rois, &crtc_state->user_roi_list,
				sizeof(crtc_state->user_roi_list))) {
			SDE_ERROR("%s: crtc -> conn roi scaling unsupported\n",
					sde_crtc->name);
			return -EINVAL;
		}
	}

	/* aggregate all clipping rectangles together for overall crtc roi */
	for (i = 0; i < crtc_state->user_roi_list.num_rects; i++) {
		user_rect = &crtc_state->user_roi_list.roi[i];

		crtc_clip.x1 = min(crtc_clip.x1, user_rect->x1);
		crtc_clip.y1 = min(crtc_clip.y1, user_rect->y1);
		crtc_clip.x2 = max(crtc_clip.x2, user_rect->x2);
		crtc_clip.y2 = max(crtc_clip.y2, user_rect->y2);

		SDE_DEBUG(
			"%s: conn%d roi%d (%d,%d),(%d,%d) -> crtc (%d,%d),(%d,%d)\n",
				sde_crtc->name, DRMID(crtc), i,
				user_rect->x1, user_rect->y1,
				user_rect->x2, user_rect->y2,
				crtc_clip.x1, crtc_clip.y1,
				crtc_clip.x2, crtc_clip.y2);

	}

	if (crtc_clip.x2  && crtc_clip.y2) {
		crtc_roi->x = crtc_clip.x1;
		crtc_roi->y = crtc_clip.y1;
		crtc_roi->w = crtc_clip.x2 - crtc_clip.x1;
		crtc_roi->h = crtc_clip.y2 - crtc_clip.y1;
	} else {
		crtc_roi->x = 0;
		crtc_roi->y = 0;
		crtc_roi->w = 0;
		crtc_roi->h = 0;
	}

	SDE_DEBUG("%s: crtc roi (%d,%d,%d,%d)\n", sde_crtc->name,
			crtc_roi->x, crtc_roi->y, crtc_roi->w, crtc_roi->h);

	return 0;
}

static int _sde_crtc_set_lm_roi(struct drm_crtc *crtc,
		struct drm_crtc_state *state, int lm_idx)
{
	struct sde_crtc *sde_crtc;
	struct sde_crtc_state *crtc_state;
	const struct sde_rect *crtc_roi;
	const struct sde_rect *lm_bounds;
	struct sde_rect *lm_roi;

	if (!crtc || !state || lm_idx >= ARRAY_SIZE(crtc_state->lm_bounds))
		return -EINVAL;

	sde_crtc = to_sde_crtc(crtc);
	crtc_state = to_sde_crtc_state(state);
	crtc_roi = &crtc_state->crtc_roi;
	lm_bounds = &crtc_state->lm_bounds[lm_idx];
	lm_roi = &crtc_state->lm_roi[lm_idx];

	if (!sde_kms_rect_is_null(crtc_roi)) {
		sde_kms_rect_intersect(crtc_roi, lm_bounds, lm_roi);
		if (sde_kms_rect_is_null(lm_roi)) {
			SDE_ERROR("unsupported R/L only partial update\n");
			return -EINVAL;
		}
	} else {
		memcpy(lm_roi, lm_bounds, sizeof(*lm_roi));
	}

	SDE_DEBUG("%s: lm%d roi (%d,%d,%d,%d)\n", sde_crtc->name, lm_idx,
			lm_roi->x, lm_roi->y, lm_roi->w, lm_roi->h);

	return 0;
}

static int _sde_crtc_check_rois_centered_and_symmetric(struct drm_crtc *crtc,
		struct drm_crtc_state *state)
{
	struct sde_crtc *sde_crtc;
	struct sde_crtc_state *crtc_state;
	const struct sde_rect *roi_prv, *roi_cur;
	int lm_idx;

	if (!crtc || !state)
		return -EINVAL;

	/*
	 * On certain HW, ROIs must be centered on the split between LMs,
	 * and be of equal width.
	 */

	sde_crtc = to_sde_crtc(crtc);
	crtc_state = to_sde_crtc_state(state);

	roi_prv = &crtc_state->lm_roi[0];
	for (lm_idx = 1; lm_idx < sde_crtc->num_mixers; lm_idx++) {
		roi_cur = &crtc_state->lm_roi[lm_idx];

		/* check lm rois are equal width & first roi ends at 2nd roi */
		if (((roi_prv->x + roi_prv->w) != roi_cur->x) ||
				(roi_prv->w != roi_cur->w)) {
			SDE_ERROR("%s: roi lm%d x %d w %d lm%d x %d w %d\n",
					sde_crtc->name,
					lm_idx-1, roi_prv->x, roi_prv->w,
					lm_idx, roi_cur->x, roi_cur->w);
			return -EINVAL;
		}
		roi_prv = roi_cur;
	}

	return 0;
}

static int _sde_crtc_check_planes_within_crtc_roi(struct drm_crtc *crtc,
		struct drm_crtc_state *state)
{
	struct sde_crtc *sde_crtc;
	struct sde_crtc_state *crtc_state;
	const struct sde_rect *crtc_roi;
	struct drm_plane_state *pstate;
	struct drm_plane *plane;

	if (!crtc || !state)
		return -EINVAL;

	/*
	 * Reject commit if a Plane CRTC destination coordinates fall outside
	 * the partial CRTC ROI. LM output is determined via connector ROIs,
	 * if they are specified, not Plane CRTC ROIs.
	 */

	sde_crtc = to_sde_crtc(crtc);
	crtc_state = to_sde_crtc_state(state);
	crtc_roi = &crtc_state->crtc_roi;

	if (sde_kms_rect_is_null(crtc_roi))
		return 0;

	drm_atomic_crtc_state_for_each_plane(plane, state) {
		struct sde_rect plane_roi, intersection;

		pstate = drm_atomic_get_plane_state(state->state, plane);
		if (IS_ERR_OR_NULL(pstate)) {
			int rc = PTR_ERR(pstate);

			SDE_ERROR("%s: failed to get plane%d state, %d\n",
					sde_crtc->name, plane->base.id, rc);
			return rc;
		}

		plane_roi.x = pstate->crtc_x;
		plane_roi.y = pstate->crtc_y;
		plane_roi.w = pstate->crtc_w;
		plane_roi.h = pstate->crtc_h;
		sde_kms_rect_intersect(crtc_roi, &plane_roi, &intersection);
		if (!sde_kms_rect_is_equal(&plane_roi, &intersection)) {
			SDE_ERROR(
				"%s: plane%d crtc roi (%d,%d,%d,%d) outside crtc roi (%d,%d,%d,%d)\n",
					sde_crtc->name, plane->base.id,
					plane_roi.x, plane_roi.y,
					plane_roi.w, plane_roi.h,
					crtc_roi->x, crtc_roi->y,
					crtc_roi->w, crtc_roi->h);
			return -E2BIG;
		}
	}

	return 0;
}

static int _sde_crtc_check_rois(struct drm_crtc *crtc,
		struct drm_crtc_state *state)
{
	struct sde_crtc *sde_crtc;
	int lm_idx;
	int rc;

	if (!crtc || !state)
		return -EINVAL;

	sde_crtc = to_sde_crtc(crtc);

	rc = _sde_crtc_set_crtc_roi(crtc, state);
	if (rc)
		return rc;

	for (lm_idx = 0; lm_idx < sde_crtc->num_mixers; lm_idx++) {
		rc = _sde_crtc_set_lm_roi(crtc, state, lm_idx);
		if (rc)
			return rc;
	}

	rc = _sde_crtc_check_rois_centered_and_symmetric(crtc, state);
	if (rc)
		return rc;

	rc = _sde_crtc_check_planes_within_crtc_roi(crtc, state);
	if (rc)
		return rc;

	return 0;
}

static void _sde_crtc_program_lm_output_roi(struct drm_crtc *crtc)
{
	struct sde_crtc *sde_crtc;
	struct sde_crtc_state *crtc_state;
	const struct sde_rect *lm_roi;
	struct sde_hw_mixer *hw_lm;
	int lm_idx, lm_horiz_position;

	if (!crtc)
		return;

	sde_crtc = to_sde_crtc(crtc);
	crtc_state = to_sde_crtc_state(crtc->state);

	lm_horiz_position = 0;
	for (lm_idx = 0; lm_idx < sde_crtc->num_mixers; lm_idx++) {
		const struct sde_rect *lm_roi = &crtc_state->lm_bounds[lm_idx];
		struct sde_hw_mixer *hw_lm = sde_crtc->mixers[lm_idx].hw_lm;
		struct sde_hw_mixer_cfg cfg;

		lm_roi = &crtc_state->lm_roi[lm_idx];
		hw_lm = sde_crtc->mixers[lm_idx].hw_lm;

		SDE_EVT32(DRMID(crtc_state->base.crtc), lm_idx,
			lm_roi->x, lm_roi->y, lm_roi->w, lm_roi->h);

		if (sde_kms_rect_is_null(lm_roi))
			continue;

@@ -742,9 +1065,12 @@ static void _sde_crtc_blend_setup_mixer(struct drm_crtc *crtc,

		format = to_sde_format(msm_framebuffer_format(pstate->base.fb));

		SDE_EVT32(DRMID(plane), state->src_x, state->src_y,
			state->src_w >> 16, state->src_h >> 16, state->crtc_x,
			state->crtc_y, state->crtc_w, state->crtc_h);
		SDE_EVT32(DRMID(crtc), DRMID(plane),
				state->fb ? state->fb->base.id : -1,
				state->src_x >> 16, state->src_y >> 16,
				state->src_w >> 16, state->src_h >> 16,
				state->crtc_x, state->crtc_y,
				state->crtc_w, state->crtc_h);

		for (lm_idx = 0; lm_idx < sde_crtc->num_mixers; lm_idx++) {
			struct sde_rect intersect;
@@ -877,6 +1203,8 @@ static void _sde_crtc_blend_setup(struct drm_crtc *crtc)
		ctl->ops.setup_blendstage(ctl, mixer[i].hw_lm->idx,
			&sde_crtc->stage_cfg, i);
	}

	_sde_crtc_program_lm_output_roi(crtc);
}

void sde_crtc_prepare_commit(struct drm_crtc *crtc,
@@ -1329,14 +1657,18 @@ static void _sde_crtc_setup_lm_bounds(struct drm_crtc *crtc,
	crtc_split_width = sde_crtc_mixer_width(sde_crtc, adj_mode);

	for (i = 0; i < sde_crtc->num_mixers; i++) {
		struct sde_rect *lm_bound = &cstate->lm_bounds[i];

		lm_bound->x = crtc_split_width * i;
		lm_bound->y = 0;
		lm_bound->w = crtc_split_width;
		lm_bound->h = adj_mode->vdisplay;
		SDE_EVT32(DRMID(crtc), i, lm_bound->x, lm_bound->y,
				lm_bound->w, lm_bound->h);
		cstate->lm_bounds[i].x = crtc_split_width * i;
		cstate->lm_bounds[i].y = 0;
		cstate->lm_bounds[i].w = crtc_split_width;
		cstate->lm_bounds[i].h = adj_mode->vdisplay;
		memcpy(&cstate->lm_roi[i], &cstate->lm_bounds[i],
				sizeof(cstate->lm_roi[i]));
		SDE_EVT32(DRMID(crtc), i,
				cstate->lm_bounds[i].x, cstate->lm_bounds[i].y,
				cstate->lm_bounds[i].w, cstate->lm_bounds[i].h);
		SDE_DEBUG("%s: lm%d bnd&roi (%d,%d,%d,%d)\n", sde_crtc->name, i,
				cstate->lm_roi[i].x, cstate->lm_roi[i].y,
				cstate->lm_roi[i].w, cstate->lm_roi[i].h);
	}

	drm_mode_debug_printmodeline(adj_mode);
@@ -1366,10 +1698,10 @@ static void sde_crtc_atomic_begin(struct drm_crtc *crtc,
	sde_crtc = to_sde_crtc(crtc);
	dev = crtc->dev;

	if (!sde_crtc->num_mixers)
	if (!sde_crtc->num_mixers) {
		_sde_crtc_setup_mixers(crtc);

		_sde_crtc_setup_lm_bounds(crtc, crtc->state);
	}

	if (sde_crtc->event) {
		WARN_ON(sde_crtc->event);
@@ -2118,6 +2450,11 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc,
		}
	}

	rc = _sde_crtc_check_rois(crtc, state);
	if (rc) {
		SDE_ERROR("crtc%d failed roi check %d\n", crtc->base.id, rc);
		goto end;
	}

end:
	_sde_crtc_rp_free_unused(&cstate->rp);
@@ -2244,6 +2581,9 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc,
			"dim_layer_v1", 0x0, 0, ~0, 0, CRTC_PROP_DIM_LAYER_V1);
	}

	msm_property_install_volatile_range(&sde_crtc->property_info,
		"sde_drm_roi_v1", 0x0, 0, ~0, 0, CRTC_PROP_ROI_V1);

	sde_kms_info_reset(info);

	sde_kms_info_add_keyint(info, "hw_version", catalog->hwversion);
@@ -2316,6 +2656,9 @@ static int sde_crtc_atomic_set_property(struct drm_crtc *crtc,
			case CRTC_PROP_DIM_LAYER_V1:
				_sde_crtc_set_dim_layer_v1(cstate, (void *)val);
				break;
			case CRTC_PROP_ROI_V1:
				ret = _sde_crtc_set_roi_v1(state, (void *)val);
				break;
			default:
				/* nothing to do */
				break;
+18 −0
Original line number Diff line number Diff line
@@ -250,6 +250,11 @@ struct sde_crtc_respool {
 * @rsc_client    : sde rsc client when mode is valid
 * @lm_bounds     : LM boundaries based on current mode full resolution, no ROI.
 *                  Origin top left of CRTC.
 * @crtc_roi      : Current CRTC ROI. Possibly sub-rectangle of mode.
 *                  Origin top left of CRTC.
 * @lm_roi        : Current LM ROI, possibly sub-rectangle of mode.
 *                  Origin top left of CRTC.
 * @user_roi_list : List of user's requested ROIs as from set property
 * @property_values: Current crtc property values
 * @input_fence_timeout_ns : Cached input fence timeout, in ns
 * @property_blobs: Reference pointers for blob properties
@@ -270,6 +275,9 @@ struct sde_crtc_state {
	bool rsc_update;

	struct sde_rect lm_bounds[CRTC_DUAL_MIXERS];
	struct sde_rect crtc_roi;
	struct sde_rect lm_roi[CRTC_DUAL_MIXERS];
	struct msm_roi_list user_roi_list;

	uint64_t property_values[CRTC_PROP_COUNT];
	uint64_t input_fence_timeout_ns;
@@ -433,4 +441,14 @@ void *sde_crtc_res_get(struct drm_crtc_state *state, u32 type, u64 tag);
 */
void sde_crtc_res_put(struct drm_crtc_state *state, u32 type, u64 tag);

/**
 * sde_crtc_get_crtc_roi - retrieve the crtc_roi from the given state object
 *	used to allow the planes to adjust their final lm out_xy value in the
 *	case of partial update
 * @crtc_state: Pointer to crtc state
 * @crtc_roi: Output pointer to crtc roi in the given state
 */
void sde_crtc_get_crtc_roi(struct drm_crtc_state *state,
		const struct sde_rect **crtc_roi);

#endif /* _SDE_CRTC_H_ */
Loading