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 <seanpaul@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/49349
Reviewed-by: Stéphane Marchesin <marcheu@chromium.org>
#define get_hdmi_context(dev) platform_get_drvdata(to_platform_device(dev))
#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;
struct hdmi_resources {
struct clk *hdmi;
struct clk *sclk_hdmi;
unsigned int external_irq;
unsigned int internal_irq;
unsigned int curr_irq;
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;
struct i2c_client *ddc_port;
struct i2c_client *hdmiphy_port;
/*
* Handle hotplug events outside the interrupt handler proper.
*/
/*
* 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);
}
drm_helper_hpd_irq_event(hdata->drm_dev);
}
static irqreturn_t hdmi_irq_handler(int irq, void *arg)
{
struct hdmi_context *hdata = arg;
static irqreturn_t hdmi_irq_handler(int irq, void *arg)
{
struct hdmi_context *hdata = arg;
+ 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 */
if (hdata->is_hdmi_powered_on) {
intc_flag = hdmi_reg_read(hdata, HDMI_INTC_FLAG);
/* clearing flags for HPD plug/unplug */
hdmi_reg_writemask(hdata, HDMI_INTC_FLAG, ~0,
HDMI_INTC_FLAG_HPD_PLUG);
}
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)
}
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));
hdata->external_irq = gpio_to_irq(hdata->hpd_gpio);
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");
ret = request_irq(hdata->internal_irq, hdmi_irq_handler,
IRQF_SHARED, "int_hdmi", hdata);
if (ret) {
DRM_ERROR("request int interrupt failed.\n");
}
disable_irq(hdata->internal_irq);
}
disable_irq(hdata->internal_irq);
}
disable_irq(hdata->external_irq);
}
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);
if (of_device_is_compatible(dev->of_node,
"samsung,exynos5-hdmi")) {
ret = hdmi_register_audio_device(pdev);
free_irq(hdata->external_irq, hdata);
err_int_irq:
free_irq(hdata->internal_irq, hdata);
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:
err_hdmiphy:
put_device(&hdata->hdmiphy_port->dev);
err_ddc:
free_irq(hdata->internal_irq, hdata);
free_irq(hdata->external_irq, hdata);
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);
clk_disable(res->hdmi);
clk_disable(res->sclk_hdmi);