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

Commit 29f50667 authored by Ajay Singh Parmar's avatar Ajay Singh Parmar
Browse files

msm: mdss: hdmi: protect hpd power from multi-thread access



HPD (Hot Plug Detect) can be powered on or off by down stream
device like MHL or slimport driver based on cable connection
triggered by user. In response to that, HDMI transmitter power
on and off is triggered in a different thread. There can be
a race condition with transmitter power on/off and HPD power
on and off with fast plug in/out. Protect these functionalities
using mutex to avoid such race conditions and keep the HDMI state
machine in valid state.

Change-Id: I7fa8de10b96ca3cf674a0b7cf83b0f96cc177509
Signed-off-by: default avatarAjay Singh Parmar <aparmar@codeaurora.org>
parent ef78af1b
Loading
Loading
Loading
Loading
+19 −26
Original line number Diff line number Diff line
@@ -887,6 +887,8 @@ static ssize_t hdmi_tx_sysfs_wta_hpd(struct device *dev,
			goto end;
		}

		hdmi_ctrl->audio_ack_enabled = false;

		hdmi_tx_set_audio_switch_node(hdmi_ctrl, 0);
		hdmi_tx_wait_for_audio_engine(hdmi_ctrl);
		hdmi_tx_send_cable_notification(hdmi_ctrl, 0);
@@ -1944,7 +1946,7 @@ static void hdmi_tx_hpd_int_work(struct work_struct *work)
	struct dss_io_data *io;

	hdmi_ctrl = container_of(work, struct hdmi_tx_ctrl, hpd_int_work);
	if (!hdmi_ctrl || !hdmi_ctrl->hpd_initialized) {
	if (!hdmi_ctrl) {
		DEV_DBG("%s: invalid input\n", __func__);
		return;
	}
@@ -1952,6 +1954,11 @@ static void hdmi_tx_hpd_int_work(struct work_struct *work)

	mutex_lock(&hdmi_ctrl->tx_lock);

	if (!hdmi_ctrl->hpd_initialized) {
		DEV_DBG("hpd not initialized\n");
		goto end;
	}

	DEV_DBG("%s: %s\n", __func__,
		hdmi_ctrl->hpd_state ? "CONNECT" : "DISCONNECT");

@@ -1978,9 +1985,6 @@ static void hdmi_tx_hpd_int_work(struct work_struct *work)
		hdmi_tx_send_cable_notification(hdmi_ctrl, false);
	}
end:
	if (!completion_done(&hdmi_ctrl->hpd_int_done))
		complete_all(&hdmi_ctrl->hpd_int_done);

	mutex_unlock(&hdmi_ctrl->tx_lock);
} /* hdmi_tx_hpd_int_work */

@@ -3964,9 +3968,6 @@ static void hdmi_tx_hpd_off(struct hdmi_tx_ctrl *hdmi_ctrl)
	hdmi_ctrl->hpd_initialized = false;
	hdmi_ctrl->hpd_off_pending = false;

	if (!completion_done(&hdmi_ctrl->hpd_off_done))
		complete_all(&hdmi_ctrl->hpd_off_done);

	DEV_DBG("%s: HPD is now OFF\n", __func__);
} /* hdmi_tx_hpd_off */

@@ -4039,21 +4040,9 @@ static int hdmi_tx_sysfs_enable_hpd(struct hdmi_tx_ctrl *hdmi_ctrl, int on)
		return -EINVAL;
	}

	DEV_INFO("%s: %d\n", __func__, on);
	DEV_DBG("%s: %d\n", __func__, on);
	if (on) {
		if (hdmi_ctrl->hpd_off_pending) {
			u32 timeout;

			reinit_completion(&hdmi_ctrl->hpd_off_done);
			timeout = wait_for_completion_timeout(
				&hdmi_ctrl->hpd_off_done, HZ);
			if (!timeout) {
		hdmi_ctrl->hpd_off_pending = false;
				DEV_ERR("%s: hpd off still pending\n",
					__func__);
				return 0;
			}
		}

		rc = hdmi_tx_hpd_on(hdmi_ctrl);
	} else {
@@ -4078,6 +4067,8 @@ static int hdmi_tx_set_mhl_hpd(struct platform_device *pdev, uint8_t on)
		return -EINVAL;
	}

	mutex_lock(&hdmi_ctrl->tx_lock);

	/* mhl status should override */
	hdmi_ctrl->mhl_hpd_on = on;

@@ -4088,7 +4079,7 @@ static int hdmi_tx_set_mhl_hpd(struct platform_device *pdev, uint8_t on)
	} else {
		DEV_DBG("%s: hpd is already '%s'. return\n", __func__,
			hdmi_ctrl->hpd_feature_on ? "enabled" : "disabled");
		return rc;
		goto end;
	}

	if (!rc) {
@@ -4099,9 +4090,9 @@ static int hdmi_tx_set_mhl_hpd(struct platform_device *pdev, uint8_t on)
		DEV_ERR("%s: failed to '%s' hpd. rc = %d\n", __func__,
			on ? "enable" : "disable", rc);
	}

end:
	mutex_unlock(&hdmi_ctrl->tx_lock);
	return rc;

}

static irqreturn_t hdmi_tx_isr(int irq, void *data)
@@ -4131,6 +4122,9 @@ static irqreturn_t hdmi_tx_isr(int irq, void *data)
			(DSS_REG_R(io, HDMI_HPD_INT_STATUS) & BIT(1)) >> 1;
		spin_unlock_irqrestore(&hdmi_ctrl->hpd_state_lock, flags);

		if (!completion_done(&hdmi_ctrl->hpd_int_done))
			complete_all(&hdmi_ctrl->hpd_int_done);

		/*
		 * check if this is a spurious interrupt, if yes, reset
		 * interrupts and return
@@ -4272,7 +4266,6 @@ static int hdmi_tx_dev_init(struct hdmi_tx_ctrl *hdmi_ctrl)
	hdmi_ctrl->hpd_initialized = false;
	hdmi_ctrl->hpd_off_pending = false;
	init_completion(&hdmi_ctrl->hpd_int_done);
	init_completion(&hdmi_ctrl->hpd_off_done);

	INIT_WORK(&hdmi_ctrl->hpd_int_work, hdmi_tx_hpd_int_work);
	INIT_WORK(&hdmi_ctrl->cable_notify_work, hdmi_tx_cable_notify_work);
@@ -4516,7 +4509,7 @@ static int hdmi_tx_panel_event_handler(struct mdss_panel_data *panel_data,
		if (!hdmi_ctrl->hpd_feature_on)
			goto end;

		if (!hdmi_ctrl->panel_power_on)
		if (!hdmi_ctrl->hpd_state && !hdmi_ctrl->panel_power_on)
			hdmi_tx_hpd_off(hdmi_ctrl);

		hdmi_ctrl->panel_suspend = true;
+0 −1
Original line number Diff line number Diff line
@@ -157,7 +157,6 @@ struct hdmi_tx_ctrl {

	struct hdmi_util_ds_data ds_data;
	struct completion hpd_int_done;
	struct completion hpd_off_done;
	struct work_struct hpd_int_work;
	struct delayed_work hdcp_cb_work;