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

Commit 79805d69 authored by Dhaval Patel's avatar Dhaval Patel
Browse files

msm: mdss: fix race condition in dsi clk off request



DSI clocks are requested from mdp and dsi for
different use cases. The master clocks(DSI0 clocks)
are refcounted to avoid toggling during slave
clock ON/OFF sequence. Same applies to split controller
clocks if broadcast mode is enabled. Now, mdp client
can remove the dsi client's vote in below case:

- MDP requested clocks in ECG state
   - clk_ctrl has removed both votes from ctrl-0
     using dsi client handler.
- DSI turns ON clock for ctrl-1
   - clk_ctrl turns on clk for ctrl-0 using dsi
     client handler.
   - clk_ctrl increase the ctrl-1 refcount to 1
   - clk_ctrl turns on clk for ctrl-1
- MDP request to turn OFF the clk for ctrl-1
   - clk_ctrl turns on clk for ctrl-0 using dsi
     client handler
   - clk_ctrl increase the ctrl-1 refcount to 2
   - clk_ctrl turns off clk for ctrl-1
   - clk_ctrl turns off clk(twice) for ctrl-0
     using dsi client handler.

This race condition leads to dsi0 clocks off event when
interface is using clocks. This change start tracking
extra vote based on calling client id.

Change-Id: I4812330453dedacd16dad1d920a2bacc3f67042b
Signed-off-by: default avatarDhaval Patel <pdhaval@codeaurora.org>
parent dc9078ea
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -2892,6 +2892,12 @@ static int mdss_dsi_cont_splash_config(struct mdss_panel_info *pinfo,
			mdss_dsi_panel_pwm_enable(ctrl_pdata);
		ctrl_pdata->ctrl_state |= (CTRL_STATE_PANEL_INIT |
			CTRL_STATE_MDP_ACTIVE | CTRL_STATE_DSI_ACTIVE);

		/*
		 * MDP client removes this extra vote during splash reconfigure
		 * for command mode panel from interface. DSI removes the vote
		 * during suspend-resume for video mode panel.
		 */
		if (ctrl_pdata->panel_data.panel_info.type == MIPI_CMD_PANEL)
			clk_handle = ctrl_pdata->mdp_clk_handle;
		else
+2 −1
Original line number Diff line number Diff line
@@ -523,7 +523,8 @@ struct mdss_dsi_ctrl_pdata {
	void *clk_mngr;
	void *dsi_clk_handle;
	void *mdp_clk_handle;
	int m_vote_cnt;
	int m_dsi_vote_cnt;
	int m_mdp_vote_cnt;
	/* debugfs structure */
	struct mdss_dsi_debugfs_info *debugfs_info;

+25 −3
Original line number Diff line number Diff line
@@ -722,8 +722,30 @@ error:
	return rc;
}

bool is_dsi_clk_in_ecg_state(void *client)
{
	struct mdss_dsi_clk_client_info *c = client;
	struct mdss_dsi_clk_mngr *mngr;
	bool is_ecg = false;


	if (!client) {
		pr_err("Invalid client params\n");
		goto end;
	}

	mngr = c->mngr;

	mutex_lock(&mngr->clk_mutex);
	is_ecg = (c->core_clk_state == MDSS_DSI_CLK_EARLY_GATE);
	mutex_unlock(&mngr->clk_mutex);

end:
	return is_ecg;
}

int mdss_dsi_clk_req_state(void *client, enum mdss_dsi_clk_type clk,
			   enum mdss_dsi_clk_state state)
	enum mdss_dsi_clk_state state, u32 index)
{
	int rc = 0;
	struct mdss_dsi_clk_client_info *c = client;
@@ -744,7 +766,7 @@ int mdss_dsi_clk_req_state(void *client, enum mdss_dsi_clk_type clk,
	       c->name, mngr->name, clk, state, c->core_clk_state,
	       c->link_clk_state);

	MDSS_XLOG(clk, state, c->core_clk_state, c->link_clk_state);
	MDSS_XLOG(index, clk, state, c->core_clk_state, c->link_clk_state);
	/*
	 * Refcount handling rules:
	 *	1. Increment refcount whenever ON is called
@@ -810,7 +832,7 @@ int mdss_dsi_clk_req_state(void *client, enum mdss_dsi_clk_type clk,
	pr_debug("[%s]%s: change=%d, Core (ref=%d, state=%d), Link (ref=%d, state=%d)\n",
		 c->name, mngr->name, changed, c->core_refcount,
		 c->core_clk_state, c->link_refcount, c->link_clk_state);
	MDSS_XLOG(clk, state, c->core_clk_state, c->link_clk_state);
	MDSS_XLOG(index, clk, state, c->core_clk_state, c->link_clk_state);

	if (changed) {
		rc = dsi_recheck_clk_state(mngr);
+15 −2
Original line number Diff line number Diff line
/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -194,6 +194,7 @@ int mdss_dsi_clk_deregister(void *client);
 * @client: client handle.
 * @clk: Type of clock requested (enum mdss_dsi_clk_type).
 * @state: clock state requested.
 * @index: controller index.
 *
 * This routine is used to request a new clock state for a specific clock. If
 * turning ON the clocks, this guarantees that clocks will be on before
@@ -203,7 +204,7 @@ int mdss_dsi_clk_deregister(void *client);
 * @return: error code.
 */
int mdss_dsi_clk_req_state(void *client, enum mdss_dsi_clk_type clk,
			   enum mdss_dsi_clk_state state);
	enum mdss_dsi_clk_state state, u32 index);

