drm/msm/dp: listen to USB events to terminate simulation

In case USB cable is removed while DP simulation is running,
as USB may re-program PHY, DP hardware may go into bad state.
Listen to USB cable notifications with high priority. If
simulation is running and cable is removed, tear down the
DP session before USB re-programs the PHY.

CRs-Fixed: 2338665
Change-Id: I55955583dd44cb42c778c2ab32a190ddd9a7e72e
Signed-off-by: Ajay Singh Parmar <aparmar@codeaurora.org>
tirimbino
Ajay Singh Parmar 6 years ago
parent 8a12c558c8
commit 9299070a0e
  1. 4
      drivers/gpu/drm/msm/dp/dp_audio.c
  2. 101
      drivers/gpu/drm/msm/dp/dp_debug.c
  3. 1
      drivers/gpu/drm/msm/dp/dp_debug.h
  4. 104
      drivers/gpu/drm/msm/dp/dp_display.c
  5. 1
      drivers/gpu/drm/msm/dp/dp_panel.c

@ -679,10 +679,8 @@ static int dp_audio_notify(struct dp_audio_private *audio, u32 state)
reinit_completion(&audio->hpd_comp);
rc = ext->intf_ops.audio_notify(audio->ext_pdev,
&ext->codec, state);
if (rc) {
pr_err("failed to notify audio. state=%d err=%d\n", state, rc);
if (rc)
goto end;
}
if (atomic_read(&audio->acked))
goto end;

@ -163,6 +163,13 @@ bail:
kfree(buf);
debug->panel->set_edid(debug->panel, edid);
/*
* print edid status as this code is executed
* only while running in debug mode which is manually
* triggered by a tester or a script.
*/
pr_info("[%s]\n", edid ? "SET" : "CLEAR");
return rc;
}
@ -250,10 +257,18 @@ bail:
* Reset panel's dpcd in case of any failure. Also, set the
* panel's dpcd only if a full dpcd is provided with offset as 0.
*/
if (!dpcd || (!offset && (data_len == dp_receiver_cap_size)))
if (!dpcd || (!offset && (data_len == dp_receiver_cap_size))) {
debug->panel->set_dpcd(debug->panel, dpcd);
else
/*
* print dpcd status as this code is executed
* only while running in debug mode which is manually
* triggered by a tester or a script.
*/
pr_info("[%s]\n", dpcd ? "SET" : "CLEAR");
} else {
debug->aux->dpcd_updated(debug->aux);
}
return rc;
}
@ -1392,36 +1407,16 @@ error:
return rc;
}
static ssize_t dp_debug_write_sim(struct file *file,
const char __user *user_buff, size_t count, loff_t *ppos)
static void dp_debug_set_sim_mode(struct dp_debug_private *debug, bool sim)
{
struct dp_debug_private *debug = file->private_data;
char buf[SZ_8];
size_t len = 0;
int sim;
if (!debug)
return -ENODEV;
if (*ppos)
return 0;
/* Leave room for termination char */
len = min_t(size_t, count, SZ_8 - 1);
if (copy_from_user(buf, user_buff, len))
goto end;
buf[len] = '\0';
if (kstrtoint(buf, 10, &sim) != 0)
goto end;
if (sim) {
if (dp_debug_get_edid_buf(debug))
goto end;
return;
if (dp_debug_get_dpcd_buf(debug))
goto error;
if (dp_debug_get_dpcd_buf(debug)) {
devm_kfree(debug->dev, debug->edid);
return;
}
debug->dp_debug.sim_mode = true;
debug->aux->set_sim_mode(debug->aux, true,
@ -1442,11 +1437,42 @@ static ssize_t dp_debug_write_sim(struct file *file,
debug->dpcd = NULL;
}
}
/*
* print simulation status as this code is executed
* only while running in debug mode which is manually
* triggered by a tester or a script.
*/
pr_info("%s\n", sim ? "[ON]" : "[OFF]");
}
static ssize_t dp_debug_write_sim(struct file *file,
const char __user *user_buff, size_t count, loff_t *ppos)
{
struct dp_debug_private *debug = file->private_data;
char buf[SZ_8];
size_t len = 0;
int sim;
if (!debug)
return -ENODEV;
if (*ppos)
return 0;
/* Leave room for termination char */
len = min_t(size_t, count, SZ_8 - 1);
if (copy_from_user(buf, user_buff, len))
goto end;
buf[len] = '\0';
if (kstrtoint(buf, 10, &sim) != 0)
goto end;
dp_debug_set_sim_mode(debug, sim);
end:
return len;
error:
devm_kfree(debug->dev, debug->edid);
return len;
}
static ssize_t dp_debug_write_attention(struct file *file,
@ -1898,6 +1924,18 @@ u8 *dp_debug_get_edid(struct dp_debug *dp_debug)
return debug->edid;
}
static void dp_debug_abort(struct dp_debug *dp_debug)
{
struct dp_debug_private *debug;
if (!dp_debug)
return;
debug = container_of(dp_debug, struct dp_debug_private, dp_debug);
dp_debug_set_sim_mode(debug, false);
}
struct dp_debug *dp_debug_get(struct dp_debug_in *in)
{
int rc = 0;
@ -1938,6 +1976,7 @@ struct dp_debug *dp_debug_get(struct dp_debug_in *in)
}
dp_debug->get_edid = dp_debug_get_edid;
dp_debug->abort = dp_debug_abort;
INIT_LIST_HEAD(&dp_debug->dp_mst_connector_list.list);

@ -47,6 +47,7 @@ struct dp_debug {
struct dp_mst_connector dp_mst_connector_list;
u8 *(*get_edid)(struct dp_debug *dp_debug);
void (*abort)(struct dp_debug *dp_debug);
};
/**

@ -20,6 +20,7 @@
#include <linux/debugfs.h>
#include <linux/component.h>
#include <linux/of_irq.h>
#include <linux/extcon.h>
#include <linux/soc/qcom/fsa4480-i2c.h>
#include "sde_connector.h"
@ -107,6 +108,8 @@ struct dp_display_private {
u32 active_stream_cnt;
struct dp_mst mst;
struct notifier_block usb_nb;
};
static const struct of_device_id dp_dt_match[] = {
@ -556,6 +559,9 @@ static int dp_display_send_hpd_notification(struct dp_display_private *dp)
if (hpd && dp->mst.mst_active)
goto skip_wait;
if (!dp->mst.mst_active && (dp->power_on == hpd))
goto skip_wait;
if (!wait_for_completion_timeout(&dp->notification_comp,
HZ * timeout_sec)) {
pr_warn("%s timeout\n", hpd ? "connect" : "disconnect");
@ -816,6 +822,24 @@ static int dp_display_handle_disconnect(struct dp_display_private *dp)
return rc;
}
static void dp_display_disconnect_sync(struct dp_display_private *dp)
{
/* cancel any pending request */
atomic_set(&dp->aborted, 1);
dp->ctrl->abort(dp->ctrl);
dp->aux->abort(dp->aux);
/* wait for idle state */
cancel_delayed_work(&dp->connect_work);
cancel_work(&dp->attention_work);
flush_workqueue(dp->wq);
dp_display_handle_disconnect(dp);
/* Reset abort value to allow future connections */
atomic_set(&dp->aborted, 0);
}
static int dp_display_usbpd_disconnect_cb(struct device *dev)
{
int rc = 0;
@ -844,21 +868,7 @@ static int dp_display_usbpd_disconnect_cb(struct device *dev)
if (dp->debug->psm_enabled)
dp->link->psm_config(dp->link, &dp->panel->link_info, true);
/* cancel any pending request */
atomic_set(&dp->aborted, 1);
dp->ctrl->abort(dp->ctrl);
dp->aux->abort(dp->aux);
/* wait for idle state */
cancel_delayed_work(&dp->connect_work);
cancel_work(&dp->attention_work);
flush_workqueue(dp->wq);
dp_display_handle_disconnect(dp);
/* Reset abort value to allow future connections */
atomic_set(&dp->aborted, 0);
dp_display_disconnect_sync(dp);
dp->dp_display.post_open = NULL;
end:
return rc;
@ -976,29 +986,14 @@ static int dp_display_usbpd_attention_cb(struct device *dev)
dp->hpd->hpd_irq, dp->hpd->hpd_high,
dp->power_on);
if (!dp->hpd->hpd_high) {
if (!dp->is_connected) {
pr_debug("already disconnected\n");
return 0;
}
/* cancel any pending request */
atomic_set(&dp->aborted, 1);
dp->ctrl->abort(dp->ctrl);
dp->aux->abort(dp->aux);
/* wait for idle state */
cancel_delayed_work(&dp->connect_work);
cancel_work(&dp->attention_work);
flush_workqueue(dp->wq);
dp_display_handle_disconnect(dp);
atomic_set(&dp->aborted, 0);
} else if (dp->hpd->hpd_irq && dp->core_initialized) {
if (!dp->hpd->hpd_high)
dp_display_disconnect_sync(dp);
else if (dp->hpd->hpd_irq && dp->core_initialized)
queue_work(dp->wq, &dp->attention_work);
} else {
else if (!dp->power_on)
queue_delayed_work(dp->wq, &dp->connect_work, 0);
}
else
pr_debug("ignored\n");
return 0;
}
@ -1026,6 +1021,41 @@ static void dp_display_connect_work(struct work_struct *work)
dp->link->send_test_response(dp->link);
}
static int dp_display_usb_notifier(struct notifier_block *nb,
unsigned long event, void *ptr)
{
struct extcon_dev *edev = ptr;
struct dp_display_private *dp = container_of(nb,
struct dp_display_private, usb_nb);
if (!edev)
goto end;
if (!event && dp->debug->sim_mode) {
dp_display_disconnect_sync(dp);
dp->debug->abort(dp->debug);
}
end:
return NOTIFY_DONE;
}
static int dp_display_get_usb_extcon(struct dp_display_private *dp)
{
struct extcon_dev *edev;
int rc;
edev = extcon_get_edev_by_phandle(&dp->pdev->dev, 0);
if (IS_ERR(edev))
return PTR_ERR(edev);
dp->usb_nb.notifier_call = dp_display_usb_notifier;
dp->usb_nb.priority = 2;
rc = extcon_register_notifier(edev, EXTCON_USB, &dp->usb_nb);
if (rc)
pr_err("failed to register for usb event: %d\n", rc);
return rc;
}
static void dp_display_deinit_sub_modules(struct dp_display_private *dp)
{
dp_audio_put(dp->panel->audio);
@ -1195,6 +1225,8 @@ static int dp_init_sub_modules(struct dp_display_private *dp)
dp->debug->hdcp_disabled = hdcp_disabled;
dp_display_update_hdcp_status(dp, true);
dp_display_get_usb_extcon(dp);
return rc;
error_debug:
dp_hpd_put(dp->hpd);

@ -985,6 +985,7 @@ static int dp_panel_set_edid(struct dp_panel *dp_panel, u8 *edid)
panel->custom_edid = true;
} else {
panel->custom_edid = false;
dp_panel->edid_ctrl->edid = NULL;
}
return 0;

Loading…
Cancel
Save