From: Lin He <[email protected]>

To more accurately determine whether the current HPD status matches the
interrupt status, the polling mechanism in the lower half of the HPD
interrupt (via hibmc_dp_check_hpd_status) has been replaced with directly
retrieving the interrupt status in the upper half of the interrupt (via
hibmc_dp_get_hpd_status).

* Detection and training are not performed if hpd_status is not HPD_IN.

* Set the initial status of hpd_status to HPD_OUT.

Fixes: 3906e7a3b26d ("drm/hisilicon/hibmc: fix dp probabilistical detect errors 
after HPD irq")
Signed-off-by: Lin He <[email protected]>
Signed-off-by: Yongbang Shi <[email protected]>
---
 drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h  |  1 -
 drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c    | 31 ++++++++++++-------
 drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h    |  4 ++-
 .../gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c    | 29 ++++++++++-------
 .../gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c   |  1 +
 5 files changed, 42 insertions(+), 24 deletions(-)

diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h 
b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h
index f53dac256ee0..b0e258b9265e 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h
+++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h
@@ -43,7 +43,6 @@ struct hibmc_dp_dev {
        u8 downstream_ports[DP_MAX_DOWNSTREAM_PORTS];
        struct drm_dp_desc desc;
        bool is_branch;
-       int hpd_status;
        void __iomem *serdes_base;
 };
 
diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c 
b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c
index d5bd3c45649b..b3c7adaaedf7 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c
+++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c
@@ -191,6 +191,10 @@ int hibmc_dp_hw_init(struct hibmc_dp *dp)
        writel(HIBMC_DP_HDCP, dp_dev->base + HIBMC_DP_HDCP_CFG);
        /* clock enable */
        writel(HIBMC_DP_CLK_EN, dp_dev->base + HIBMC_DP_DPTX_CLK_CTRL);
+       /* To latch the HPD interrupt, ensuring that DP can support more modes
+        * within the fbcon framework when connected alone.
+        */
+       msleep(100);
 
        return 0;
 }
@@ -322,20 +326,25 @@ void hibmc_dp_set_cbar(struct hibmc_dp *dp, const struct 
hibmc_dp_cbar_cfg *cfg)
        writel(HIBMC_DP_SYNC_EN_MASK, dp_dev->base + HIBMC_DP_TIMING_SYNC_CTRL);
 }
 
