From 7ecb1cc1462ef33a4cbfd62e14ebc6efc18635f6 Mon Sep 17 00:00:00 2001 From: Sean Paul Date: Fri, 26 Apr 2013 16:06:43 -0400 Subject: [PATCH] drm/exynos: Debounce gpio hotplug interrupts MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Debounce the gpio (external) hotplug interrupts. This patch debounces hotplug interrupts generated while the HDMI block is off. The reason this is needed is that we get multiple (5) interrupts every time a monitor is inserted which causes us to needlessly enable and disable the IP block. BUG=chromium:220033 TEST=Tested using an HDMI analyzer. Many hotplugs without any 20s freeze Change-Id: I9c5d61364790f4110fc758a93fabac48b646b3fa Signed-off-by: Sean Paul Reviewed-on: https://gerrit.chromium.org/gerrit/49349 Reviewed-by: Stéphane Marchesin --- drivers/gpu/drm/exynos/exynos_hdmi.c | 42 +++++++++++++--------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index a884b495ec7b..153edeb058a9 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -47,6 +47,8 @@ #define get_hdmi_context(dev) platform_get_drvdata(to_platform_device(dev)) +#define HOTPLUG_DEBOUNCE_MS 1100 + struct hdmi_resources { struct clk *hdmi; struct clk *sclk_hdmi; @@ -144,8 +146,7 @@ struct hdmi_context { unsigned int external_irq; unsigned int internal_irq; unsigned int curr_irq; - struct workqueue_struct *wq; - struct work_struct hotplug_work; + struct timer_list hotplug_timer; struct i2c_client *ddc_port; struct i2c_client *hdmiphy_port; @@ -2099,10 +2100,9 @@ static struct exynos_panel_ops hdmi_ops = { /* * Handle hotplug events outside the interrupt handler proper. */ -static void hdmi_hotplug_func(struct work_struct *work) +static void hdmi_hotplug_timer_func(unsigned long data) { - struct hdmi_context *hdata = - container_of(work, struct hdmi_context, hotplug_work); + struct hdmi_context *hdata = (struct hdmi_context *)data; drm_helper_hpd_irq_event(hdata->drm_dev); } @@ -2110,7 +2110,8 @@ static void hdmi_hotplug_func(struct work_struct *work) static irqreturn_t hdmi_irq_handler(int irq, void *arg) { struct hdmi_context *hdata = arg; - u32 intc_flag; + u32 intc_flag, time_ms = HOTPLUG_DEBOUNCE_MS; + if (hdata->is_hdmi_powered_on) { intc_flag = hdmi_reg_read(hdata, HDMI_INTC_FLAG); /* clearing flags for HPD plug/unplug */ @@ -2126,10 +2127,17 @@ static irqreturn_t hdmi_irq_handler(int irq, void *arg) hdmi_reg_writemask(hdata, HDMI_INTC_FLAG, ~0, HDMI_INTC_FLAG_HPD_PLUG); } + + /* + * No need to debounce if we're powered on, the hardware + * has already done it for us. + */ + time_ms = 0; } if (hdata->drm_dev && hdata->hpd_handle) - queue_work(hdata->wq, &hdata->hotplug_work); + mod_timer(&hdata->hotplug_timer, + jiffies + msecs_to_jiffies(time_ms)); return IRQ_HANDLED; } @@ -2393,21 +2401,11 @@ static int __devinit hdmi_probe(struct platform_device *pdev) hdata->external_irq = gpio_to_irq(hdata->hpd_gpio); - /* create workqueue and hotplug work */ - hdata->wq = alloc_workqueue("exynos-drm-hdmi", - WQ_UNBOUND | WQ_NON_REENTRANT, 1); - if (hdata->wq == NULL) { - DRM_ERROR("Failed to create workqueue.\n"); - ret = -ENOMEM; - goto err_hdmiphy; - } - INIT_WORK(&hdata->hotplug_work, hdmi_hotplug_func); - ret = request_irq(hdata->internal_irq, hdmi_irq_handler, IRQF_SHARED, "int_hdmi", hdata); if (ret) { DRM_ERROR("request int interrupt failed.\n"); - goto err_workqueue; + goto err_hdmiphy; } disable_irq(hdata->internal_irq); @@ -2420,6 +2418,9 @@ static int __devinit hdmi_probe(struct platform_device *pdev) } disable_irq(hdata->external_irq); + setup_timer(&hdata->hotplug_timer, hdmi_hotplug_timer_func, + (unsigned long)hdata); + if (of_device_is_compatible(dev->of_node, "samsung,exynos5-hdmi")) { ret = hdmi_register_audio_device(pdev); @@ -2445,8 +2446,6 @@ err_ext_irq: free_irq(hdata->external_irq, hdata); err_int_irq: free_irq(hdata->internal_irq, hdata); - err_workqueue: - destroy_workqueue(hdata->wq); err_hdmiphy: put_device(&hdata->hdmiphy_port->dev); err_ddc: @@ -2478,8 +2477,7 @@ static int __devexit hdmi_remove(struct platform_device *pdev) free_irq(hdata->internal_irq, hdata); free_irq(hdata->external_irq, hdata); - cancel_work_sync(&hdata->hotplug_work); - destroy_workqueue(hdata->wq); + del_timer(&hdata->hotplug_timer); clk_disable(res->hdmi); clk_disable(res->sclk_hdmi); -- 2.20.1