drm/nv50: punt hotplug irq handling out to workqueue
authorBen Skeggs <bskeggs@redhat.com>
Tue, 30 Mar 2010 05:14:41 +0000 (15:14 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Fri, 9 Apr 2010 00:15:40 +0000 (10:15 +1000)
On DP outputs we'll likely end up running vbios init tables here, which
may sleep.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/nouveau_drv.h
drivers/gpu/drm/nouveau/nouveau_irq.c
drivers/gpu/drm/nouveau/nv50_display.c
drivers/gpu/drm/nouveau/nv50_display.h

index f7d4d2a10052530f5e7adb26ea40bccc75f24715..c4f5658b9e4985b747950a2f06312629b76b4969 100644 (file)
@@ -520,6 +520,7 @@ struct drm_nouveau_private {
 
        struct workqueue_struct *wq;
        struct work_struct irq_work;
+       struct work_struct hpd_work;
 
        struct list_head vbl_waiting;
 
index 2bd59a92fee50439ddd892c96f839f909af50bf2..13e73cee4c44d6fa2764f7ff3ff710148f545c3e 100644 (file)
@@ -51,6 +51,7 @@ nouveau_irq_preinstall(struct drm_device *dev)
 
        if (dev_priv->card_type == NV_50) {
                INIT_WORK(&dev_priv->irq_work, nv50_display_irq_handler_bh);
+               INIT_WORK(&dev_priv->hpd_work, nv50_display_irq_hotplug_bh);
                INIT_LIST_HEAD(&dev_priv->vbl_waiting);
        }
 }
index fd00f4000f14fc20335954fd7a59cbc7cb0e1f09..649db4c1b690677689dbba5627781f9efc16ce8c 100644 (file)
@@ -887,10 +887,12 @@ nv50_display_error_handler(struct drm_device *dev)
        nv_wr32(dev, NV50_PDISPLAY_TRAPPED_ADDR, 0x90000000);
 }
 
-static void
-nv50_display_irq_hotplug(struct drm_device *dev)
+void
+nv50_display_irq_hotplug_bh(struct work_struct *work)
 {
-       struct drm_nouveau_private *dev_priv = dev->dev_private;
+       struct drm_nouveau_private *dev_priv =
+               container_of(work, struct drm_nouveau_private, hpd_work);
+       struct drm_device *dev = dev_priv->dev;
        struct drm_connector *connector;
        const uint32_t gpio_reg[4] = { 0xe104, 0xe108, 0xe280, 0xe284 };
        uint32_t unplug_mask, plug_mask, change_mask;
@@ -951,8 +953,10 @@ nv50_display_irq_handler(struct drm_device *dev)
        struct drm_nouveau_private *dev_priv = dev->dev_private;
        uint32_t delayed = 0;
 
-       while (nv_rd32(dev, NV50_PMC_INTR_0) & NV50_PMC_INTR_0_HOTPLUG)
-               nv50_display_irq_hotplug(dev);
+       if (nv_rd32(dev, NV50_PMC_INTR_0) & NV50_PMC_INTR_0_HOTPLUG) {
+               if (!work_pending(&dev_priv->hpd_work))
+                       queue_work(dev_priv->wq, &dev_priv->hpd_work);
+       }
 
        while (nv_rd32(dev, NV50_PMC_INTR_0) & NV50_PMC_INTR_0_DISPLAY) {
                uint32_t intr0 = nv_rd32(dev, NV50_PDISPLAY_INTR_0);
index 3ae8d0725f635d8b41b0e797f5b1b30f4c7a79d5..581d405ac014606041e253c7830c137569e63af6 100644 (file)
@@ -37,6 +37,7 @@
 
 void nv50_display_irq_handler(struct drm_device *dev);
 void nv50_display_irq_handler_bh(struct work_struct *work);
+void nv50_display_irq_hotplug_bh(struct work_struct *work);
 int nv50_display_init(struct drm_device *dev);
 int nv50_display_create(struct drm_device *dev);
 int nv50_display_destroy(struct drm_device *dev);