Skip to content
/ linux Public

Commit bc95089

Browse files
mark1188-uigregkh
authored andcommitted
drm/hisilicon/hibmc: fix dp probabilistical detect errors after HPD irq
[ Upstream commit 3906e7a ] The issue is that drm_connector_helper_detect_from_ddc() returns wrong status when plugging or unplugging the monitor, which may cause the link failed err.[0] Use HPD pin status in DP's detect_ctx() for real physical monitor in/out, and implement a complete DP detection including read DPCD, check if it's a branch device and its sink count for different situations. [0]: hibme-drm 0000:83:00.0: [drm] *ERROR* channel equalization failed 5 times hibme-drm 0000:83:00.0: [drm] *ERROR* channel equalization failed 5 times hibme-drm 0000:83:00.0: [drm] *ERROR* dp link training failed, ret: -16 hibmc-drm 0000:83:00.0: [drm] *ERROR* hibme dp mode set failed: -16 Fixes: 3c7623f ("drm/hisilicon/hibmc: Enable this hot plug detect of irq feature") Signed-off-by: Baihan Li <libaihan@huawei.com> Signed-off-by: Yongbang Shi <shiyongbang@huawei.com> Reviewed-by: Tao Tian <tiantao6@hisilicon.com> Link: https://patch.msgid.link/20251210023759.3944834-2-shiyongbang@huawei.com Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent 840581b commit bc95089

File tree

5 files changed

+80
-4
lines changed

5 files changed

+80
-4
lines changed

drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ struct hibmc_dp_dev {
4040
struct mutex lock; /* protects concurrent RW in hibmc_dp_reg_write_field() */
4141
struct hibmc_dp_link link;
4242
u8 dpcd[DP_RECEIVER_CAP_SIZE];
43+
u8 downstream_ports[DP_MAX_DOWNSTREAM_PORTS];
44+
struct drm_dp_desc desc;
45+
bool is_branch;
46+
int hpd_status;
4347
void __iomem *serdes_base;
4448
};
4549

drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Copyright (c) 2024 Hisilicon Limited.
33

44
#include <linux/io.h>
5+
#include <linux/iopoll.h>
56
#include <linux/delay.h>
67
#include "dp_config.h"
78
#include "dp_comm.h"
@@ -305,3 +306,21 @@ void hibmc_dp_set_cbar(struct hibmc_dp *dp, const struct hibmc_dp_cbar_cfg *cfg)
305306
hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL, BIT(0), cfg->enable);
306307
writel(HIBMC_DP_SYNC_EN_MASK, dp_dev->base + HIBMC_DP_TIMING_SYNC_CTRL);
307308
}
309+
310+
bool hibmc_dp_check_hpd_status(struct hibmc_dp *dp, int exp_status)
311+
{
312+
u32 status;
313+
int ret;
314+
315+
ret = readl_poll_timeout(dp->dp_dev->base + HIBMC_DP_HPD_STATUS, status,
316+
FIELD_GET(HIBMC_DP_HPD_CUR_STATE, status) == exp_status,
317+
1000, 100000); /* DP spec says 100ms */
318+
if (ret) {
319+
drm_dbg_dp(dp->drm_dev, "wait hpd status timeout");
320+
return false;
321+
}
322+
323+
dp->dp_dev->hpd_status = exp_status;
324+
325+
return true;
326+
}

drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@
1414

1515
struct hibmc_dp_dev;
1616

17+
enum hibmc_hpd_status {
18+
HIBMC_HPD_OUT,
19+
HIBMC_HPD_IN,
20+
};
21+
1722
enum hibmc_dp_cbar_pattern {
1823
CBAR_COLOR_BAR,
1924
CBAR_WHITE,
@@ -60,5 +65,6 @@ void hibmc_dp_reset_link(struct hibmc_dp *dp);
6065
void hibmc_dp_hpd_cfg(struct hibmc_dp *dp);
6166
void hibmc_dp_enable_int(struct hibmc_dp *dp);
6267
void hibmc_dp_disable_int(struct hibmc_dp *dp);
68+
bool hibmc_dp_check_hpd_status(struct hibmc_dp *dp, int exp_status);
6369

6470
#endif

drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
#define HIBMC_DP_CFG_AUX_READY_DATA_BYTE GENMASK(16, 12)
2525
#define HIBMC_DP_CFG_AUX GENMASK(24, 17)
2626

27+
#define HIBMC_DP_HPD_STATUS 0x98
28+
#define HIBMC_DP_HPD_CUR_STATE GENMASK(7, 4)
29+
2730
#define HIBMC_DP_PHYIF_CTRL0 0xa0
2831
#define HIBMC_DP_CFG_SCRAMBLE_EN BIT(0)
2932
#define HIBMC_DP_CFG_PAT_SEL GENMASK(7, 4)

drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "hibmc_drm_drv.h"
1414
#include "dp/dp_hw.h"
15+
#include "dp/dp_comm.h"
1516

1617
#define DP_MASKED_SINK_HPD_PLUG_INT BIT(2)
1718

@@ -31,12 +32,53 @@ static int hibmc_dp_connector_get_modes(struct drm_connector *connector)
3132
return count;
3233
}
3334

35+
static bool hibmc_dp_get_dpcd(struct hibmc_dp_dev *dp_dev)
36+
{
37+
int ret;
38+
39+
ret = drm_dp_read_dpcd_caps(dp_dev->aux, dp_dev->dpcd);
40+
if (ret)
41+
return false;
42+
43+
dp_dev->is_branch = drm_dp_is_branch(dp_dev->dpcd);
44+
45+
ret = drm_dp_read_desc(dp_dev->aux, &dp_dev->desc, dp_dev->is_branch);
46+
if (ret)
47+
return false;
48+
49+
ret = drm_dp_read_downstream_info(dp_dev->aux, dp_dev->dpcd, dp_dev->downstream_ports);
50+
if (ret)
51+
return false;
52+
53+
return true;
54+
}
55+
3456
static int hibmc_dp_detect(struct drm_connector *connector,
3557
struct drm_modeset_acquire_ctx *ctx, bool force)
3658
{
37-
mdelay(200);
59+
struct hibmc_dp *dp = to_hibmc_dp(connector);
60+
struct hibmc_dp_dev *dp_dev = dp->dp_dev;
61+
int ret;
62+
63+
if (dp->irq_status) {
64+
if (dp_dev->hpd_status != HIBMC_HPD_IN)
65+
return connector_status_disconnected;
66+
}
67+
68+
if (!hibmc_dp_get_dpcd(dp_dev))
69+
return connector_status_disconnected;
70+
71+
if (!dp_dev->is_branch)
72+
return connector_status_connected;
73+
74+
if (drm_dp_read_sink_count_cap(connector, dp_dev->dpcd, &dp_dev->desc) &&
75+
dp_dev->downstream_ports[0] & DP_DS_PORT_HPD) {
76+
ret = drm_dp_read_sink_count(dp_dev->aux);
77+
if (ret > 0)
78+
return connector_status_connected;
79+
}
3880

39-
return drm_connector_helper_detect_from_ddc(connector, ctx, force);
81+
return connector_status_disconnected;
4082
}
4183

4284
static const struct drm_connector_helper_funcs hibmc_dp_conn_helper_funcs = {
@@ -115,20 +157,22 @@ irqreturn_t hibmc_dp_hpd_isr(int irq, void *arg)
115157
{
116158
struct drm_device *dev = (struct drm_device *)arg;
117159
struct hibmc_drm_private *priv = to_hibmc_drm_private(dev);
118-
int idx;
160+
int idx, exp_status;
119161

120162
if (!drm_dev_enter(dev, &idx))
121163
return -ENODEV;
122164

123165
if (priv->dp.irq_status & DP_MASKED_SINK_HPD_PLUG_INT) {
124166
drm_dbg_dp(&priv->dev, "HPD IN isr occur!\n");
125167
hibmc_dp_hpd_cfg(&priv->dp);
168+
exp_status = HIBMC_HPD_IN;
126169
} else {
127170
drm_dbg_dp(&priv->dev, "HPD OUT isr occur!\n");
128171
hibmc_dp_reset_link(&priv->dp);
172+
exp_status = HIBMC_HPD_OUT;
129173
}
130174

131-
if (dev->registered)
175+
if (hibmc_dp_check_hpd_status(&priv->dp, exp_status))
132176
drm_connector_helper_hpd_irq_event(&priv->dp.connector);
133177

134178
drm_dev_exit(idx);

0 commit comments

Comments
 (0)