From 601aff23373b69de250ef58b398419424f25ca26 Mon Sep 17 00:00:00 2001 From: Zhang Yubing Date: Mon, 22 Apr 2024 17:42:19 +0800 Subject: [PATCH] drm/rockchip: dw-dp: optimize the logic to deal with hpd Detecting the hpd irq in the gpio irq handler, the hpd type will overwrite by the next gpio irq. So the hpd work can't recognize the hpd irq. Use a state machine to deal with the hpd status to avoid this issue happen. Change-Id: I1214b1a281cbb8e82431bcc1c2b4a0856d64a7a0 Signed-off-by: Zhang Yubing --- drivers/gpu/drm/rockchip/dw-dp.c | 62 +++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/rockchip/dw-dp.c b/drivers/gpu/drm/rockchip/dw-dp.c index 9cab6397f2d1..114f8b240157 100644 --- a/drivers/gpu/drm/rockchip/dw-dp.c +++ b/drivers/gpu/drm/rockchip/dw-dp.c @@ -357,7 +357,15 @@ struct dw_dp_sdp { int stream_id; }; +enum gpio_hpd_state { + GPIO_STATE_IDLE, + GPIO_STATE_PLUG, + GPIO_STATE_UNPLUG, +}; + struct dw_dp_hotplug { + struct delayed_work state_work; + enum gpio_hpd_state state; bool long_hpd; bool status; }; @@ -2750,28 +2758,56 @@ static int dw_dp_video_enable(struct dw_dp *dp, struct dw_dp_video *video, int s return 0; } +static void dw_dp_gpio_hpd_state_work(struct work_struct *work) +{ + struct dw_dp_hotplug *hotplug = container_of(to_delayed_work(work), struct dw_dp_hotplug, + state_work); + struct dw_dp *dp = container_of(hotplug, struct dw_dp, hotplug); + + mutex_lock(&dp->irq_lock); + if (hotplug->state == GPIO_STATE_UNPLUG) { + dev_dbg(dp->dev, "hpd state unplug to idle\n"); + dp->hotplug.long_hpd = true; + dp->hotplug.status = false; + dp->hotplug.state = GPIO_STATE_IDLE; + schedule_work(&dp->hpd_work); + } + mutex_unlock(&dp->irq_lock); +} + static irqreturn_t dw_dp_hpd_irq_handler(int irq, void *arg) { struct dw_dp *dp = arg; bool hpd = dw_dp_detect(dp); + dev_dbg(dp->dev, "trigger gpio to %s\n", hpd ? "high" : "low"); mutex_lock(&dp->irq_lock); - - dp->hotplug.long_hpd = true; - - if (dp->hotplug.status && !hpd) { - usleep_range(2000, 2001); - - hpd = dw_dp_detect(dp); - if (hpd) + if (dp->hotplug.state == GPIO_STATE_IDLE) { + if (hpd) { + dev_dbg(dp->dev, "hpd state idle to plug\n"); + dp->hotplug.long_hpd = true; + dp->hotplug.status = hpd; + dp->hotplug.state = GPIO_STATE_PLUG; + schedule_work(&dp->hpd_work); + } + } else if (dp->hotplug.state == GPIO_STATE_PLUG) { + if (!hpd) { + dev_dbg(dp->dev, "hpd state plug to unplug\n"); + dp->hotplug.state = GPIO_STATE_UNPLUG; + schedule_delayed_work(&dp->hotplug.state_work, msecs_to_jiffies(2)); + } + } else if (dp->hotplug.state == GPIO_STATE_UNPLUG) { + if (hpd) { + dev_dbg(dp->dev, "hpd state unplug to plug\n"); + cancel_delayed_work_sync(&dp->hotplug.state_work); dp->hotplug.long_hpd = false; + dp->hotplug.status = hpd; + dp->hotplug.state = GPIO_STATE_PLUG; + schedule_work(&dp->hpd_work); + } } - - dp->hotplug.status = hpd; - mutex_unlock(&dp->irq_lock); - schedule_work(&dp->hpd_work); return IRQ_HANDLED; } @@ -5350,6 +5386,7 @@ static int dw_dp_probe(struct platform_device *pdev) mutex_init(&dp->irq_lock); INIT_WORK(&dp->hpd_work, dw_dp_hpd_work); + INIT_DELAYED_WORK(&dp->hotplug.state_work, dw_dp_gpio_hpd_state_work); init_completion(&dp->complete); init_completion(&dp->hdcp_complete); @@ -5471,6 +5508,7 @@ static int dw_dp_remove(struct platform_device *pdev) component_del(dp->dev, &dw_dp_component_ops); cancel_work_sync(&dp->hpd_work); + cancel_delayed_work_sync(&dp->hotplug.state_work); return 0; }