-bool hibmc_dp_check_hpd_status(struct hibmc_dp *dp, int exp_status)
+int hibmc_dp_get_hpd_status(struct hibmc_dp *dp)
 {
+       int ret = HIBMC_HPD_UNKNOWN;
        u32 status;
-       int ret;
 
-       ret = readl_poll_timeout(dp->dp_dev->base + HIBMC_DP_HPD_STATUS, status,
-                                FIELD_GET(HIBMC_DP_HPD_CUR_STATE, status) == 
exp_status,
-                                1000, 100000); /* DP spec says 100ms */
-       if (ret) {
-               drm_dbg_dp(dp->drm_dev, "wait hpd status timeout");
-               return false;
+       status = FIELD_GET(HIBMC_DP_HPD_CUR_STATE,
+                          readl(dp->dp_dev->base + HIBMC_DP_HPD_STATUS));
+
+       switch (status) {
+       case 0: /* 0: unplug */
+       case 3: /* 3: done */
+               ret = HIBMC_HPD_OUT;
+               break;
+       case 1: /* plug */
+               ret = HIBMC_HPD_IN;
+               break;
+       default:
+               break;
        }
 
-       dp->dp_dev->hpd_status = exp_status;
-
-       return true;
+       return ret;
 }
diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h 
b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h
index 0f3662d8737e..bcd4e9d155c8 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h
+++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h
@@ -15,6 +15,7 @@
 struct hibmc_dp_dev;
 
 enum hibmc_hpd_status {
+       HIBMC_HPD_UNKNOWN,
        HIBMC_HPD_OUT,
        HIBMC_HPD_IN,
 };
@@ -55,6 +56,7 @@ struct hibmc_dp {
        struct drm_dp_aux aux;
        struct hibmc_dp_cbar_cfg cfg;
        u32 irq_status;
+       int hpd_status;
        int phys_status;
 };
 
@@ -66,7 +68,7 @@ void hibmc_dp_reset_link(struct hibmc_dp *dp);
 void hibmc_dp_hpd_cfg(struct hibmc_dp *dp);
 void hibmc_dp_enable_int(struct hibmc_dp *dp);
 void hibmc_dp_disable_int(struct hibmc_dp *dp);
-bool hibmc_dp_check_hpd_status(struct hibmc_dp *dp, int exp_status);
+int hibmc_dp_get_hpd_status(struct hibmc_dp *dp);
 u8 hibmc_dp_get_link_rate(struct hibmc_dp *dp);
 u8 hibmc_dp_get_lanes(struct hibmc_dp *dp);
 
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c 
b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c
index 2e9403b8bf3c..23716d48149f 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c
@@ -63,12 +63,8 @@ static int hibmc_dp_detect(struct drm_connector *connector,
        struct hibmc_dp_dev *dp_dev = dp->dp_dev;
        int ret = connector_status_disconnected;
 
-       if (dp->irq_status) {
-               if (dp_dev->hpd_status != HIBMC_HPD_IN) {
-                       ret = connector_status_disconnected;
-                       goto exit;
-               }
-       }
+       if (dp->hpd_status != HIBMC_HPD_IN)
+               goto exit;
 
        if (!hibmc_dp_get_dpcd(dp_dev)) {
                ret = connector_status_disconnected;
@@ -166,6 +162,9 @@ static void hibmc_dp_encoder_enable(struct drm_encoder 
*drm_encoder,
        struct hibmc_dp *dp = container_of(drm_encoder, struct hibmc_dp, 
encoder);
        struct drm_display_mode *mode = &drm_encoder->crtc->state->mode;
 
+       if (dp->hpd_status != HIBMC_HPD_IN)
+               return;
+
        if (hibmc_dp_prepare(dp, mode))
                return;
 
@@ -189,24 +188,31 @@ irqreturn_t hibmc_dp_hpd_isr(int irq, void *arg)
 {
        struct drm_device *dev = (struct drm_device *)arg;
        struct hibmc_drm_private *priv = to_hibmc_drm_private(dev);
-       int idx, exp_status;
+       int status = priv->dp.hpd_status;
+       int idx;
 
        if (!drm_dev_enter(dev, &idx))
                return -ENODEV;
 
        if (priv->dp.irq_status & DP_MASKED_SINK_HPD_PLUG_INT) {
                drm_dbg_dp(&priv->dev, "HPD IN isr occur!\n");
+               if (status != HIBMC_HPD_IN) {
+                       drm_err(&priv->dev, "HPD status (%d) error", status);
+                       goto exit;
+               }
                hibmc_dp_hpd_cfg(&priv->dp);
-               exp_status = HIBMC_HPD_IN;
        } else {
                drm_dbg_dp(&priv->dev, "HPD OUT isr occur!\n");
+               if (status != HIBMC_HPD_OUT) {
+                       drm_err(&priv->dev, "HPD status (%d) error", status);
+                       goto exit;
+               }
                hibmc_dp_reset_link(&priv->dp);
-               exp_status = HIBMC_HPD_OUT;
        }
 
-       if (hibmc_dp_check_hpd_status(&priv->dp, exp_status))
-               drm_connector_helper_hpd_irq_event(&priv->dp.connector);
+       drm_connector_helper_hpd_irq_event(&priv->dp.connector);
 
+exit:
        drm_dev_exit(idx);
 
        return IRQ_HANDLED;
@@ -223,6 +229,7 @@ int hibmc_dp_init(struct hibmc_drm_private *priv)
 
        dp->mmio = priv->mmio;
        dp->drm_dev = dev;
+       dp->hpd_status = HIBMC_HPD_OUT;
 
        ret = hibmc_dp_hw_init(&priv->dp);
        if (ret) {
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c 
b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
index 4d85c89f3f88..e5cca7b63b78 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
@@ -63,6 +63,7 @@ static irqreturn_t hibmc_dp_interrupt(int irq, void *arg)
        status = readl(priv->mmio + HIBMC_DP_INTSTAT);
        if (status) {
                priv->dp.irq_status = status;
+               priv->dp.hpd_status = hibmc_dp_get_hpd_status(&priv->dp);
                writel(status, priv->mmio + HIBMC_DP_INTCLR);
                return IRQ_WAKE_THREAD;
        }
-- 
2.43.0

Reply via email to