// Copyright (c) 2024 Hisilicon Limited.
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/delay.h>
#include "dp_config.h"
#include "dp_comm.h"
hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL, BIT(0), cfg->enable);
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)
+{
+ 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;
+ }
+
+ dp->dp_dev->hpd_status = exp_status;
+
+ return true;
+}
#include "hibmc_drm_drv.h"
#include "dp/dp_hw.h"
+#include "dp/dp_comm.h"
#define DP_MASKED_SINK_HPD_PLUG_INT BIT(2)
return count;
}
+static bool hibmc_dp_get_dpcd(struct hibmc_dp_dev *dp_dev)
+{
+ int ret;
+
+ ret = drm_dp_read_dpcd_caps(dp_dev->aux, dp_dev->dpcd);
+ if (ret)
+ return false;
+
+ dp_dev->is_branch = drm_dp_is_branch(dp_dev->dpcd);
+
+ ret = drm_dp_read_desc(dp_dev->aux, &dp_dev->desc, dp_dev->is_branch);
+ if (ret)
+ return false;
+
+ ret = drm_dp_read_downstream_info(dp_dev->aux, dp_dev->dpcd, dp_dev->downstream_ports);
+ if (ret)
+ return false;
+
+ return true;
+}
+
static int hibmc_dp_detect(struct drm_connector *connector,
struct drm_modeset_acquire_ctx *ctx, bool force)
{
- mdelay(200);
+ struct hibmc_dp *dp = to_hibmc_dp(connector);
+ struct hibmc_dp_dev *dp_dev = dp->dp_dev;
+ int ret;
+
+ if (dp->irq_status) {
+ if (dp_dev->hpd_status != HIBMC_HPD_IN)
+ return connector_status_disconnected;
+ }
+
+ if (!hibmc_dp_get_dpcd(dp_dev))
+ return connector_status_disconnected;
+
+ if (!dp_dev->is_branch)
+ return connector_status_connected;
+
+ if (drm_dp_read_sink_count_cap(connector, dp_dev->dpcd, &dp_dev->desc) &&
+ dp_dev->downstream_ports[0] & DP_DS_PORT_HPD) {
+ ret = drm_dp_read_sink_count(dp_dev->aux);
+ if (ret > 0)
+ return connector_status_connected;
+ }
- return drm_connector_helper_detect_from_ddc(connector, ctx, force);
+ return connector_status_disconnected;
}
static const struct drm_connector_helper_funcs hibmc_dp_conn_helper_funcs = {
{
struct drm_device *dev = (struct drm_device *)arg;
struct hibmc_drm_private *priv = to_hibmc_drm_private(dev);
- int idx;
+ int idx, exp_status;
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");
hibmc_dp_hpd_cfg(&priv->dp);
+ exp_status = HIBMC_HPD_IN;
} else {
drm_dbg_dp(&priv->dev, "HPD OUT isr occur!\n");
hibmc_dp_reset_link(&priv->dp);
+ exp_status = HIBMC_HPD_OUT;
}
- if (dev->registered)
+ if (hibmc_dp_check_hpd_status(&priv->dp, exp_status))
drm_connector_helper_hpd_irq_event(&priv->dp.connector);
drm_dev_exit(idx);