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

Commit e53b737c authored by Lloyd Atkinson's avatar Lloyd Atkinson
Browse files

drm/msm: support partial update of dsi panel output



Add support in the DSI DRM driver for partial update. A user's
region of interest request is used to send DCS col/page commands
to the DSI panel to update only a given region on the panel.

Change-Id: I42a462076537131a6f8806bc011af0b80fdfa64e
Signed-off-by: default avatarLloyd Atkinson <latkinso@codeaurora.org>
parent deda3be7
Loading
Loading
Loading
Loading
+7 −2
Original line number Diff line number Diff line
@@ -331,8 +331,13 @@ Optional properties:
					as below:
					--> Reset GPIO value
					--> Sleep value (in ms)
- qcom,partial-update-enabled:		Boolean used to enable partial
- qcom,partial-update-enabled:		String used to enable partial
					panel update for command mode panels.
					"none": partial update is disabled
					"single_roi": default enable mode, only single roi is sent to panel
					"dual_roi": two rois are merged into one big roi. Panel ddic should be able
					to process two roi's along with the DCS command to send two rois.
					disabled if property is not specified.
- qcom,mdss-dsi-horizontal-line-idle:	List of width ranges (EC - SC) in pixels indicating
					additional idle time in dsi clock cycles that is needed
					to compensate for smaller line width.
@@ -627,7 +632,7 @@ Example:
		qcom,mdss-tear-check-rd-ptr-trigger-intr = <1281>;
		qcom,mdss-tear-check-frame-rate = <6000>;
		qcom,mdss-dsi-reset-sequence = <1 2>, <0 10>, <1 10>;
		qcom,partial-update-enabled;
		qcom,partial-update-enabled = "single_roi";
		qcom,dcs-cmd-by-left;
		qcom,mdss-dsi-lp11-init;
		qcom,mdss-dsi-init-delay-us = <100>;
+36 −13
Original line number Diff line number Diff line
@@ -1556,19 +1556,9 @@ int dsi_ctrl_async_timing_update(struct dsi_ctrl *dsi_ctrl,
	return rc;
}

/**
 * dsi_ctrl_setup() - Setup DSI host hardware while coming out of idle screen.
 * @dsi_ctrl:        DSI controller handle.
 *
 * Initializes DSI controller hardware with host configuration provided by
 * dsi_ctrl_update_host_config(). Initialization can be performed only during
 * DSI_CTRL_POWER_CORE_CLK_ON state and after the PHY SW reset has been
 * performed.
 *
 * Return: error code.
 */
int dsi_ctrl_setup(struct dsi_ctrl *dsi_ctrl)
{
	struct dsi_mode_info video_timing;
	int rc = 0;

	if (!dsi_ctrl) {
@@ -1578,6 +1568,12 @@ int dsi_ctrl_setup(struct dsi_ctrl *dsi_ctrl)

	mutex_lock(&dsi_ctrl->ctrl_lock);

	/* replace video mode width with actual roi width */
	memcpy(&video_timing, &dsi_ctrl->host_config.video_timing,
			sizeof(video_timing));
	video_timing.h_active = dsi_ctrl->roi.w;
	video_timing.v_active = dsi_ctrl->roi.h;

	dsi_ctrl->hw.ops.setup_lane_map(&dsi_ctrl->hw,
					&dsi_ctrl->host_config.lane_map);

@@ -1590,8 +1586,8 @@ int dsi_ctrl_setup(struct dsi_ctrl *dsi_ctrl)
					&dsi_ctrl->host_config.u.cmd_engine);

		dsi_ctrl->hw.ops.setup_cmd_stream(&dsi_ctrl->hw,
				&dsi_ctrl->host_config.video_timing,
				dsi_ctrl->host_config.video_timing.h_active * 3,
				&video_timing,
				video_timing.h_active * 3,
				0x0);
		dsi_ctrl->hw.ops.cmd_engine_en(&dsi_ctrl->hw, true);
	} else {
@@ -1611,6 +1607,26 @@ int dsi_ctrl_setup(struct dsi_ctrl *dsi_ctrl)
	return rc;
}

int dsi_ctrl_set_roi(struct dsi_ctrl *dsi_ctrl, struct dsi_rect *roi,
		bool *changed)
{
	int rc = 0;

	if (!dsi_ctrl || !roi || !changed) {
		pr_err("Invalid params\n");
		return -EINVAL;
	}

	mutex_lock(&dsi_ctrl->ctrl_lock);
	if (!dsi_rect_is_equal(&dsi_ctrl->roi, roi)) {
		*changed = true;
		memcpy(&dsi_ctrl->roi, roi, sizeof(dsi_ctrl->roi));
	} else
		*changed = false;
	mutex_unlock(&dsi_ctrl->ctrl_lock);
	return rc;
}

/**
 * dsi_ctrl_phy_reset_config() - Mask/unmask propagation of ahb reset signal
 *	to DSI PHY hardware.
@@ -1789,6 +1805,13 @@ int dsi_ctrl_update_host_config(struct dsi_ctrl *ctrl,

	pr_debug("[DSI_%d]Host config updated\n", ctrl->cell_index);
	memcpy(&ctrl->host_config, config, sizeof(ctrl->host_config));
	ctrl->mode_bounds.x = ctrl->host_config.video_timing.h_active *
			ctrl->horiz_index;
	ctrl->mode_bounds.y = 0;
	ctrl->mode_bounds.w = ctrl->host_config.video_timing.h_active;
	ctrl->mode_bounds.h = ctrl->host_config.video_timing.v_active;
	memcpy(&ctrl->roi, &ctrl->mode_bounds, sizeof(ctrl->mode_bounds));
	ctrl->roi.x = 0;
error:
	mutex_unlock(&ctrl->ctrl_lock);
	return rc;
+23 −1
Original line number Diff line number Diff line
@@ -168,6 +168,7 @@ struct dsi_ctrl_interrupts {
 * struct dsi_ctrl - DSI controller object
 * @pdev:                Pointer to platform device.
 * @cell_index:          Instance cell id.
 * @horiz_index:         Index in physical horizontal CTRL layout, 0 = leftmost
 * @name:                Name of the controller instance.
 * @refcount:            ref counter.
 * @ctrl_lock:           Mutex for hardware and object access.
@@ -182,6 +183,10 @@ struct dsi_ctrl_interrupts {
 * @pwr_info:            Power information.
 * @axi_bus_info:        AXI bus information.
 * @host_config:         Current host configuration.
 * @mode_bounds:         Boundaries of the default mode ROI.
 *                       Origin is at top left of all CTRLs.
 * @roi:                 Partial update region of interest.
 *                       Origin is top left of this CTRL.
 * @tx_cmd_buf:          Tx command buffer.
 * @cmd_buffer_size:     Size of command buffer.
 * @debugfs_root:        Root for debugfs entries.
@@ -189,6 +194,7 @@ struct dsi_ctrl_interrupts {
struct dsi_ctrl {
	struct platform_device *pdev;
	u32 cell_index;
	u32 horiz_index;
	const char *name;
	u32 refcount;
	struct mutex ctrl_lock;
@@ -209,6 +215,9 @@ struct dsi_ctrl {
	struct dsi_ctrl_bus_scale_info axi_bus_info;

	struct dsi_host_config host_config;
	struct dsi_rect mode_bounds;
	struct dsi_rect roi;

	/* Command tx and rx */
	struct drm_gem_object *tx_cmd_buf;
	u32 cmd_buffer_size;
@@ -387,10 +396,24 @@ int dsi_ctrl_set_ulps(struct dsi_ctrl *dsi_ctrl, bool enable);
 * DSI_CTRL_POWER_CORE_CLK_ON state and after the PHY SW reset has been
 * performed.
 *
 * Also used to program the video mode timing values.
 *
 * Return: error code.
 */
int dsi_ctrl_setup(struct dsi_ctrl *dsi_ctrl);

/**
 * dsi_ctrl_set_roi() - Set DSI controller's region of interest
 * @dsi_ctrl:        DSI controller handle.
 * @roi:             Region of interest rectangle, must be less than mode bounds
 * @changed:         Output parameter, set to true of the controller's ROI was
 *                   dirtied by setting the new ROI, and DCS cmd update needed
 *
 * Return: error code.
 */
int dsi_ctrl_set_roi(struct dsi_ctrl *dsi_ctrl, struct dsi_rect *roi,
		bool *changed);

/**
 * dsi_ctrl_set_tpg_state() - enable/disable test pattern on the controller
 * @dsi_ctrl:          DSI controller handle.
@@ -401,7 +424,6 @@ int dsi_ctrl_setup(struct dsi_ctrl *dsi_ctrl);
 *
 * Return: error code.
 */

int dsi_ctrl_set_tpg_state(struct dsi_ctrl *dsi_ctrl, bool on);

/**
+36 −0
Original line number Diff line number Diff line
@@ -411,4 +411,40 @@ struct dsi_display_mode {
	struct msm_mode_info *mode_info;
};

/**
 * struct dsi_rect - dsi rectangle representation
 * Note: sde_rect is also using u16, this must be maintained for memcpy
 */
struct dsi_rect {
	u16 x;
	u16 y;
	u16 w;
	u16 h;
};

/**
 * dsi_rect_intersect - intersect two rectangles
 * @r1: first rectangle
 * @r2: scissor rectangle
 * @result: result rectangle, all 0's on no intersection found
 */
void dsi_rect_intersect(const struct dsi_rect *r1,
		const struct dsi_rect *r2,
		struct dsi_rect *result);

/**
 * dsi_rect_is_equal - compares two rects
 * @r1: rect value to compare
 * @r2: rect value to compare
 *
 * Returns true if the rects are same
 */
static inline bool dsi_rect_is_equal(struct dsi_rect *r1,
		struct dsi_rect *r2)
{
	return r1->x == r2->x && r1->y == r2->y && r1->w == r2->w &&
			r1->h == r2->h;
}


#endif /* _DSI_DEFS_H_ */
+130 −1
Original line number Diff line number Diff line
@@ -38,6 +38,30 @@ static const struct of_device_id dsi_display_dt_match[] = {

static struct dsi_display *main_display;

void dsi_rect_intersect(const struct dsi_rect *r1,
		const struct dsi_rect *r2,
		struct dsi_rect *result)
{
	int l, t, r, b;

	if (!r1 || !r2 || !result)
		return;

	l = max(r1->x, r2->x);
	t = max(r1->y, r2->y);
	r = min((r1->x + r1->w), (r2->x + r2->w));
	b = min((r1->y + r1->h), (r2->y + r2->h));

	if (r <= l || b <= t) {
		memset(result, 0, sizeof(*result));
	} else {
		result->x = l;
		result->y = t;
		result->w = r - l;
		result->h = b - t;
	}
}

int dsi_display_set_backlight(void *display, u32 bl_lvl)
{
	struct dsi_display *dsi_display = display;
@@ -2285,6 +2309,7 @@ static int dsi_display_bind(struct device *dev,
			       display->name, i, rc);
			goto error_ctrl_deinit;
		}
		display_ctrl->ctrl->horiz_index = i;

		rc = dsi_phy_drv_init(display_ctrl->phy);
		if (rc) {
@@ -2748,6 +2773,10 @@ int dsi_display_get_info(struct msm_display_info *info, void *disp)
				display->panel->mode.panel_mode);
		break;
	}

	memcpy(&info->roi_caps, &display->panel->roi_caps,
			sizeof(info->roi_caps));

error:
	mutex_unlock(&display->display_lock);
	return rc;
@@ -3034,10 +3063,110 @@ int dsi_display_prepare(struct dsi_display *display)
	return rc;
}

static int dsi_display_calc_ctrl_roi(const struct dsi_display *display,
		const struct dsi_display_ctrl *ctrl,
		const struct msm_roi_list *req_rois,
		struct dsi_rect *out_roi)
{
	const struct dsi_rect *bounds = &ctrl->ctrl->mode_bounds;
	struct dsi_rect req_roi = { 0 };
	int rc = 0;

	if (req_rois->num_rects > display->panel->roi_caps.num_roi) {
		pr_err("request for %d rois greater than max %d\n",
				req_rois->num_rects,
				display->panel->roi_caps.num_roi);
		rc = -EINVAL;
		goto exit;
	}

	/**
	 * if no rois, user wants to reset back to full resolution
	 * note: h_active is already divided by ctrl_count
	 */
	if (!req_rois->num_rects) {
		*out_roi = *bounds;
		goto exit;
	}

	/* intersect with the bounds */
	req_roi.x = req_rois->roi[0].x1;
	req_roi.y = req_rois->roi[0].y1;
	req_roi.w = req_rois->roi[0].x2 - req_rois->roi[0].x1;
	req_roi.h = req_rois->roi[0].y2 - req_rois->roi[0].y1;
	dsi_rect_intersect(&req_roi, bounds, out_roi);

exit:
	/* adjust the ctrl origin to be top left within the ctrl */
	out_roi->x = out_roi->x - bounds->x;

	pr_debug("ctrl%d:%d: req (%d,%d,%d,%d) bnd (%d,%d,%d,%d) out (%d,%d,%d,%d)\n",
			ctrl->dsi_ctrl_idx, ctrl->ctrl->cell_index,
			req_roi.x, req_roi.y, req_roi.w, req_roi.h,
			bounds->x, bounds->y, bounds->w, bounds->h,
			out_roi->x, out_roi->y, out_roi->w, out_roi->h);

	return rc;
}

static int dsi_display_set_roi(struct dsi_display *display,
		struct msm_roi_list *rois)
{
	int rc = 0;
	int i;

	if (!display || !rois || !display->panel)
		return -EINVAL;

	if (!display->panel->roi_caps.enabled)
		return 0;

	for (i = 0; i < display->ctrl_count; i++) {
		struct dsi_display_ctrl *ctrl = &display->ctrl[i];
		struct dsi_rect ctrl_roi;
		bool changed = false;

		rc = dsi_display_calc_ctrl_roi(display, ctrl, rois, &ctrl_roi);
		if (rc) {
			pr_err("dsi_display_calc_ctrl_roi failed rc %d\n", rc);
			return rc;
		}

		rc = dsi_ctrl_set_roi(ctrl->ctrl, &ctrl_roi, &changed);
		if (rc) {
			pr_err("dsi_ctrl_set_roi failed rc %d\n", rc);
			return rc;
		}

		if (!changed)
			continue;

		/* send the new roi to the panel via dcs commands */
		rc = dsi_panel_send_roi_dcs(display->panel, i, &ctrl_roi);
		if (rc) {
			pr_err("dsi_panel_set_roi failed rc %d\n", rc);
			return rc;
		}

		/* re-program the ctrl with the timing based on the new roi */
		rc = dsi_ctrl_setup(ctrl->ctrl);
		if (rc) {
			pr_err("dsi_ctrl_setup failed rc %d\n", rc);
			return rc;
		}
	}

	return rc;
}

int dsi_display_pre_kickoff(struct dsi_display *display,
		struct msm_display_kickoff_params *params)
{
	return 0;
	int rc = 0;

	rc = dsi_display_set_roi(display, params->rois);

	return rc;
}

int dsi_display_enable(struct dsi_display *display)
Loading