diff --git a/drivers/staging/omapdrm/omap_plane.c b/drivers/staging/omapdrm/omap_plane.c
index 7997be7..8d8aa5b 100644
--- a/drivers/staging/omapdrm/omap_plane.c
+++ b/drivers/staging/omapdrm/omap_plane.c
@@ -82,7 +82,7 @@ static void dispc_isr(void *arg, uint32_t mask)
struct omap_plane *omap_plane = to_omap_plane(plane);
struct omap_drm_private *priv = plane->dev->dev_private;
- omap_dispc_unregister_isr(dispc_isr, plane,
+ omap_dispc_unregister_isr_nosync(dispc_isr, plane,
id2irq[omap_plane->ovl->id]);
queue_work(priv->wq, &omap_plane->work);
diff --git a/drivers/video/omap2/dss/apply.c b/drivers/video/omap2/dss/apply.c
index 0fefc68..9386834 100644
--- a/drivers/video/omap2/dss/apply.c
+++ b/drivers/video/omap2/dss/apply.c
@@ -847,7 +847,7 @@ static void dss_unregister_vsync_isr(void)
for (i = 0; i < num_mgrs; ++i)
mask |= dispc_mgr_get_framedone_irq(i);
- r = omap_dispc_unregister_isr(dss_apply_irq_handler, NULL, mask);
+ r = omap_dispc_unregister_isr_nosync(dss_apply_irq_handler, NULL, mask);
WARN_ON(r);
dss_data.irq_enabled = false;
diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c
index ee9e296..6032252 100644
--- a/drivers/video/omap2/dss/dispc.c
+++ b/drivers/video/omap2/dss/dispc.c
@@ -3320,7 +3320,8 @@ err:
}
EXPORT_SYMBOL(omap_dispc_register_isr);
-int omap_dispc_unregister_isr(omap_dispc_isr_t isr, void *arg, u32 mask)
+/* WARNING: callback might be executed even after this function returns! */
+int omap_dispc_unregister_isr_nosync(omap_dispc_isr_t isr, void *arg, u32 mask)
{
int i;
unsigned long flags;@@ -3352,6 +3353,37 @@ int omap_dispc_unregister_isr(omap_dispc_isr_t isr, void *arg, u32 mask)
return ret;
}
+EXPORT_SYMBOL(omap_dispc_unregister_isr_nosync);
+
+/*
+ * Ensure that callback <isr> will NOT be executed after this function
+ * returns. Must be called from sleepable context, though!
+ */
+int omap_dispc_unregister_isr(omap_dispc_isr_t isr, void *arg, u32 mask)
+{
+ int ret;
+
+ ret = omap_dispc_unregister_isr_nosync(isr, arg, mask);
+
+ /*
+ * Task context is not really needed. But if we're called from atomic
+ * context, it is probably from DISPC IRQ, where we will deadlock.
+ * So use might_sleep() to catch potential deadlocks.
+ */
+ might_sleep();
+
+ /*
+ * DISPC IRQ executes callbacks with dispc.irq_lock released. Hence
+ * unregister_isr() and DISPC IRQ might be running in parallel on
+ * different CPUs. So there is a chance that a callback is executed
+ * even though it has been unregistered. Add a barrier, in order to
+ * ensure that after returning from this function, the new DISPC IRQ
+ * will use an updated callback array, and NOT its cached copy.
+ */
+ synchronize_irq(dispc.irq);
+
+ return ret;
+}
EXPORT_SYMBOL(omap_dispc_unregister_isr);
#ifdef DEBUGdiff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c
index b07e886..c0fcb68 100644
--- a/drivers/video/omap2/dss/dsi.c
+++ b/drivers/video/omap2/dss/dsi.c
@@ -960,6 +960,11 @@ static int dsi_unregister_isr(struct platform_device *dsidev,
spin_unlock_irqrestore(&dsi->irq_lock, flags);
+ might_sleep();
+
+ /* See notes for dispc.c:omap_dispc_unregister_isr() . */
+ synchronize_irq(dsi->irq);
+
return r;
}
@@ -1002,6 +1007,11 @@ static int dsi_unregister_isr_vc(struct platform_device *dsidev, int channel,
spin_unlock_irqrestore(&dsi->irq_lock, flags);
+ might_sleep();
+
+ /* See notes for dispc.c:omap_dispc_unregister_isr() . */
+ synchronize_irq(dsi->irq);
+
return r;
}
@@ -1042,6 +1052,11 @@ static int dsi_unregister_isr_cio(struct platform_device *dsidev,
spin_unlock_irqrestore(&dsi->irq_lock, flags);
+ might_sleep();
+
+ /* See notes for dispc.c:omap_dispc_unregister_isr() . */
+ synchronize_irq(dsi->irq);
+
return r;
}
diff --git a/include/video/omapdss.h b/include/video/omapdss.h
index a6267a2..769f981 100644
--- a/include/video/omapdss.h
+++ b/include/video/omapdss.h
@@ -708,6 +708,7 @@ void omapdss_default_get_timings(struct omap_dss_device *dssdev,
typedef void (*omap_dispc_isr_t) (void *arg, u32 mask);
int omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask);
int omap_dispc_unregister_isr(omap_dispc_isr_t isr, void *arg, u32 mask);
+int omap_dispc_unregister_isr_nosync(omap_dispc_isr_t isr, void *arg, u32 mask);
int omap_dispc_wait_for_irq_timeout(u32 irqmask, unsigned long timeout);
int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask,
--
1.7.10.4