Commit 19e52bcb authored by Kuogee Hsieh's avatar Kuogee Hsieh Committed by Rob Clark

drm/msm/dp: return correct connection status after suspend

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:  8ede2ecc (drm/msm/dp: Add DP compliance tests on Snapdragon Chipsets)
Tested-by: default avatarStephen Boyd <swboyd@chromium.org>
Signed-off-by: default avatarKuogee Hsieh <khsieh@codeaurora.org>
Signed-off-by: default avatarRob Clark <robdclark@chromium.org>
parent 5771de5d
...@@ -572,6 +572,19 @@ void dp_catalog_ctrl_hpd_config(struct dp_catalog *dp_catalog) ...@@ -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); 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) u32 dp_catalog_hpd_get_intr_status(struct dp_catalog *dp_catalog)
{ {
struct dp_catalog_private *catalog = container_of(dp_catalog, struct dp_catalog_private *catalog = container_of(dp_catalog,
......
...@@ -97,6 +97,7 @@ void dp_catalog_ctrl_enable_irq(struct dp_catalog *dp_catalog, bool enable); ...@@ -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, void dp_catalog_hpd_config_intr(struct dp_catalog *dp_catalog,
u32 intr_mask, bool en); u32 intr_mask, bool en);
void dp_catalog_ctrl_hpd_config(struct dp_catalog *dp_catalog); 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); u32 dp_catalog_hpd_get_intr_status(struct dp_catalog *dp_catalog);
void dp_catalog_ctrl_phy_reset(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, int dp_catalog_ctrl_update_vx_px(struct dp_catalog *dp_catalog, u8 v_level,
......
...@@ -1403,6 +1403,8 @@ int dp_ctrl_host_init(struct dp_ctrl *dp_ctrl, bool flip) ...@@ -1403,6 +1403,8 @@ int dp_ctrl_host_init(struct dp_ctrl *dp_ctrl, bool flip)
void dp_ctrl_host_deinit(struct dp_ctrl *dp_ctrl) void dp_ctrl_host_deinit(struct dp_ctrl *dp_ctrl)
{ {
struct dp_ctrl_private *ctrl; struct dp_ctrl_private *ctrl;
struct dp_io *dp_io;
struct phy *phy;
if (!dp_ctrl) { if (!dp_ctrl) {
DRM_ERROR("Invalid input data\n"); DRM_ERROR("Invalid input data\n");
...@@ -1410,8 +1412,11 @@ void dp_ctrl_host_deinit(struct dp_ctrl *dp_ctrl) ...@@ -1410,8 +1412,11 @@ void dp_ctrl_host_deinit(struct dp_ctrl *dp_ctrl)
} }
ctrl = container_of(dp_ctrl, struct dp_ctrl_private, 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); dp_catalog_ctrl_enable_irq(ctrl->catalog, false);
phy_exit(phy);
DRM_DEBUG_DP("Host deinitialized successfully\n"); DRM_DEBUG_DP("Host deinitialized successfully\n");
} }
......
...@@ -108,14 +108,12 @@ struct dp_display_private { ...@@ -108,14 +108,12 @@ struct dp_display_private {
/* event related only access by event thread */ /* event related only access by event thread */
struct mutex event_mutex; struct mutex event_mutex;
wait_queue_head_t event_q; wait_queue_head_t event_q;
atomic_t hpd_state; u32 hpd_state;
u32 event_pndx; u32 event_pndx;
u32 event_gndx; u32 event_gndx;
struct dp_event event_list[DP_EVENT_Q_MAX]; struct dp_event event_list[DP_EVENT_Q_MAX];
spinlock_t event_lock; spinlock_t event_lock;
struct completion resume_comp;
struct dp_audio *audio; struct dp_audio *audio;
}; };
...@@ -367,6 +365,20 @@ static void dp_display_host_init(struct dp_display_private *dp) ...@@ -367,6 +365,20 @@ static void dp_display_host_init(struct dp_display_private *dp)
dp->core_initialized = true; 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) static int dp_display_usbpd_configure_cb(struct device *dev)
{ {
int rc = 0; int rc = 0;
...@@ -491,7 +503,7 @@ static int dp_hpd_plug_handle(struct dp_display_private *dp, u32 data) ...@@ -491,7 +503,7 @@ static int dp_hpd_plug_handle(struct dp_display_private *dp, u32 data)
mutex_lock(&dp->event_mutex); mutex_lock(&dp->event_mutex);
state = atomic_read(&dp->hpd_state); state = dp->hpd_state;
if (state == ST_SUSPEND_PENDING) { if (state == ST_SUSPEND_PENDING) {
mutex_unlock(&dp->event_mutex); mutex_unlock(&dp->event_mutex);
return 0; return 0;
...@@ -509,17 +521,14 @@ static int dp_hpd_plug_handle(struct dp_display_private *dp, u32 data) ...@@ -509,17 +521,14 @@ static int dp_hpd_plug_handle(struct dp_display_private *dp, u32 data)
return 0; return 0;
} }
if (state == ST_SUSPENDED) dp->hpd_state = ST_CONNECT_PENDING;
tout = DP_TIMEOUT_NONE;
atomic_set(&dp->hpd_state, ST_CONNECT_PENDING);
hpd->hpd_high = 1; hpd->hpd_high = 1;
ret = dp_display_usbpd_configure_cb(&dp->pdev->dev); ret = dp_display_usbpd_configure_cb(&dp->pdev->dev);
if (ret) { /* failed */ if (ret) { /* failed */
hpd->hpd_high = 0; hpd->hpd_high = 0;
atomic_set(&dp->hpd_state, ST_DISCONNECTED); dp->hpd_state = ST_DISCONNECTED;
} }
/* start sanity checking */ /* start sanity checking */
...@@ -540,10 +549,10 @@ static int dp_connect_pending_timeout(struct dp_display_private *dp, u32 data) ...@@ -540,10 +549,10 @@ static int dp_connect_pending_timeout(struct dp_display_private *dp, u32 data)
mutex_lock(&dp->event_mutex); mutex_lock(&dp->event_mutex);
state = atomic_read(&dp->hpd_state); state = dp->hpd_state;
if (state == ST_CONNECT_PENDING) { if (state == ST_CONNECT_PENDING) {
dp_display_enable(dp, 0); dp_display_enable(dp, 0);
atomic_set(&dp->hpd_state, ST_CONNECTED); dp->hpd_state = ST_CONNECTED;
} }
mutex_unlock(&dp->event_mutex); mutex_unlock(&dp->event_mutex);
...@@ -568,7 +577,7 @@ static int dp_hpd_unplug_handle(struct dp_display_private *dp, u32 data) ...@@ -568,7 +577,7 @@ static int dp_hpd_unplug_handle(struct dp_display_private *dp, u32 data)
mutex_lock(&dp->event_mutex); mutex_lock(&dp->event_mutex);
state = atomic_read(&dp->hpd_state); state = dp->hpd_state;
if (state == ST_SUSPEND_PENDING) { if (state == ST_SUSPEND_PENDING) {
mutex_unlock(&dp->event_mutex); mutex_unlock(&dp->event_mutex);
return 0; return 0;
...@@ -586,7 +595,7 @@ static int dp_hpd_unplug_handle(struct dp_display_private *dp, u32 data) ...@@ -586,7 +595,7 @@ static int dp_hpd_unplug_handle(struct dp_display_private *dp, u32 data)
return 0; return 0;
} }
atomic_set(&dp->hpd_state, ST_DISCONNECT_PENDING); dp->hpd_state = ST_DISCONNECT_PENDING;
/* disable HPD plug interrupt until disconnect is done */ /* disable HPD plug interrupt until disconnect is done */
dp_catalog_hpd_config_intr(dp->catalog, DP_DP_HPD_PLUG_INT_MASK 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 ...@@ -621,10 +630,10 @@ static int dp_disconnect_pending_timeout(struct dp_display_private *dp, u32 data
mutex_lock(&dp->event_mutex); mutex_lock(&dp->event_mutex);
state = atomic_read(&dp->hpd_state); state = dp->hpd_state;
if (state == ST_DISCONNECT_PENDING) { if (state == ST_DISCONNECT_PENDING) {
dp_display_disable(dp, 0); dp_display_disable(dp, 0);
atomic_set(&dp->hpd_state, ST_DISCONNECTED); dp->hpd_state = ST_DISCONNECTED;
} }
mutex_unlock(&dp->event_mutex); mutex_unlock(&dp->event_mutex);
...@@ -639,7 +648,7 @@ static int dp_irq_hpd_handle(struct dp_display_private *dp, u32 data) ...@@ -639,7 +648,7 @@ static int dp_irq_hpd_handle(struct dp_display_private *dp, u32 data)
mutex_lock(&dp->event_mutex); mutex_lock(&dp->event_mutex);
/* irq_hpd can happen at either connected or disconnected state */ /* 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) { if (state == ST_SUSPEND_PENDING) {
mutex_unlock(&dp->event_mutex); mutex_unlock(&dp->event_mutex);
return 0; return 0;
...@@ -790,17 +799,10 @@ static int dp_display_enable(struct dp_display_private *dp, u32 data) ...@@ -790,17 +799,10 @@ static int dp_display_enable(struct dp_display_private *dp, u32 data)
dp_display = g_dp_display; 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); rc = dp_ctrl_on_stream(dp->ctrl);
if (!rc) if (!rc)
dp_display->power_on = true; dp_display->power_on = true;
/* complete resume_comp regardless it is armed or not */
complete(&dp->resume_comp);
return rc; return rc;
} }
...@@ -829,9 +831,6 @@ static int dp_display_disable(struct dp_display_private *dp, u32 data) ...@@ -829,9 +831,6 @@ static int dp_display_disable(struct dp_display_private *dp, u32 data)
dp_display = g_dp_display; dp_display = g_dp_display;
if (!dp_display->power_on)
return -EINVAL;
/* wait only if audio was enabled */ /* wait only if audio was enabled */
if (dp_display->audio_enabled) { if (dp_display->audio_enabled) {
if (!wait_for_completion_timeout(&dp->audio_comp, if (!wait_for_completion_timeout(&dp->audio_comp,
...@@ -1152,9 +1151,6 @@ static int dp_display_probe(struct platform_device *pdev) ...@@ -1152,9 +1151,6 @@ static int dp_display_probe(struct platform_device *pdev)
} }
mutex_init(&dp->event_mutex); mutex_init(&dp->event_mutex);
init_completion(&dp->resume_comp);
g_dp_display = &dp->dp_display; g_dp_display = &dp->dp_display;
/* Store DP audio handle inside DP display */ /* Store DP audio handle inside DP display */
...@@ -1190,20 +1186,54 @@ static int dp_display_remove(struct platform_device *pdev) ...@@ -1190,20 +1186,54 @@ static int dp_display_remove(struct platform_device *pdev)
static int dp_pm_resume(struct device *dev) 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; return 0;
} }
static int dp_pm_suspend(struct device *dev) static int dp_pm_suspend(struct device *dev)
{ {
struct platform_device *pdev = to_platform_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) { dp = container_of(dp_display, struct dp_display_private, dp_display);
DRM_ERROR("DP driver bind failed. Invalid driver data\n");
return -EINVAL;
}
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; return 0;
} }
...@@ -1318,19 +1348,6 @@ int msm_dp_modeset_init(struct msm_dp *dp_display, struct drm_device *dev, ...@@ -1318,19 +1348,6 @@ int msm_dp_modeset_init(struct msm_dp *dp_display, struct drm_device *dev,
return 0; 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 msm_dp_display_enable(struct msm_dp *dp, struct drm_encoder *encoder)
{ {
int rc = 0; int rc = 0;
...@@ -1345,6 +1362,8 @@ int msm_dp_display_enable(struct msm_dp *dp, struct drm_encoder *encoder) ...@@ -1345,6 +1362,8 @@ int msm_dp_display_enable(struct msm_dp *dp, struct drm_encoder *encoder)
mutex_lock(&dp_display->event_mutex); 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); rc = dp_display_set_mode(dp, &dp_display->dp_mode);
if (rc) { if (rc) {
DRM_ERROR("Failed to perform a mode set, rc=%d\n", 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) ...@@ -1359,15 +1378,10 @@ int msm_dp_display_enable(struct msm_dp *dp, struct drm_encoder *encoder)
return rc; return rc;
} }
state = atomic_read(&dp_display->hpd_state); state = 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);
/* wait until dp interface is up */ if (state == ST_SUSPEND_PENDING)
goto resume_done; dp_display_host_init(dp_display);
}
dp_display_enable(dp_display, 0); dp_display_enable(dp_display, 0);
...@@ -1378,21 +1392,15 @@ int msm_dp_display_enable(struct msm_dp *dp, struct drm_encoder *encoder) ...@@ -1378,21 +1392,15 @@ int msm_dp_display_enable(struct msm_dp *dp, struct drm_encoder *encoder)
dp_display_unprepare(dp); dp_display_unprepare(dp);
} }
dp_del_event(dp_display, EV_CONNECT_PENDING_TIMEOUT);
if (state == ST_SUSPEND_PENDING) if (state == ST_SUSPEND_PENDING)
dp_add_event(dp_display, EV_IRQ_HPD_INT, 0, 0); dp_add_event(dp_display, EV_IRQ_HPD_INT, 0, 0);
/* completed connection */ /* completed connection */
atomic_set(&dp_display->hpd_state, ST_CONNECTED); dp_display->hpd_state = ST_CONNECTED;
mutex_unlock(&dp_display->event_mutex); mutex_unlock(&dp_display->event_mutex);
return rc; 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) 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) ...@@ -1416,20 +1424,20 @@ int msm_dp_display_disable(struct msm_dp *dp, struct drm_encoder *encoder)
mutex_lock(&dp_display->event_mutex); mutex_lock(&dp_display->event_mutex);
dp_del_event(dp_display, EV_DISCONNECT_PENDING_TIMEOUT);
dp_display_disable(dp_display, 0); dp_display_disable(dp_display, 0);
rc = dp_display_unprepare(dp); rc = dp_display_unprepare(dp);
if (rc) if (rc)
DRM_ERROR("DP display unprepare failed, rc=%d\n", rc); DRM_ERROR("DP display unprepare failed, rc=%d\n", rc);
dp_del_event(dp_display, EV_DISCONNECT_PENDING_TIMEOUT); state = dp_display->hpd_state;
state = atomic_read(&dp_display->hpd_state);
if (state == ST_DISCONNECT_PENDING) { if (state == ST_DISCONNECT_PENDING) {
/* completed disconnection */ /* completed disconnection */
atomic_set(&dp_display->hpd_state, ST_DISCONNECTED); dp_display->hpd_state = ST_DISCONNECTED;
} else { } else {
atomic_set(&dp_display->hpd_state, ST_SUSPEND_PENDING); dp_display->hpd_state = ST_SUSPEND_PENDING;
} }
mutex_unlock(&dp_display->event_mutex); mutex_unlock(&dp_display->event_mutex);
......
...@@ -32,6 +32,8 @@ ...@@ -32,6 +32,8 @@
#define DP_DP_IRQ_HPD_INT_ACK (0x00000002) #define DP_DP_IRQ_HPD_INT_ACK (0x00000002)
#define DP_DP_HPD_REPLUG_INT_ACK (0x00000004) #define DP_DP_HPD_REPLUG_INT_ACK (0x00000004)
#define DP_DP_HPD_UNPLUG_INT_ACK (0x00000008) #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 REG_DP_DP_HPD_INT_MASK (0x0000000C)
#define DP_DP_HPD_PLUG_INT_MASK (0x00000001) #define DP_DP_HPD_PLUG_INT_MASK (0x00000001)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment