From 3e26909b0a1a110c1562d1fcbd14f083062afe49 Mon Sep 17 00:00:00 2001 From: Tatenda Chipeperekwa Date: Fri, 8 Nov 2019 12:55:44 +0530 Subject: [PATCH] disp: msm: dp: abort aux before going to dp suspend During resume, DP uses AUX to perform various functionalities like DPCD/EDID read or link training etc. This involves other hardware modules as well like USB and Charger. In a situation like continuous suspend/resume, while DP is processing resume, suspend can trigger resulting in dependent hardware modules to go to sleep. As AUX communication is hardware interrupt based, this can result in unstable system. Abort all functionalities before going to suspend to avoid unnecessary AUX and other functionality failures. Change-Id: Id52d408270232adf7258a7eb064ee969eba4be71 Signed-off-by: Sankeerth Billakanti Signed-off-by: Tatenda Chipeperekwa --- msm/dp/dp_aux.c | 4 ++-- msm/dp/dp_aux.h | 2 +- msm/dp/dp_ctrl.c | 4 ++-- msm/dp/dp_ctrl.h | 4 ++-- msm/dp/dp_debug.c | 4 ++-- msm/dp/dp_display.c | 40 ++++++++++++++++++++++++++++++++++++---- 6 files changed, 45 insertions(+), 13 deletions(-) diff --git a/msm/dp/dp_aux.c b/msm/dp/dp_aux.c index 907ccdb0b0..7d73abe4f4 100644 --- a/msm/dp/dp_aux.c +++ b/msm/dp/dp_aux.c @@ -310,7 +310,7 @@ static void dp_aux_reconfig(struct dp_aux *dp_aux) aux->catalog->reset(aux->catalog); } -static void dp_aux_abort_transaction(struct dp_aux *dp_aux) +static void dp_aux_abort_transaction(struct dp_aux *dp_aux, bool abort) { struct dp_aux_private *aux; @@ -321,7 +321,7 @@ static void dp_aux_abort_transaction(struct dp_aux *dp_aux) aux = container_of(dp_aux, struct dp_aux_private, dp_aux); - atomic_set(&aux->aborted, 1); + atomic_set(&aux->aborted, abort); } static void dp_aux_update_offset_and_segment(struct dp_aux_private *aux, diff --git a/msm/dp/dp_aux.h b/msm/dp/dp_aux.h index e7339fc35b..850941b1e5 100644 --- a/msm/dp/dp_aux.h +++ b/msm/dp/dp_aux.h @@ -50,7 +50,7 @@ struct dp_aux { void (*init)(struct dp_aux *aux, struct dp_aux_cfg *aux_cfg); void (*deinit)(struct dp_aux *aux); void (*reconfig)(struct dp_aux *aux); - void (*abort)(struct dp_aux *aux); + void (*abort)(struct dp_aux *aux, bool abort); void (*dpcd_updated)(struct dp_aux *aux); void (*set_sim_mode)(struct dp_aux *aux, bool en, u8 *edid, u8 *dpcd); int (*aux_switch)(struct dp_aux *aux, bool enable, int orientation); diff --git a/msm/dp/dp_ctrl.c b/msm/dp/dp_ctrl.c index be90c50c0e..1b81192c02 100644 --- a/msm/dp/dp_ctrl.c +++ b/msm/dp/dp_ctrl.c @@ -102,7 +102,7 @@ static void dp_ctrl_video_ready(struct dp_ctrl_private *ctrl) complete(&ctrl->video_comp); } -static void dp_ctrl_abort(struct dp_ctrl *dp_ctrl) +static void dp_ctrl_abort(struct dp_ctrl *dp_ctrl, bool abort) { struct dp_ctrl_private *ctrl; @@ -113,7 +113,7 @@ static void dp_ctrl_abort(struct dp_ctrl *dp_ctrl) ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl); - atomic_set(&ctrl->aborted, 1); + atomic_set(&ctrl->aborted, abort); } static void dp_ctrl_state_ctrl(struct dp_ctrl_private *ctrl, u32 state) diff --git a/msm/dp/dp_ctrl.h b/msm/dp/dp_ctrl.h index ac6af632a3..50b3d48ab9 100644 --- a/msm/dp/dp_ctrl.h +++ b/msm/dp/dp_ctrl.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. */ #ifndef _DP_CTRL_H_ @@ -19,7 +19,7 @@ struct dp_ctrl { int (*on)(struct dp_ctrl *dp_ctrl, bool mst_mode, bool fec_en, bool dsc_en, bool shallow); void (*off)(struct dp_ctrl *dp_ctrl); - void (*abort)(struct dp_ctrl *dp_ctrl); + void (*abort)(struct dp_ctrl *dp_ctrl, bool abort); void (*isr)(struct dp_ctrl *dp_ctrl); bool (*handle_sink_request)(struct dp_ctrl *dp_ctrl); void (*process_phy_test_request)(struct dp_ctrl *dp_ctrl); diff --git a/msm/dp/dp_debug.c b/msm/dp/dp_debug.c index 3ddb4d9f69..54cdb3c008 100644 --- a/msm/dp/dp_debug.c +++ b/msm/dp/dp_debug.c @@ -1596,8 +1596,8 @@ static void dp_debug_set_sim_mode(struct dp_debug_private *debug, bool sim) debug->hpd->simulate_connect(debug->hpd, false); debug->hotplug = false; } - debug->aux->abort(debug->aux); - debug->ctrl->abort(debug->ctrl); + debug->aux->abort(debug->aux, true); + debug->ctrl->abort(debug->ctrl, true); debug->aux->set_sim_mode(debug->aux, false, NULL, NULL); debug->dp_debug.sim_mode = false; diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index e027060640..ce58c17fe1 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -1155,8 +1155,8 @@ static int dp_display_handle_disconnect(struct dp_display_private *dp) rc = dp_display_process_hpd_low(dp); if (rc) { /* cancel any pending request */ - dp->ctrl->abort(dp->ctrl); - dp->aux->abort(dp->aux); + dp->ctrl->abort(dp->ctrl, true); + dp->aux->abort(dp->aux, true); } mutex_lock(&dp->session_lock); @@ -1177,8 +1177,8 @@ static void dp_display_disconnect_sync(struct dp_display_private *dp) /* cancel any pending request */ dp_display_state_add(DP_STATE_ABORTED); - dp->ctrl->abort(dp->ctrl); - dp->aux->abort(dp->aux); + dp->ctrl->abort(dp->ctrl, true); + dp->aux->abort(dp->aux, true); /* wait for idle state */ cancel_work_sync(&dp->connect_work); @@ -3104,9 +3104,25 @@ static int dp_pm_prepare(struct device *dev) struct dp_display_private *dp = container_of(g_dp_display, struct dp_display_private, dp_display); + mutex_lock(&dp->session_lock); dp_display_set_mst_state(g_dp_display, PM_SUSPEND); + /* + * There are a few instances where the DP is hotplugged when the device + * is in PM suspend state. After hotplug, it is observed the device + * enters and exits the PM suspend multiple times while aux transactions + * are taking place. This may sometimes cause an unclocked register + * access error. So, abort aux transactions when such a situation + * arises i.e. when DP is connected but display not enabled yet. + */ + if (dp_display_state_is(DP_STATE_CONNECTED) && + !dp_display_state_is(DP_STATE_ENABLED)) { + dp->aux->abort(dp->aux, true); + dp->ctrl->abort(dp->ctrl, true); + } + dp_display_state_add(DP_STATE_SUSPENDED); + mutex_unlock(&dp->session_lock); return 0; } @@ -3116,9 +3132,25 @@ static void dp_pm_complete(struct device *dev) struct dp_display_private *dp = container_of(g_dp_display, struct dp_display_private, dp_display); + mutex_lock(&dp->session_lock); dp_display_set_mst_state(g_dp_display, PM_DEFAULT); + /* + * There are multiple PM suspend entry and exits observed before + * the connect uevent is issued to userspace. The aux transactions are + * aborted during PM suspend entry in dp_pm_prepare to prevent unclocked + * register access. On PM suspend exit, there will be no host_init call + * to reset the abort flags for ctrl and aux incase DP is connected + * but display not enabled. So, resetting abort flags for aux and ctrl. + */ + if (dp_display_state_is(DP_STATE_CONNECTED) && + !dp_display_state_is(DP_STATE_ENABLED)) { + dp->aux->abort(dp->aux, false); + dp->ctrl->abort(dp->ctrl, false); + } + dp_display_state_remove(DP_STATE_SUSPENDED); + mutex_unlock(&dp->session_lock); } static const struct dev_pm_ops dp_pm_ops = {