Skip to content

Commit

Permalink
drm/msm/dp: Add Display Port HPD feature
Browse files Browse the repository at this point in the history
Configure HPD registers in DP controller and
enable HPD interrupt.

Add interrupt to handle HPD connect and disconnect events.

Changes in v8: None

Signed-off-by: Tanmay Shah <tanmay@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@chromium.org>
  • Loading branch information
Tanmay Shah authored and Rob Clark committed Sep 15, 2020
1 parent a10476e commit 220b856
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 35 deletions.
18 changes: 18 additions & 0 deletions drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,23 @@ static void dpu_irq_preinstall(struct msm_kms *kms)
dpu_core_irq_preinstall(dpu_kms);
}

static int dpu_irq_postinstall(struct msm_kms *kms)
{
struct msm_drm_private *priv;
struct dpu_kms *dpu_kms = to_dpu_kms(kms);

if (!dpu_kms || !dpu_kms->dev)
return -EINVAL;

priv = dpu_kms->dev->dev_private;
if (!priv)
return -EINVAL;

msm_dp_irq_postinstall(priv->dp);

return 0;
}

static void dpu_irq_uninstall(struct msm_kms *kms)
{
struct dpu_kms *dpu_kms = to_dpu_kms(kms);
Expand All @@ -784,6 +801,7 @@ static void dpu_irq_uninstall(struct msm_kms *kms)
static const struct msm_kms_funcs kms_funcs = {
.hw_init = dpu_kms_hw_init,
.irq_preinstall = dpu_irq_preinstall,
.irq_postinstall = dpu_irq_postinstall,
.irq_uninstall = dpu_irq_uninstall,
.irq = dpu_irq,
.enable_commit = dpu_kms_enable_commit,
Expand Down
67 changes: 41 additions & 26 deletions drivers/gpu/drm/msm/dp/dp_catalog.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
#define POLLING_SLEEP_US 1000
#define POLLING_TIMEOUT_US 10000

#define REFTIMER_DEFAULT_VALUE 0x20000
#define SCRAMBLER_RESET_COUNT_VALUE 0xFC

#define DP_INTERRUPT_STATUS_ACK_SHIFT 1
Expand Down Expand Up @@ -746,35 +745,51 @@ void dp_catalog_ctrl_enable_irq(struct dp_catalog *dp_catalog,
}
}

void dp_catalog_ctrl_hpd_config(struct dp_catalog *dp_catalog, bool en)
void dp_catalog_hpd_config_intr(struct dp_catalog *dp_catalog,
u32 intr_mask, bool en)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);

if (en) {
u32 reftimer = dp_read_aux(catalog, REG_DP_DP_HPD_REFTIMER);

dp_write_aux(catalog, REG_DP_DP_HPD_INT_ACK,
DP_DP_HPD_PLUG_INT_ACK |
DP_DP_IRQ_HPD_INT_ACK |
DP_DP_HPD_REPLUG_INT_ACK |
DP_DP_HPD_UNPLUG_INT_ACK);
dp_write_aux(catalog, REG_DP_DP_HPD_INT_MASK,
DP_DP_HPD_PLUG_INT_MASK |
DP_DP_IRQ_HPD_INT_MASK |
DP_DP_HPD_REPLUG_INT_MASK |
DP_DP_HPD_UNPLUG_INT_MASK);

/* Configure REFTIMER */
reftimer |= REFTIMER_DEFAULT_VALUE;
dp_write_aux(catalog, REG_DP_DP_HPD_REFTIMER, reftimer);
/* Enable HPD */
dp_write_aux(catalog, REG_DP_DP_HPD_CTRL,
DP_DP_HPD_CTRL_HPD_EN);
} else {
/* Disable HPD */
dp_write_aux(catalog, REG_DP_DP_HPD_CTRL, 0x0);
}
u32 config = dp_read_aux(catalog, REG_DP_DP_HPD_INT_MASK);

config = (en ? config | intr_mask : config & ~intr_mask);

dp_write_aux(catalog, REG_DP_DP_HPD_INT_MASK,
config & DP_DP_HPD_INT_MASK);
}

void dp_catalog_ctrl_hpd_config(struct dp_catalog *dp_catalog)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);

u32 reftimer = dp_read_aux(catalog, REG_DP_DP_HPD_REFTIMER);

/* enable HPD interrupts */
dp_catalog_hpd_config_intr(dp_catalog,
DP_DP_HPD_PLUG_INT_MASK | DP_DP_IRQ_HPD_INT_MASK
| DP_DP_HPD_UNPLUG_INT_MASK, true);

/* Configure REFTIMER and enable it */
reftimer |= DP_DP_HPD_REFTIMER_ENABLE;
dp_write_aux(catalog, REG_DP_DP_HPD_REFTIMER, reftimer);

/* Enable HPD */
dp_write_aux(catalog, REG_DP_DP_HPD_CTRL, DP_DP_HPD_CTRL_HPD_EN);
}

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, dp_catalog);
int isr = 0;