/**
 * mdss_dsi_clk_set_link_rate() - set clock rate for link clocks
@@ -235,4 +236,16 @@ int mdss_dsi_clk_set_link_rate(void *client, enum mdss_dsi_link_clk_type clk,
 * @return:error code.
 */
int mdss_dsi_clk_force_toggle(void *client, u32 clk);

/**
 * is_dsi_clk_in_ecg_state() - Checks the current state of clocks
 * @client: client handle.
 *
 * This routine returns checks the clocks status for client and return
 * success code based on it.
 *
 * @return:true: if clocks are in ECG state
 *         false: for all other cases
 */
bool is_dsi_clk_in_ecg_state(void *client);
#endif /* _MDSS_DSI_CLK_H_ */
+37 −15
Original line number Diff line number Diff line
@@ -1936,7 +1936,11 @@ int mdss_dsi_clk_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, void *clk_handle,
{
	int rc = 0;
	struct mdss_dsi_ctrl_pdata *mctrl = NULL;
	int i;
	int i, *vote_cnt;

	void *m_clk_handle;
	bool is_ecg = false;
	int state = MDSS_DSI_CLK_OFF;

	if (!ctrl) {
		pr_err("%s: Invalid arg\n", __func__);
@@ -1967,6 +1971,18 @@ int mdss_dsi_clk_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, void *clk_handle,
				__func__);
	}

	/*
	 * it should add and remove extra votes based on voting clients to avoid
	 * removal of legitimate vote from DSI client.
	 */
	if (mctrl && (clk_handle == ctrl->dsi_clk_handle)) {
		m_clk_handle = mctrl->dsi_clk_handle;
		vote_cnt = &mctrl->m_dsi_vote_cnt;
	} else if (mctrl) {
		m_clk_handle = mctrl->mdp_clk_handle;
		vote_cnt = &mctrl->m_mdp_vote_cnt;
	}

	/*
	 * When DSI is used in split mode, the link clock for master controller
	 * has to be turned on first before the link clock for slave can be
@@ -1979,18 +1995,24 @@ int mdss_dsi_clk_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, void *clk_handle,
		 __func__, ctrl->ndx, clk_type, clk_state,
		 __builtin_return_address(0), mctrl ? 1 : 0);
	if (mctrl && (clk_type & MDSS_DSI_LINK_CLK)) {
		rc = mdss_dsi_clk_req_state(mctrl->dsi_clk_handle,
					     MDSS_DSI_ALL_CLKS,
					     MDSS_DSI_CLK_ON);
		if (clk_state != MDSS_DSI_CLK_ON) {
			/* preserve clk state; do not turn off forcefully */
			is_ecg = is_dsi_clk_in_ecg_state(m_clk_handle);
			if (is_ecg)
				state = MDSS_DSI_CLK_EARLY_GATE;
		}

		rc = mdss_dsi_clk_req_state(m_clk_handle,
			MDSS_DSI_ALL_CLKS, MDSS_DSI_CLK_ON, mctrl->ndx);
		if (rc) {
			pr_err("%s: failed to turn on mctrl clocks, rc=%d\n",
				 __func__, rc);
			goto error;
		}
		ctrl->m_vote_cnt++;
		(*vote_cnt)++;
	}

	rc = mdss_dsi_clk_req_state(clk_handle, clk_type, clk_state);
	rc = mdss_dsi_clk_req_state(clk_handle, clk_type, clk_state, ctrl->ndx);
	if (rc) {
		pr_err("%s: failed set clk state, rc = %d\n", __func__, rc);
		goto error;
@@ -2019,24 +2041,24 @@ int mdss_dsi_clk_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, void *clk_handle,
		 *	   for ON, since the previous ECG state must have
		 *	   removed two votes to let clocks turn off.
		 *
		 * To satisfy the above requirement, m_vote_cnt keeps track of
		 * To satisfy the above requirement, vote_cnt keeps track of
		 * the number of ON votes for master requested by slave. For
		 * every OFF/ECG state request, Either 2 or m_vote_cnt number of
		 * every OFF/ECG state request, Either 2 or vote_cnt number of
		 * votes are removed depending on which is lower.
		 */
		for (i = 0; (i < ctrl->m_vote_cnt && i < 2); i++) {
			rc = mdss_dsi_clk_req_state(mctrl->dsi_clk_handle,
						     MDSS_DSI_ALL_CLKS,
						     MDSS_DSI_CLK_OFF);
		for (i = 0; (i < *vote_cnt && i < 2); i++) {
			rc = mdss_dsi_clk_req_state(m_clk_handle,
				MDSS_DSI_ALL_CLKS, state, mctrl->ndx);
			if (rc) {
				pr_err("%s: failed to set mctrl clk state, rc = %d\n",
				       __func__, rc);
				goto error;
			}
		}
		ctrl->m_vote_cnt -= i;
		pr_debug("%s: ctrl=%d, m_vote_cnt=%d\n", __func__, ctrl->ndx,
			 ctrl->m_vote_cnt);
		(*vote_cnt) -= i;
		pr_debug("%s: ctrl=%d, vote_cnt=%d dsi_vote_cnt=%d mdp_vote_cnt:%d\n",
			__func__, ctrl->ndx, *vote_cnt, mctrl->m_dsi_vote_cnt,
			mctrl->m_mdp_vote_cnt);
	}

error: