From 9299070a0e62817ee9b446fe9e254b95a8cfd880 Mon Sep 17 00:00:00 2001 From: Ajay Singh Parmar Date: Fri, 26 Oct 2018 20:19:25 -0700 Subject: [PATCH] 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 --- drivers/gpu/drm/msm/dp/dp_audio.c | 4 +- drivers/gpu/drm/msm/dp/dp_debug.c | 101 ++++++++++++++++++--------- drivers/gpu/drm/msm/dp/dp_debug.h | 1 + drivers/gpu/drm/msm/dp/dp_display.c | 104 ++++++++++++++++++---------- drivers/gpu/drm/msm/dp/dp_panel.c | 1 + 5 files changed, 141 insertions(+), 70 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_audio.c b/drivers/gpu/drm/msm/dp/dp_audio.c index 01eee0827a26..8405978fdd13 100644 --- a/drivers/gpu/drm/msm/dp/dp_audio.c +++ b/drivers/gpu/drm/msm/dp/dp_audio.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; diff --git a/drivers/gpu/drm/msm/dp/dp_debug.c b/drivers/gpu/drm/msm/dp/dp_debug.c index 9b56e611d787..a62958741a6c 100644 --- a/drivers/gpu/drm/msm/dp/dp_debug.c +++ b/drivers/gpu/drm/msm/dp/dp_debug.c @@ -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); diff --git a/drivers/gpu/drm/msm/dp/dp_debug.h b/drivers/gpu/drm/msm/dp/dp_debug.h index 340f01546ae8..e7f3e33ab4c1 100644 --- a/drivers/gpu/drm/msm/dp/dp_debug.h +++ b/drivers/gpu/drm/msm/dp/dp_debug.h @@ -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); }; /** diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index 4eace1fa7c7b..e262d91bb0bd 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #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); diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c index 0e3a5a17876f..e860179c2d9e 100644 --- a/drivers/gpu/drm/msm/dp/dp_panel.c +++ b/drivers/gpu/drm/msm/dp/dp_panel.c @@ -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;