isr = dp_read_aux(catalog, REG_DP_DP_HPD_INT_STATUS);
dp_write_aux(catalog, REG_DP_DP_HPD_INT_ACK,
(isr & DP_DP_HPD_INT_MASK));

return isr;
}

int dp_catalog_ctrl_get_interrupt(struct dp_catalog *dp_catalog)
Expand Down
5 changes: 4 additions & 1 deletion drivers/gpu/drm/msm/dp/dp_catalog.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,10 @@ void dp_catalog_ctrl_reset(struct dp_catalog *dp_catalog);
void dp_catalog_ctrl_usb_reset(struct dp_catalog *dp_catalog, bool flip);
bool dp_catalog_ctrl_mainlink_ready(struct dp_catalog *dp_catalog);
void dp_catalog_ctrl_enable_irq(struct dp_catalog *dp_catalog, bool enable);
void dp_catalog_ctrl_hpd_config(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_intr_status(struct dp_catalog *dp_catalog);
void dp_catalog_ctrl_phy_reset(struct dp_catalog *dp_catalog);
void dp_catalog_ctrl_phy_lane_cfg(struct dp_catalog *dp_catalog, bool flipped,
u8 lane_cnt);
Expand Down
1 change: 0 additions & 1 deletion drivers/gpu/drm/msm/dp/dp_ctrl.c
Original file line number Diff line number Diff line change
Expand Up @@ -1563,7 +1563,6 @@ int dp_ctrl_on(struct dp_ctrl *dp_ctrl)
rate = ctrl->panel->link_info.rate;

dp_power_clk_enable(ctrl->power, DP_CORE_PM, true);
dp_catalog_ctrl_hpd_config(ctrl->catalog, true);

if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) {
DRM_DEBUG_DP("using phy test link parameters\n");
Expand Down
108 changes: 101 additions & 7 deletions drivers/gpu/drm/msm/dp/dp_display.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "dp_power.h"
#include "dp_catalog.h"
#include "dp_aux.h"
#include "dp_reg.h"
#include "dp_link.h"
#include "dp_panel.h"
#include "dp_ctrl.h"
Expand All @@ -36,6 +37,7 @@ struct dp_display_private {
bool power_on;
bool hpd_irq_on;
bool audio_supported;
atomic_t hpd_isr_status;

struct platform_device *pdev;
struct dentry *root;
Expand All @@ -54,6 +56,8 @@ struct dp_display_private {
struct dp_usbpd_cb usbpd_cb;
struct dp_display_mode dp_mode;
struct msm_dp dp_display;

struct delayed_work config_hpd_work;
};

static const struct of_device_id dp_dt_match[] = {
Expand All @@ -64,13 +68,75 @@ static const struct of_device_id dp_dt_match[] = {
static irqreturn_t dp_display_irq(int irq, void *dev_id)
{
struct dp_display_private *dp = dev_id;
irqreturn_t ret = IRQ_HANDLED;
u32 hpd_isr_status;

if (!dp) {
DRM_ERROR("invalid data\n");
return IRQ_NONE;
}

hpd_isr_status = dp_catalog_hpd_get_intr_status(dp->catalog);

if (hpd_isr_status & DP_DP_HPD_INT_MASK) {
atomic_set(&dp->hpd_isr_status, hpd_isr_status);
ret = IRQ_WAKE_THREAD;
}

/* DP controller isr */
dp_ctrl_isr(dp->ctrl);

/* DP aux isr */
dp_aux_isr(dp->aux);

return ret;
}

static irqreturn_t dp_display_hpd_isr_work(int irq, void *data)
{
struct dp_display_private *dp;
struct dp_usbpd *hpd;
u32 isr = 0;

dp = (struct dp_display_private *)data;
if (!dp)
return IRQ_NONE;

isr = atomic_read(&dp->hpd_isr_status);

/* reset to default */
atomic_set(&dp->hpd_isr_status, 0);

hpd = dp->usbpd;
if (!hpd)
return IRQ_NONE;

if (isr & DP_DP_HPD_PLUG_INT_MASK &&
isr & DP_DP_HPD_STATE_STATUS_CONNECTED) {
hpd->hpd_high = 1;
dp->usbpd_cb.configure(&dp->pdev->dev);
} else if (isr & DP_DP_HPD_UNPLUG_INT_MASK &&
(isr & DP_DP_HPD_STATE_STATUS_MASK) ==
DP_DP_HPD_STATE_STATUS_DISCONNECTED) {

/* disable HPD plug interrupt until disconnect is done
*/
dp_catalog_hpd_config_intr(dp->catalog,
DP_DP_HPD_PLUG_INT_MASK | DP_DP_IRQ_HPD_INT_MASK,
false);

hpd->hpd_high = 0;

/* We don't need separate work for disconnect as
* connect/attention interrupts are disabled
*/
dp->usbpd_cb.disconnect(&dp->pdev->dev);

dp_catalog_hpd_config_intr(dp->catalog,
DP_DP_HPD_PLUG_INT_MASK | DP_DP_IRQ_HPD_INT_MASK,
true);
}

return IRQ_HANDLED;
}

Expand Down Expand Up @@ -212,8 +278,6 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp)
int rc = 0;
struct edid *edid;

dp_aux_init(dp->aux);

if (dp->link->psm_enabled)
goto notify;

Expand Down Expand Up @@ -270,10 +334,6 @@ static void dp_display_host_deinit(struct dp_display_private *dp)
return;
}

dp_ctrl_host_deinit(dp->ctrl);
dp_aux_deinit(dp->aux);
dp_power_deinit(dp->power);
disable_irq(dp->irq);
dp->core_initialized = false;
}

Expand Down Expand Up @@ -630,7 +690,8 @@ int dp_display_request_irq(struct msm_dp *dp_display)
return rc;
}

