From 8c15e0b8c5ffee5baaefc385ecfd7996d87973be Mon Sep 17 00:00:00 2001 From: Wyon Bi Date: Thu, 2 Jan 2020 17:48:27 +0800 Subject: [PATCH] drm/bridge: analogix_dp: Fix hpd handling for GPIO Change-Id: I0d62201095ab82f5ed0ddcfd53abaef6089a2e9d Signed-off-by: Wyon Bi --- .../drm/bridge/analogix/analogix_dp_core.c | 59 +++++++++++++------ .../gpu/drm/bridge/analogix/analogix_dp_reg.c | 41 +++++-------- 2 files changed, 56 insertions(+), 44 deletions(-) diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index 6cc6e9cf061b..14936864376b 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -800,25 +800,44 @@ static int analogix_dp_enable_scramble(struct analogix_dp_device *dp, return ret < 0 ? ret : 0; } +static irqreturn_t analogix_dp_hpd_irq_handler(int irq, void *arg) +{ + struct analogix_dp_device *dp = arg; + + if (dp->drm_dev) + drm_helper_hpd_irq_event(dp->drm_dev); + + return IRQ_HANDLED; +} + static irqreturn_t analogix_dp_hardirq(int irq, void *arg) { struct analogix_dp_device *dp = arg; - irqreturn_t ret = IRQ_NONE; enum dp_irq_type irq_type; + int ret; + + ret = pm_runtime_get_sync(dp->dev); + if (ret < 0) + return IRQ_NONE; irq_type = analogix_dp_get_irq_type(dp); - if (irq_type != DP_IRQ_TYPE_UNKNOWN) { + if (irq_type != DP_IRQ_TYPE_UNKNOWN) analogix_dp_mute_hpd_interrupt(dp); - ret = IRQ_WAKE_THREAD; - } - return ret; + pm_runtime_put_sync(dp->dev); + + return IRQ_WAKE_THREAD; } static irqreturn_t analogix_dp_irq_thread(int irq, void *arg) { struct analogix_dp_device *dp = arg; enum dp_irq_type irq_type; + int ret; + + ret = pm_runtime_get_sync(dp->dev); + if (ret < 0) + return IRQ_NONE; irq_type = analogix_dp_get_irq_type(dp); if (irq_type & DP_IRQ_TYPE_HP_CABLE_IN || @@ -833,6 +852,8 @@ static irqreturn_t analogix_dp_irq_thread(int irq, void *arg) analogix_dp_unmute_hpd_interrupt(dp); } + pm_runtime_put_sync(dp->dev); + return IRQ_HANDLED; } @@ -1567,7 +1588,6 @@ analogix_dp_probe(struct device *dev, struct analogix_dp_plat_data *plat_data) struct platform_device *pdev = to_platform_device(dev); struct analogix_dp_device *dp; struct resource *res; - unsigned int irq_flags; int ret; if (!plat_data) { @@ -1640,20 +1660,21 @@ analogix_dp_probe(struct device *dev, struct analogix_dp_plat_data *plat_data) } if (dp->hpd_gpiod) { - /* - * Set up the hotplug GPIO from the device tree as an interrupt. - * Simply specifying a different interrupt in the device tree - * doesn't work since we handle hotplug rather differently when - * using a GPIO. We also need the actual GPIO specifier so - * that we can get the current state of the GPIO. - */ - dp->irq = gpiod_to_irq(dp->hpd_gpiod); - irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; - } else { - dp->irq = platform_get_irq(pdev, 0); - irq_flags = 0; + ret = devm_request_threaded_irq(dev, + gpiod_to_irq(dp->hpd_gpiod), + NULL, + analogix_dp_hpd_irq_handler, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + "analogix-hpd", dp); + if (ret) { + dev_err(dev, "failed to request hpd IRQ: %d\n", ret); + return ERR_PTR(ret); + } } + dp->irq = platform_get_irq(pdev, 0); if (dp->irq == -ENXIO) { dev_err(&pdev->dev, "failed to get irq\n"); return ERR_PTR(-ENODEV); @@ -1663,7 +1684,7 @@ analogix_dp_probe(struct device *dev, struct analogix_dp_plat_data *plat_data) ret = devm_request_threaded_irq(&pdev->dev, dp->irq, analogix_dp_hardirq, analogix_dp_irq_thread, - irq_flags, "analogix-dp", dp); + 0, "analogix-dp", dp); if (ret) { dev_err(&pdev->dev, "failed to request irq\n"); return ERR_PTR(ret); diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c index ed1c00564b2a..776f4e05a42b 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c @@ -22,7 +22,6 @@ #define COMMON_INT_MASK_2 0 #define COMMON_INT_MASK_3 0 #define COMMON_INT_MASK_4 (HOTPLUG_CHG | HPD_LOST | PLUG) -#define INT_STA_MASK INT_HPD static void analogix_dp_write(struct analogix_dp_device *dp, u32 reg, u32 val) { @@ -203,11 +202,10 @@ void analogix_dp_config_interrupt(struct analogix_dp_device *dp) reg = COMMON_INT_MASK_3; analogix_dp_write(dp, ANALOGIX_DP_COMMON_INT_MASK_3, reg); - reg = COMMON_INT_MASK_4; - analogix_dp_write(dp, ANALOGIX_DP_COMMON_INT_MASK_4, reg); - - reg = INT_STA_MASK; - analogix_dp_write(dp, ANALOGIX_DP_INT_STA_MASK, reg); + if (dp->force_hpd || dp->hpd_gpiod) + analogix_dp_mute_hpd_interrupt(dp); + else + analogix_dp_unmute_hpd_interrupt(dp); } void analogix_dp_mute_hpd_interrupt(struct analogix_dp_device *dp) @@ -220,7 +218,7 @@ void analogix_dp_mute_hpd_interrupt(struct analogix_dp_device *dp) analogix_dp_write(dp, ANALOGIX_DP_COMMON_INT_MASK_4, reg); reg = analogix_dp_read(dp, ANALOGIX_DP_INT_STA_MASK); - reg &= ~INT_STA_MASK; + reg &= ~INT_HPD; analogix_dp_write(dp, ANALOGIX_DP_INT_STA_MASK, reg); } @@ -232,7 +230,8 @@ void analogix_dp_unmute_hpd_interrupt(struct analogix_dp_device *dp) reg = COMMON_INT_MASK_4; analogix_dp_write(dp, ANALOGIX_DP_COMMON_INT_MASK_4, reg); - reg = INT_STA_MASK; + reg = analogix_dp_read(dp, ANALOGIX_DP_INT_STA_MASK); + reg |= INT_HPD; analogix_dp_write(dp, ANALOGIX_DP_INT_STA_MASK, reg); } @@ -437,27 +436,19 @@ enum dp_irq_type analogix_dp_get_irq_type(struct analogix_dp_device *dp) { u32 reg; - if (dp->hpd_gpiod) { - reg = gpiod_get_value(dp->hpd_gpiod); - if (reg) - return DP_IRQ_TYPE_HP_CABLE_IN; - else - return DP_IRQ_TYPE_HP_CABLE_OUT; - } else { - /* Parse hotplug interrupt status register */ - reg = analogix_dp_read(dp, ANALOGIX_DP_COMMON_INT_STA_4); + /* Parse hotplug interrupt status register */ + reg = analogix_dp_read(dp, ANALOGIX_DP_COMMON_INT_STA_4); - if (reg & PLUG) - return DP_IRQ_TYPE_HP_CABLE_IN; + if (reg & PLUG) + return DP_IRQ_TYPE_HP_CABLE_IN; - if (reg & HPD_LOST) - return DP_IRQ_TYPE_HP_CABLE_OUT; + if (reg & HPD_LOST) + return DP_IRQ_TYPE_HP_CABLE_OUT; - if (reg & HOTPLUG_CHG) - return DP_IRQ_TYPE_HP_CHANGE; + if (reg & HOTPLUG_CHG) + return DP_IRQ_TYPE_HP_CHANGE; - return DP_IRQ_TYPE_UNKNOWN; - } + return DP_IRQ_TYPE_UNKNOWN; } void analogix_dp_reset_aux(struct analogix_dp_device *dp)