diff options
author | Tanmay Shah <tanmay@codeaurora.org> | 2020-08-27 14:16:58 -0700 |
---|---|---|
committer | Rob Clark <robdclark@chromium.org> | 2020-09-15 10:54:34 -0700 |
commit | 220b856a3d3742a22831cb6cd94e48133a58d30e (patch) | |
tree | b2ab2fcb2a4b41a61e5cdd94156d7afc16996cf2 /drivers/gpu/drm/msm/dp/dp_display.c | |
parent | a10476e45026fefe782c444a39c6fc5d4999a6ac (diff) |
drm/msm/dp: Add Display Port HPD feature
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>
Diffstat (limited to 'drivers/gpu/drm/msm/dp/dp_display.c')
-rw-r--r-- | drivers/gpu/drm/msm/dp/dp_display.c | 108 |
1 files changed, 101 insertions, 7 deletions
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index c8851f4bbf72..925c89720a16 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -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" @@ -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; @@ -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[] = { @@ -64,6 +68,20 @@ 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); @@ -71,6 +89,54 @@ static irqreturn_t dp_display_irq(int irq, void *dev_id) /* 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; } @@ -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; @@ -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; } @@ -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", @@ -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) { |