rc = devm_request_irq(&dp->pdev->dev, dp->irq, dp_display_irq,
rc = devm_request_threaded_irq(&dp->pdev->dev, dp->irq,
dp_display_irq, dp_display_hpd_isr_work,
IRQF_TRIGGER_HIGH, "dp_display_isr", dp);
if (rc < 0) {
DRM_ERROR("failed to request IRQ%u: %d\n",
Expand Down Expand Up @@ -829,6 +890,39 @@ void __exit msm_dp_unregister(void)
platform_driver_unregister(&dp_display_driver);
}

static void dp_display_config_hpd_work(struct work_struct *work)
{
struct dp_display_private *dp;
struct delayed_work *dw = to_delayed_work(work);

dp = container_of(dw, struct dp_display_private, config_hpd_work);

dp_display_host_init(dp);
dp_catalog_ctrl_hpd_config(dp->catalog);

/* set default to 0 */
atomic_set(&dp->hpd_isr_status, 0);

/* Enable interrupt first time
* we are leaving dp clocks on during disconnect
* and never disable interrupt
*/
enable_irq(dp->irq);
}

void msm_dp_irq_postinstall(struct msm_dp *dp_display)
{
struct dp_display_private *dp;

if (!dp_display)
return;

dp = container_of(dp_display, struct dp_display_private, dp_display);

INIT_DELAYED_WORK(&dp->config_hpd_work, dp_display_config_hpd_work);
queue_delayed_work(system_wq, &dp->config_hpd_work, HZ * 10);
}

int msm_dp_modeset_init(struct msm_dp *dp_display, struct drm_device *dev,
struct drm_encoder *encoder)
{
Expand Down
12 changes: 12 additions & 0 deletions drivers/gpu/drm/msm/dp/dp_reg.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,22 @@
#define DP_DP_IRQ_HPD_INT_MASK (0x00000002)
#define DP_DP_HPD_REPLUG_INT_MASK (0x00000004)
#define DP_DP_HPD_UNPLUG_INT_MASK (0x00000008)
#define DP_DP_HPD_INT_MASK (DP_DP_HPD_PLUG_INT_MASK | \
DP_DP_IRQ_HPD_INT_MASK | \
DP_DP_HPD_REPLUG_INT_MASK | \
DP_DP_HPD_UNPLUG_INT_MASK)
#define DP_DP_HPD_STATE_STATUS_CONNECTED (0x40000000)
#define DP_DP_HPD_STATE_STATUS_PENDING (0x20000000)
#define DP_DP_HPD_STATE_STATUS_DISCONNECTED (0x00000000)
#define DP_DP_HPD_STATE_STATUS_MASK (0xE0000000)

#define REG_DP_DP_HPD_REFTIMER (0x00000018)
#define DP_DP_HPD_REFTIMER_ENABLE (1 << 16)

#define REG_DP_DP_HPD_EVENT_TIME_0 (0x0000001C)
#define REG_DP_DP_HPD_EVENT_TIME_1 (0x00000020)
#define DP_DP_HPD_EVENT_TIME_0_VAL (0x3E800FA)
#define DP_DP_HPD_EVENT_TIME_1_VAL (0x1F407D0)

#define REG_DP_AUX_CTRL (0x00000030)
#define DP_AUX_CTRL_ENABLE (0x00000001)
Expand Down
6 changes: 6 additions & 0 deletions drivers/gpu/drm/msm/msm_drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,7 @@ int msm_dp_display_disable(struct msm_dp *dp, struct drm_encoder *encoder);
void msm_dp_display_mode_set(struct msm_dp *dp, struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
void msm_dp_irq_postinstall(struct msm_dp *dp_display);

#else
static inline int __init msm_dp_register(void)
Expand Down Expand Up @@ -426,6 +427,11 @@ static inline void msm_dp_display_mode_set(struct msm_dp *dp,
struct drm_display_mode *adjusted_mode)
{
}

static inline void msm_dp_irq_postinstall(struct msm_dp *dp_display)
{
}

#endif

void __init msm_mdp_register(void);
Expand Down

0 comments on commit 220b856

Please sign in to comment.