From patchwork Mon Dec 28 12:41:27 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Greg Kroah-Hartman X-Patchwork-Id: 354975 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-18.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER, INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0AB73C4321A for ; Mon, 28 Dec 2020 14:03:17 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id D1726208B3 for ; Mon, 28 Dec 2020 14:03:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2408227AbgL1ODO (ORCPT ); Mon, 28 Dec 2020 09:03:14 -0500 Received: from mail.kernel.org ([198.145.29.99]:35908 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2437101AbgL1ODN (ORCPT ); Mon, 28 Dec 2020 09:03:13 -0500 Received: by mail.kernel.org (Postfix) with ESMTPSA id 46B81206E5; Mon, 28 Dec 2020 14:02:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=linuxfoundation.org; s=korg; t=1609164177; bh=YMi6WXVbxDbWHpzfwMiymrbNowyh1xpo2nZlu2M3WXQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ZcYhUpcnClcz/yzJf3h/y88aG4GIajmNV5tHT9fYXPUZ0K6A/AIrvK97IKfqjHneD X2cNsbO2H9+j0l1559XSpnW0BJykVACCE89nlhrHq7CJ3H7zEzlVXGYst/y5Dk/ZnA Iom6RX986oXWrdhgDOVkgDLk8Qzfo8YH7wcZdKMY= From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, Stephen Boyd , Kuogee Hsieh , Rob Clark , Sasha Levin Subject: [PATCH 5.10 089/717] drm/msm/dp: return correct connection status after suspend Date: Mon, 28 Dec 2020 13:41:27 +0100 Message-Id: <20201228125025.241228629@linuxfoundation.org> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20201228125020.963311703@linuxfoundation.org> References: <20201228125020.963311703@linuxfoundation.org> User-Agent: quilt/0.66 MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: stable@vger.kernel.org From: Kuogee Hsieh [ Upstream commit 19e52bcb27c2ba140699d2230d722366d7d048a1 ] During suspend, dp host controller and hpd block are disabled due to both ahb and aux clock are disabled. Therefore hpd plug/unplug interrupts will not be generated. At dp_pm_resume(), reinitialize both dp host controller and hpd block so that hpd plug/unplug interrupts will be generated and handled by driver so that hpd connection state is updated correctly. This patch will fix link training flaky issues. Changes in v2: -- use container_of to cast correct dp_display_private pointer at both dp_pm_suspend() and dp_pm_resume(). Changes in v3: -- replace hpd_state atomic_t with u32 Changes in v4 -- call dp_display_host_deinit() at dp_pm_suspend() -- call dp_display_host_init() at msm_dp_display_enable() -- fix phy->init_count unbalance which causes link training failed Changes in v5 -- add Fixes tag Fixes: 8ede2ecc3e5e (drm/msm/dp: Add DP compliance tests on Snapdragon Chipsets) Tested-by: Stephen Boyd Signed-off-by: Kuogee Hsieh Signed-off-by: Rob Clark Signed-off-by: Sasha Levin --- drivers/gpu/drm/msm/dp/dp_catalog.c | 13 +++ drivers/gpu/drm/msm/dp/dp_catalog.h | 1 + drivers/gpu/drm/msm/dp/dp_ctrl.c | 5 + drivers/gpu/drm/msm/dp/dp_display.c | 144 +++++++++++++++------------- drivers/gpu/drm/msm/dp/dp_reg.h | 2 + 5 files changed, 97 insertions(+), 68 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c index b15b4ce4ba35a..4963bfe6a4726 100644 --- a/drivers/gpu/drm/msm/dp/dp_catalog.c +++ b/drivers/gpu/drm/msm/dp/dp_catalog.c @@ -572,6 +572,19 @@ void dp_catalog_ctrl_hpd_config(struct dp_catalog *dp_catalog) dp_write_aux(catalog, REG_DP_DP_HPD_CTRL, DP_DP_HPD_CTRL_HPD_EN); } +u32 dp_catalog_hpd_get_state_status(struct dp_catalog *dp_catalog) +{ + struct dp_catalog_private *catalog = container_of(dp_catalog, + struct dp_catalog_private, dp_catalog); + u32 status; + + status = dp_read_aux(catalog, REG_DP_DP_HPD_INT_STATUS); + status >>= DP_DP_HPD_STATE_STATUS_BITS_SHIFT; + status &= DP_DP_HPD_STATE_STATUS_BITS_MASK; + + return status; +} + u32 dp_catalog_hpd_get_intr_status(struct dp_catalog *dp_catalog) { struct dp_catalog_private *catalog = container_of(dp_catalog, diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.h b/drivers/gpu/drm/msm/dp/dp_catalog.h index 4b7666f1fe6fe..6d257dbebf294 100644 --- a/drivers/gpu/drm/msm/dp/dp_catalog.h +++ b/drivers/gpu/drm/msm/dp/dp_catalog.h @@ -97,6 +97,7 @@ void dp_catalog_ctrl_enable_irq(struct dp_catalog *dp_catalog, bool enable); void dp_catalog_hpd_config_intr(struct dp_catalog *dp_catalog, u32 intr_mask, bool en); void dp_catalog_ctrl_hpd_config(struct dp_catalog *dp_catalog); +u32 dp_catalog_hpd_get_state_status(struct dp_catalog *dp_catalog); u32 dp_catalog_hpd_get_intr_status(struct dp_catalog *dp_catalog); void dp_catalog_ctrl_phy_reset(struct dp_catalog *dp_catalog); int dp_catalog_ctrl_update_vx_px(struct dp_catalog *dp_catalog, u8 v_level, diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c index 872b12689e317..cee161c8ecc67 100644 --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c @@ -1400,6 +1400,8 @@ int dp_ctrl_host_init(struct dp_ctrl *dp_ctrl, bool flip) void dp_ctrl_host_deinit(struct dp_ctrl *dp_ctrl) { struct dp_ctrl_private *ctrl; + struct dp_io *dp_io; + struct phy *phy; if (!dp_ctrl) { DRM_ERROR("Invalid input data\n"); @@ -1407,8 +1409,11 @@ void dp_ctrl_host_deinit(struct dp_ctrl *dp_ctrl) } ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl); + dp_io = &ctrl->parser->io; + phy = dp_io->phy; dp_catalog_ctrl_enable_irq(ctrl->catalog, false); + phy_exit(phy); DRM_DEBUG_DP("Host deinitialized successfully\n"); } diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index ae9989ece73f4..8703c63d85c87 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -108,14 +108,12 @@ struct dp_display_private { /* event related only access by event thread */ struct mutex event_mutex; wait_queue_head_t event_q; - atomic_t hpd_state; + u32 hpd_state; u32 event_pndx; u32 event_gndx; struct dp_event event_list[DP_EVENT_Q_MAX]; spinlock_t event_lock; - struct completion resume_comp; - struct dp_audio *audio; }; @@ -367,6 +365,20 @@ static void dp_display_host_init(struct dp_display_private *dp) dp->core_initialized = true; } +static void dp_display_host_deinit(struct dp_display_private *dp) +{ + if (!dp->core_initialized) { + DRM_DEBUG_DP("DP core not initialized\n"); + return; + } + + dp_ctrl_host_deinit(dp->ctrl); + dp_aux_deinit(dp->aux); + dp_power_deinit(dp->power); + + dp->core_initialized = false; +} + static int dp_display_usbpd_configure_cb(struct device *dev) { int rc = 0; @@ -491,7 +503,7 @@ static int dp_hpd_plug_handle(struct dp_display_private *dp, u32 data) mutex_lock(&dp->event_mutex); - state = atomic_read(&dp->hpd_state); + state = dp->hpd_state; if (state == ST_SUSPEND_PENDING) { mutex_unlock(&dp->event_mutex); return 0; @@ -509,17 +521,14 @@ static int dp_hpd_plug_handle(struct dp_display_private *dp, u32 data) return 0; } - if (state == ST_SUSPENDED) - tout = DP_TIMEOUT_NONE; - - atomic_set(&dp->hpd_state, ST_CONNECT_PENDING); + dp->hpd_state = ST_CONNECT_PENDING; hpd->hpd_high = 1; ret = dp_display_usbpd_configure_cb(&dp->pdev->dev); if (ret) { /* failed */ hpd->hpd_high = 0; - atomic_set(&dp->hpd_state, ST_DISCONNECTED); + dp->hpd_state = ST_DISCONNECTED; } /* start sanity checking */ @@ -540,10 +549,10 @@ static int dp_connect_pending_timeout(struct dp_display_private *dp, u32 data) mutex_lock(&dp->event_mutex); - state = atomic_read(&dp->hpd_state); + state = dp->hpd_state; if (state == ST_CONNECT_PENDING) { dp_display_enable(dp, 0); - atomic_set(&dp->hpd_state, ST_CONNECTED); + dp->hpd_state = ST_CONNECTED; } mutex_unlock(&dp->event_mutex); @@ -568,7 +577,7 @@ static int dp_hpd_unplug_handle(struct dp_display_private *dp, u32 data) mutex_lock(&dp->event_mutex); - state = atomic_read(&dp->hpd_state); + state = dp->hpd_state; if (state == ST_SUSPEND_PENDING) { mutex_unlock(&dp->event_mutex); return 0; @@ -586,7 +595,7 @@ static int dp_hpd_unplug_handle(struct dp_display_private *dp, u32 data) return 0; } - atomic_set(&dp->hpd_state, ST_DISCONNECT_PENDING); + dp->hpd_state = ST_DISCONNECT_PENDING; /* disable HPD plug interrupt until disconnect is done */ dp_catalog_hpd_config_intr(dp->catalog, DP_DP_HPD_PLUG_INT_MASK @@ -621,10 +630,10 @@ static int dp_disconnect_pending_timeout(struct dp_display_private *dp, u32 data mutex_lock(&dp->event_mutex); - state = atomic_read(&dp->hpd_state); + state = dp->hpd_state; if (state == ST_DISCONNECT_PENDING) { dp_display_disable(dp, 0); - atomic_set(&dp->hpd_state, ST_DISCONNECTED); + dp->hpd_state = ST_DISCONNECTED; } mutex_unlock(&dp->event_mutex); @@ -639,7 +648,7 @@ static int dp_irq_hpd_handle(struct dp_display_private *dp, u32 data) mutex_lock(&dp->event_mutex); /* irq_hpd can happen at either connected or disconnected state */ - state = atomic_read(&dp->hpd_state); + state = dp->hpd_state; if (state == ST_SUSPEND_PENDING) { mutex_unlock(&dp->event_mutex); return 0; @@ -790,17 +799,10 @@ static int dp_display_enable(struct dp_display_private *dp, u32 data) dp_display = g_dp_display; - if (dp_display->power_on) { - DRM_DEBUG_DP("Link already setup, return\n"); - return 0; - } - rc = dp_ctrl_on_stream(dp->ctrl); if (!rc) dp_display->power_on = true; - /* complete resume_comp regardless it is armed or not */ - complete(&dp->resume_comp); return rc; } @@ -829,9 +831,6 @@ static int dp_display_disable(struct dp_display_private *dp, u32 data) dp_display = g_dp_display; - if (!dp_display->power_on) - return -EINVAL; - /* wait only if audio was enabled */ if (dp_display->audio_enabled) { if (!wait_for_completion_timeout(&dp->audio_comp, @@ -1152,9 +1151,6 @@ static int dp_display_probe(struct platform_device *pdev) } mutex_init(&dp->event_mutex); - - init_completion(&dp->resume_comp); - g_dp_display = &dp->dp_display; /* Store DP audio handle inside DP display */ @@ -1190,20 +1186,54 @@ static int dp_display_remove(struct platform_device *pdev) static int dp_pm_resume(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); + struct msm_dp *dp_display = platform_get_drvdata(pdev); + struct dp_display_private *dp; + u32 status; + + dp = container_of(dp_display, struct dp_display_private, dp_display); + + mutex_lock(&dp->event_mutex); + + /* start from disconnected state */ + dp->hpd_state = ST_DISCONNECTED; + + /* turn on dp ctrl/phy */ + dp_display_host_init(dp); + + dp_catalog_ctrl_hpd_config(dp->catalog); + + status = dp_catalog_hpd_get_state_status(dp->catalog); + + if (status) { + dp->dp_display.is_connected = true; + } else { + dp->dp_display.is_connected = false; + /* make sure next resume host_init be called */ + dp->core_initialized = false; + } + + mutex_unlock(&dp->event_mutex); + return 0; } static int dp_pm_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); - struct dp_display_private *dp = platform_get_drvdata(pdev); + struct msm_dp *dp_display = platform_get_drvdata(pdev); + struct dp_display_private *dp; - if (!dp) { - DRM_ERROR("DP driver bind failed. Invalid driver data\n"); - return -EINVAL; - } + dp = container_of(dp_display, struct dp_display_private, dp_display); - atomic_set(&dp->hpd_state, ST_SUSPENDED); + mutex_lock(&dp->event_mutex); + + if (dp->core_initialized == true) + dp_display_host_deinit(dp); + + dp->hpd_state = ST_SUSPENDED; + + mutex_unlock(&dp->event_mutex); return 0; } @@ -1318,19 +1348,6 @@ int msm_dp_modeset_init(struct msm_dp *dp_display, struct drm_device *dev, return 0; } -static int dp_display_wait4resume_done(struct dp_display_private *dp) -{ - int ret = 0; - - reinit_completion(&dp->resume_comp); - if (!wait_for_completion_timeout(&dp->resume_comp, - WAIT_FOR_RESUME_TIMEOUT_JIFFIES)) { - DRM_ERROR("wait4resume_done timedout\n"); - ret = -ETIMEDOUT; - } - return ret; -} - int msm_dp_display_enable(struct msm_dp *dp, struct drm_encoder *encoder) { int rc = 0; @@ -1345,6 +1362,8 @@ int msm_dp_display_enable(struct msm_dp *dp, struct drm_encoder *encoder) mutex_lock(&dp_display->event_mutex); + dp_del_event(dp_display, EV_CONNECT_PENDING_TIMEOUT); + rc = dp_display_set_mode(dp, &dp_display->dp_mode); if (rc) { DRM_ERROR("Failed to perform a mode set, rc=%d\n", rc); @@ -1359,15 +1378,10 @@ int msm_dp_display_enable(struct msm_dp *dp, struct drm_encoder *encoder) return rc; } - state = atomic_read(&dp_display->hpd_state); - if (state == ST_SUSPENDED) { - /* start link training */ - dp_add_event(dp_display, EV_HPD_PLUG_INT, 0, 0); - mutex_unlock(&dp_display->event_mutex); + state = dp_display->hpd_state; - /* wait until dp interface is up */ - goto resume_done; - } + if (state == ST_SUSPEND_PENDING) + dp_display_host_init(dp_display); dp_display_enable(dp_display, 0); @@ -1378,21 +1392,15 @@ int msm_dp_display_enable(struct msm_dp *dp, struct drm_encoder *encoder) dp_display_unprepare(dp); } - dp_del_event(dp_display, EV_CONNECT_PENDING_TIMEOUT); - if (state == ST_SUSPEND_PENDING) dp_add_event(dp_display, EV_IRQ_HPD_INT, 0, 0); /* completed connection */ - atomic_set(&dp_display->hpd_state, ST_CONNECTED); + dp_display->hpd_state = ST_CONNECTED; mutex_unlock(&dp_display->event_mutex); return rc; - -resume_done: - dp_display_wait4resume_done(dp_display); - return rc; } int msm_dp_display_pre_disable(struct msm_dp *dp, struct drm_encoder *encoder) @@ -1416,20 +1424,20 @@ int msm_dp_display_disable(struct msm_dp *dp, struct drm_encoder *encoder) mutex_lock(&dp_display->event_mutex); + dp_del_event(dp_display, EV_DISCONNECT_PENDING_TIMEOUT); + dp_display_disable(dp_display, 0); rc = dp_display_unprepare(dp); if (rc) DRM_ERROR("DP display unprepare failed, rc=%d\n", rc); - dp_del_event(dp_display, EV_DISCONNECT_PENDING_TIMEOUT); - - state = atomic_read(&dp_display->hpd_state); + state = dp_display->hpd_state; if (state == ST_DISCONNECT_PENDING) { /* completed disconnection */ - atomic_set(&dp_display->hpd_state, ST_DISCONNECTED); + dp_display->hpd_state = ST_DISCONNECTED; } else { - atomic_set(&dp_display->hpd_state, ST_SUSPEND_PENDING); + dp_display->hpd_state = ST_SUSPEND_PENDING; } mutex_unlock(&dp_display->event_mutex); diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h index 43042ff90a199..268602803d9a3 100644 --- a/drivers/gpu/drm/msm/dp/dp_reg.h +++ b/drivers/gpu/drm/msm/dp/dp_reg.h @@ -32,6 +32,8 @@ #define DP_DP_IRQ_HPD_INT_ACK (0x00000002) #define DP_DP_HPD_REPLUG_INT_ACK (0x00000004) #define DP_DP_HPD_UNPLUG_INT_ACK (0x00000008) +#define DP_DP_HPD_STATE_STATUS_BITS_MASK (0x0000000F) +#define DP_DP_HPD_STATE_STATUS_BITS_SHIFT (0x1C) #define REG_DP_DP_HPD_INT_MASK (0x0000000C) #define DP_DP_HPD_PLUG_INT_MASK (0x00000001)