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: Ajay Singh Parmar <aparmar@codeaurora.org>
This commit is contained in:
Ajay Singh Parmar 2016-02-02 15:01:12 -08:00 committed by David Keitel
parent 62be9c73b9
commit 88af065f71
2 changed files with 19 additions and 27 deletions

View file

@ -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;
}
}
hdmi_ctrl->hpd_off_pending = false;
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;

View file

@ -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;