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

Commit d7efa377 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: mdss: dsi: ensure lanes are idle prior to ULPS entry"

parents 8ba4f0b1 5edf0058
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -570,6 +570,7 @@ void mdss_dsi_clk_req(struct mdss_dsi_ctrl_pdata *ctrl,
void mdss_dsi_controller_cfg(int enable,
				struct mdss_panel_data *pdata);
void mdss_dsi_sw_reset(struct mdss_dsi_ctrl_pdata *ctrl_pdata, bool restore);
int mdss_dsi_wait_for_lane_idle(struct mdss_dsi_ctrl_pdata *ctrl);

irqreturn_t mdss_dsi_isr(int irq, void *ptr);
irqreturn_t hw_vsync_handler(int irq, void *data);
+70 −0
Original line number Diff line number Diff line
@@ -34,6 +34,9 @@
#define DMA_TX_TIMEOUT 200
#define DMA_TPG_FIFO_LEN 64

#define FIFO_STATUS	0x0C
#define LANE_STATUS	0xA8

struct mdss_dsi_ctrl_pdata *ctrl_list[DSI_CTRL_MAX];

struct mdss_hw mdss_dsi0_hw = {
@@ -517,6 +520,73 @@ void mdss_dsi_sw_reset(struct mdss_dsi_ctrl_pdata *ctrl, bool restore)
	spin_unlock_irqrestore(&ctrl->mdp_lock, flag);
}

/**
 * mdss_dsi_wait_for_lane_idle() - Wait for DSI lanes to be idle
 * @ctrl: pointer to DSI controller structure
 *
 * This function waits for all the active DSI lanes to be idle by polling all
 * the *FIFO_EMPTY bits and polling the lane status to ensure that all the lanes
 * are in stop state. This function assumes that the bus clocks required to
 * access the registers are already turned on.
 */
int mdss_dsi_wait_for_lane_idle(struct mdss_dsi_ctrl_pdata *ctrl)
{
	int rc;
	u32 val;
	u32 fifo_empty_mask = 0;
	u32 stop_state_mask = 0;
	struct mipi_panel_info *mipi;
	u32 const sleep_us = 10;
	u32 const timeout_us = 100;

	if (!ctrl) {
		pr_err("%s: invalid input\n", __func__);
		return -EINVAL;
	}

	mipi = &ctrl->panel_data.panel_info.mipi;

	if (mipi->data_lane0) {
		stop_state_mask |= BIT(0);
		fifo_empty_mask |= (BIT(12) | BIT(16));
	}
	if (mipi->data_lane1) {
		stop_state_mask |= BIT(1);
		fifo_empty_mask |= BIT(20);
	}
	if (mipi->data_lane2) {
		stop_state_mask |= BIT(2);
		fifo_empty_mask |= BIT(24);
	}
	if (mipi->data_lane3) {
		stop_state_mask |= BIT(3);
		fifo_empty_mask |= BIT(28);
	}

	pr_debug("%s: polling for fifo empty, mask=0x%08x\n", __func__,
		fifo_empty_mask);
	rc = readl_poll_timeout(ctrl->ctrl_base + FIFO_STATUS, val,
		(val & fifo_empty_mask), sleep_us, timeout_us);
	if (rc) {
		pr_err("%s: fifo not empty, FIFO_STATUS=0x%08x\n",
			__func__, val);
		goto error;
	}

	pr_debug("%s: polling for lanes to be in stop state, mask=0x%08x\n",
		__func__, stop_state_mask);
	rc = readl_poll_timeout(ctrl->ctrl_base + LANE_STATUS, val,
		(val & stop_state_mask), sleep_us, timeout_us);
	if (rc) {
		pr_err("%s: lanes not in stop state, LANE_STATUS=0x%08x\n",
			__func__, val);
		goto error;
	}

error:
	return rc;
}

static void mdss_dsi_cfg_lane_ctrl(struct mdss_dsi_ctrl_pdata *ctrl,
						u32 bits, int set)
{
+95 −17
Original line number Diff line number Diff line
@@ -1502,6 +1502,67 @@ int mdss_dsi_clk_div_config(struct mdss_panel_info *panel_info,
	return 0;
}

static bool mdss_dsi_is_ulps_req_valid(struct mdss_dsi_ctrl_pdata *ctrl,
		int enable)
{
	struct mdss_dsi_ctrl_pdata *octrl = NULL;
	struct mdss_panel_data *pdata = &ctrl->panel_data;
	struct mdss_panel_info *pinfo = &pdata->panel_info;

	pr_debug("%s: checking ulps req validity for ctrl%d\n",
		__func__, ctrl->ndx);

	if (!mdss_dsi_ulps_feature_enabled(pdata) &&
			!pinfo->ulps_suspend_enabled) {
		pr_debug("%s: ULPS feature is not enabled\n", __func__);
		return false;
	}

	/*
	 * No need to enter ULPS when transitioning from splash screen to
	 * boot animation since it is expected that the clocks would be turned
	 * right back on.
	 */
	if (enable && pinfo->cont_splash_enabled) {
		pr_debug("%s: skip ULPS config with splash screen enabled\n",
			__func__);
		return false;
	}

	/*
	 * No need to enable ULPS if panel is not yet initialized.
	 * However, this should be allowed in following usecases:
	 *   1. If ULPS during suspend feature is enabled, where we
	 *      configure the lanes in ULPS after turning off the panel.
	 *   2. When coming out of idle PC with clamps enabled, where we
	 *      transition the controller HW state back to ULPS prior to
	 *      disabling ULPS.
	 */
	if (enable && !ctrl->mmss_clamp &&
		!(ctrl->ctrl_state & CTRL_STATE_PANEL_INIT) &&
		!pdata->panel_info.ulps_suspend_enabled) {
		pr_debug("%s: panel not yet initialized\n", __func__);
		return false;
	}

	/*
	 * For split-DSI usecase, wait till both controllers are initialized.
	 * The same exceptions as above are applicable here too.
	 */
	if (mdss_dsi_is_hw_config_split(ctrl->shared_data)) {
		octrl = mdss_dsi_get_other_ctrl(ctrl);
		if (enable && !ctrl->mmss_clamp && octrl &&
			!(octrl->ctrl_state & CTRL_STATE_PANEL_INIT) &&
			!pdata->panel_info.ulps_suspend_enabled) {
			pr_debug("%s: split-DSI, other ctrl not ready yet\n",
				__func__);
			return false;
		}
	}

	return true;
}

/**
 * mdss_dsi_ulps_config() - Program DSI lanes to enter/exit ULPS mode
 * @ctrl: pointer to DSI controller structure
@@ -1534,20 +1595,9 @@ static int mdss_dsi_ulps_config(struct mdss_dsi_ctrl_pdata *ctrl,
	pinfo = &pdata->panel_info;
	mipi = &pinfo->mipi;

	if (!mdss_dsi_ulps_feature_enabled(pdata) &&
			!pinfo->ulps_suspend_enabled) {
		pr_debug("%s: ULPS feature is not enabled\n", __func__);
		return 0;
	}

	/*
	 * No need to enter ULPS when transitioning from splash screen to
	 * boot animation since it is expected that the clocks would be turned
	 * right back on.
	 */
	if (pinfo->cont_splash_enabled) {
		pr_debug("%s: skip ULPS config with splash screen enabled\n",
			__func__);
	if (!mdss_dsi_is_ulps_req_valid(ctrl, enable)) {
		pr_debug("%s: skiping ULPS config for ctrl%d, enable=%d\n",
			__func__, ctrl->ndx, enable);
		return 0;
	}

@@ -1566,11 +1616,31 @@ static int mdss_dsi_ulps_config(struct mdss_dsi_ctrl_pdata *ctrl,
	if (mipi->data_lane3)
		active_lanes |= BIT(3);

	pr_debug("%s: configuring ulps (%s) for ctrl%d, active lanes=0x%08x\n",
	pr_debug("%s: configuring ulps (%s) for ctrl%d, active lanes=0x%08x,clamps=%s\n",
		__func__, (enable ? "on" : "off"), ctrl->ndx,
		active_lanes);
		active_lanes, ctrl->mmss_clamp ? "enabled" : "disabled");

	if (enable && !ctrl->ulps) {
		/*
		 * Ensure that the lanes are idle prior to placing a ULPS entry
		 * request. This is needed to ensure that there is no overlap
		 * between any HS or LP commands being sent out on the lane and
		 * a potential ULPS entry request.
		 *
		 * This check needs to be avoided when we are resuming from idle
		 * power collapse and just restoring the controller state to
		 * ULPS with the clamps still in place.
		 */
		if (!ctrl->mmss_clamp) {
			ret = mdss_dsi_wait_for_lane_idle(ctrl);
			if (ret) {
				pr_warn("%s: lanes not idle, skip ulps\n",
					__func__);
				ret = 0;
				goto error;
			}
		}

		/*
		 * ULPS Entry Request.
		 * Wait for a short duration to ensure that the lanes
@@ -1913,7 +1983,6 @@ int mdss_dsi_pre_clkoff_cb(void *priv,
	}

	if ((clk & MDSS_DSI_CORE_CLK) && (new_state == MDSS_DSI_CLK_OFF)) {

		/*
		 * Enable DSI clamps only if entering idle power collapse or
		 * when ULPS during suspend is enabled.
@@ -1924,6 +1993,15 @@ int mdss_dsi_pre_clkoff_cb(void *priv,
			if (rc)
				pr_err("%s: Failed to enable dsi clamps. rc=%d\n",
					__func__, rc);
		} else {
			/*
			* Make sure that controller is not in ULPS state when
			* the DSI link is not active.
			*/
			rc = mdss_dsi_ulps_config(ctrl, 0);
			if (rc)
				pr_err("%s: failed to disable ulps. rc=%d\n",
					__func__, rc);
		}
	}