summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/msm/dp/dp_display.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/msm/dp/dp_display.c')
-rw-r--r--drivers/gpu/drm/msm/dp/dp_display.c108
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)
{