From 81fc1566e23e55669d835755eee1f1c49b995b4c Mon Sep 17 00:00:00 2001 From: Aravind Venkateswaran Date: Fri, 7 Apr 2017 22:25:59 -0700 Subject: [PATCH] msm: mdss: dp: gracefully handle cable disconnect When the downstream DP device is physically disconnected, ensure that all pending events are removed and the event thread is parked. Reset all the software state so that subsequent connection events are handled correctly. CRs-Fixed: 2034023 Change-Id: Ie94b1da903b8e78509220e373bec4ff54026885c Signed-off-by: Aravind Venkateswaran --- drivers/video/fbdev/msm/mdss_dp.c | 80 +++++++++++++++++++++++++------ drivers/video/fbdev/msm/mdss_dp.h | 1 - 2 files changed, 66 insertions(+), 15 deletions(-) diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c index 9684196c04ba..f57a3abf7eca 100644 --- a/drivers/video/fbdev/msm/mdss_dp.c +++ b/drivers/video/fbdev/msm/mdss_dp.c @@ -1055,26 +1055,28 @@ static int mdss_dp_wait4video_ready(struct mdss_dp_drv_pdata *dp_drv) static void mdss_dp_update_cable_status(struct mdss_dp_drv_pdata *dp, bool connected) { - mutex_lock(&dp->pd_msg_mutex); + mutex_lock(&dp->attention_lock); pr_debug("cable_connected to %d\n", connected); if (dp->cable_connected != connected) dp->cable_connected = connected; else pr_debug("no change in cable status\n"); - mutex_unlock(&dp->pd_msg_mutex); + mutex_unlock(&dp->attention_lock); } static int dp_get_cable_status(struct platform_device *pdev, u32 vote) { - struct mdss_dp_drv_pdata *dp_ctrl = platform_get_drvdata(pdev); + struct mdss_dp_drv_pdata *dp = platform_get_drvdata(pdev); u32 hpd; - if (!dp_ctrl) { + if (!dp) { DEV_ERR("%s: invalid input\n", __func__); return -ENODEV; } - hpd = dp_ctrl->cable_connected; + mutex_lock(&dp->attention_lock); + hpd = dp->cable_connected; + mutex_unlock(&dp->attention_lock); return hpd; } @@ -2043,7 +2045,6 @@ static int mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp, bool notify = false; bool connect; - mutex_lock(&dp->pd_msg_mutex); pr_debug("beginning notification\n"); if (status == dp->hpd_notification_status) { pr_debug("No change in status %s --> %s\n", @@ -2135,7 +2136,6 @@ notify: end: dp->hpd_notification_status = status; - mutex_unlock(&dp->pd_msg_mutex); return ret; } @@ -2423,12 +2423,16 @@ static ssize_t mdss_dp_rda_connected(struct device *dev, { ssize_t ret; struct mdss_dp_drv_pdata *dp = mdss_dp_get_drvdata(dev); + bool cable_connected; if (!dp) return -EINVAL; - ret = snprintf(buf, PAGE_SIZE, "%d\n", dp->cable_connected); - pr_debug("%d\n", dp->cable_connected); + mutex_lock(&dp->attention_lock); + cable_connected = dp->cable_connected; + mutex_unlock(&dp->attention_lock); + ret = snprintf(buf, PAGE_SIZE, "%d\n", cable_connected); + pr_debug("%d\n", cable_connected); return ret; } @@ -2597,6 +2601,7 @@ static ssize_t mdss_dp_wta_hpd(struct device *dev, int hpd, rc; ssize_t ret = strnlen(buf, PAGE_SIZE); struct mdss_dp_drv_pdata *dp = mdss_dp_get_drvdata(dev); + bool cable_connected; if (!dp) { pr_err("invalid data\n"); @@ -2612,9 +2617,13 @@ static ssize_t mdss_dp_wta_hpd(struct device *dev, } dp->hpd = !!hpd; - pr_debug("hpd=%d\n", dp->hpd); + mutex_lock(&dp->attention_lock); + cable_connected = dp->cable_connected; + mutex_unlock(&dp->attention_lock); + pr_debug("hpd=%d cable_connected=%s\n", dp->hpd, + cable_connected ? "true" : "false"); - if (dp->hpd && dp->cable_connected) { + if (dp->hpd && cable_connected) { if (dp->alt_mode.current_state & DP_CONFIGURE_DONE) { mdss_dp_host_init(&dp->panel_data); mdss_dp_process_hpd_high(dp); @@ -3248,10 +3257,21 @@ static int mdss_dp_event_thread(void *data) ev_data = (struct mdss_dp_event_data *)data; + pr_debug("starting\n"); while (!kthread_should_stop()) { wait_event(ev_data->event_q, (ev_data->pndx != ev_data->gndx) || - kthread_should_stop()); + kthread_should_stop() || + kthread_should_park()); + if (kthread_should_stop()) + return 0; + + if (kthread_should_park()) { + pr_debug("parking event thread\n"); + kthread_parkme(); + continue; + } + spin_lock_irqsave(&ev_data->event_lock, flag); ev = &(ev_data->event_list[ev_data->gndx++]); todo = ev->id; @@ -3427,6 +3447,27 @@ static int mdss_dp_event_setup(struct mdss_dp_drv_pdata *dp) return 0; } +static void mdss_dp_reset_event_list(struct mdss_dp_drv_pdata *dp) +{ + struct mdss_dp_event_data *ev_data = &dp->dp_event; + + spin_lock(&ev_data->event_lock); + ev_data->pndx = ev_data->gndx = 0; + spin_unlock(&ev_data->event_lock); + + mutex_lock(&dp->attention_lock); + INIT_LIST_HEAD(&dp->attention_head); + mutex_unlock(&dp->attention_lock); +} + +static void mdss_dp_reset_sw_state(struct mdss_dp_drv_pdata *dp) +{ + pr_debug("enter\n"); + mdss_dp_reset_event_list(dp); + atomic_set(&dp->notification_pending, 0); + complete_all(&dp->notification_comp); +} + static void usbpd_connect_callback(struct usbpd_svid_handler *hdlr) { struct mdss_dp_drv_pdata *dp_drv; @@ -3438,6 +3479,8 @@ static void usbpd_connect_callback(struct usbpd_svid_handler *hdlr) } mdss_dp_update_cable_status(dp_drv, true); + if (dp_drv->ev_thread) + kthread_unpark(dp_drv->ev_thread); if (dp_drv->hpd) dp_send_events(dp_drv, EV_USBPD_DISCOVER_MODES); @@ -3457,6 +3500,9 @@ static void usbpd_disconnect_callback(struct usbpd_svid_handler *hdlr) mdss_dp_update_cable_status(dp_drv, false); dp_drv->alt_mode.current_state = UNKNOWN_STATE; + mdss_dp_reset_sw_state(dp_drv); + kthread_park(dp_drv->ev_thread); + /** * Manually turn off the DP controller if we are in PHY * testing mode. @@ -3975,7 +4021,8 @@ static void usbpd_response_callback(struct usbpd_svid_handler *hdlr, u8 cmd, node->vdo = *vdos; mutex_lock(&dp_drv->attention_lock); - list_add_tail(&node->list, &dp_drv->attention_head); + if (dp_drv->cable_connected) + list_add_tail(&node->list, &dp_drv->attention_head); mutex_unlock(&dp_drv->attention_lock); dp_send_events(dp_drv, EV_USBPD_ATTENTION); @@ -4093,6 +4140,11 @@ static void mdss_dp_handle_attention(struct mdss_dp_drv_pdata *dp) reinit_completion(&dp->notification_comp); mutex_lock(&dp->attention_lock); + if (!dp->cable_connected) { + pr_debug("cable disconnected, returning\n"); + mutex_unlock(&dp->attention_lock); + goto exit; + } node = list_first_entry(&dp->attention_head, struct mdss_dp_attention_node, list); @@ -4124,6 +4176,7 @@ static void mdss_dp_handle_attention(struct mdss_dp_drv_pdata *dp) pr_debug("done processing item %d in the list\n", i); }; +exit: pr_debug("exit\n"); } @@ -4199,7 +4252,6 @@ static int mdss_dp_probe(struct platform_device *pdev) dp_drv->mask1 = EDP_INTR_MASK1; dp_drv->mask2 = EDP_INTR_MASK2; mutex_init(&dp_drv->emutex); - mutex_init(&dp_drv->pd_msg_mutex); mutex_init(&dp_drv->attention_lock); mutex_init(&dp_drv->hdcp_mutex); spin_lock_init(&dp_drv->lock); diff --git a/drivers/video/fbdev/msm/mdss_dp.h b/drivers/video/fbdev/msm/mdss_dp.h index e9f5a04e2d19..f358aad8a667 100644 --- a/drivers/video/fbdev/msm/mdss_dp.h +++ b/drivers/video/fbdev/msm/mdss_dp.h @@ -617,7 +617,6 @@ struct mdss_dp_drv_pdata { struct completion notification_comp; struct mutex aux_mutex; struct mutex train_mutex; - struct mutex pd_msg_mutex; struct mutex attention_lock; struct mutex hdcp_mutex; bool cable_connected;