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 = {