diff options
author | Andy Lutomirski <luto@mit.edu> | 2010-11-16 18:40:52 -0500 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2010-11-17 23:39:07 -0500 |
commit | ab838338a2a9e0cb8346eb0cab9977be13e8dce5 (patch) | |
tree | cf8d058f811fe2b037db5d27f8c21277415efeeb /drivers | |
parent | c1b60ece91460214b5135360a375f3f6fbb59608 (diff) |
nouveau: Acknowledge HPD irq in handler, not bottom half
The old code generated an interrupt storm bad enough to completely
take down my system.
Signed-off-by: Andy Lutomirski <luto@mit.edu>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_drv.h | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_irq.c | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nv50_display.c | 35 |
3 files changed, 33 insertions, 9 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 10a8d4e78e58..1c7db64c03bf 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h | |||
@@ -584,6 +584,12 @@ struct drm_nouveau_private { | |||
584 | struct work_struct irq_work; | 584 | struct work_struct irq_work; |
585 | struct work_struct hpd_work; | 585 | struct work_struct hpd_work; |
586 | 586 | ||
587 | struct { | ||
588 | spinlock_t lock; | ||
589 | uint32_t hpd0_bits; | ||
590 | uint32_t hpd1_bits; | ||
591 | } hpd_state; | ||
592 | |||
587 | struct list_head vbl_waiting; | 593 | struct list_head vbl_waiting; |
588 | 594 | ||
589 | struct { | 595 | struct { |
diff --git a/drivers/gpu/drm/nouveau/nouveau_irq.c b/drivers/gpu/drm/nouveau/nouveau_irq.c index e2f2d59be3ea..7bfd9e6c9d67 100644 --- a/drivers/gpu/drm/nouveau/nouveau_irq.c +++ b/drivers/gpu/drm/nouveau/nouveau_irq.c | |||
@@ -60,6 +60,7 @@ nouveau_irq_preinstall(struct drm_device *dev) | |||
60 | if (dev_priv->card_type >= NV_50) { | 60 | if (dev_priv->card_type >= NV_50) { |
61 | INIT_WORK(&dev_priv->irq_work, nv50_display_irq_handler_bh); | 61 | INIT_WORK(&dev_priv->irq_work, nv50_display_irq_handler_bh); |
62 | INIT_WORK(&dev_priv->hpd_work, nv50_display_irq_hotplug_bh); | 62 | INIT_WORK(&dev_priv->hpd_work, nv50_display_irq_hotplug_bh); |
63 | spin_lock_init(&dev_priv->hpd_state.lock); | ||
63 | INIT_LIST_HEAD(&dev_priv->vbl_waiting); | 64 | INIT_LIST_HEAD(&dev_priv->vbl_waiting); |
64 | } | 65 | } |
65 | } | 66 | } |
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 55c9663ef2bf..f624c611ddea 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c | |||
@@ -1032,11 +1032,18 @@ nv50_display_irq_hotplug_bh(struct work_struct *work) | |||
1032 | struct drm_connector *connector; | 1032 | struct drm_connector *connector; |
1033 | const uint32_t gpio_reg[4] = { 0xe104, 0xe108, 0xe280, 0xe284 }; | 1033 | const uint32_t gpio_reg[4] = { 0xe104, 0xe108, 0xe280, 0xe284 }; |
1034 | uint32_t unplug_mask, plug_mask, change_mask; | 1034 | uint32_t unplug_mask, plug_mask, change_mask; |
1035 | uint32_t hpd0, hpd1 = 0; | 1035 | uint32_t hpd0, hpd1; |
1036 | 1036 | ||
1037 | hpd0 = nv_rd32(dev, 0xe054) & nv_rd32(dev, 0xe050); | 1037 | spin_lock_irq(&dev_priv->hpd_state.lock); |
1038 | hpd0 = dev_priv->hpd_state.hpd0_bits; | ||
1039 | dev_priv->hpd_state.hpd0_bits = 0; | ||
1040 | hpd1 = dev_priv->hpd_state.hpd1_bits; | ||
1041 | dev_priv->hpd_state.hpd1_bits = 0; | ||
1042 | spin_unlock_irq(&dev_priv->hpd_state.lock); | ||
1043 | |||
1044 | hpd0 &= nv_rd32(dev, 0xe050); | ||
1038 | if (dev_priv->chipset >= 0x90) | 1045 | if (dev_priv->chipset >= 0x90) |
1039 | hpd1 = nv_rd32(dev, 0xe074) & nv_rd32(dev, 0xe070); | 1046 | hpd1 &= nv_rd32(dev, 0xe070); |
1040 | 1047 | ||
1041 | plug_mask = (hpd0 & 0x0000ffff) | (hpd1 << 16); | 1048 | plug_mask = (hpd0 & 0x0000ffff) | (hpd1 << 16); |
1042 | unplug_mask = (hpd0 >> 16) | (hpd1 & 0xffff0000); | 1049 | unplug_mask = (hpd0 >> 16) | (hpd1 & 0xffff0000); |
@@ -1078,10 +1085,6 @@ nv50_display_irq_hotplug_bh(struct work_struct *work) | |||
1078 | helper->dpms(connector->encoder, DRM_MODE_DPMS_OFF); | 1085 | helper->dpms(connector->encoder, DRM_MODE_DPMS_OFF); |
1079 | } | 1086 | } |
1080 | 1087 | ||
1081 | nv_wr32(dev, 0xe054, nv_rd32(dev, 0xe054)); | ||
1082 | if (dev_priv->chipset >= 0x90) | ||
1083 | nv_wr32(dev, 0xe074, nv_rd32(dev, 0xe074)); | ||
1084 | |||
1085 | drm_helper_hpd_irq_event(dev); | 1088 | drm_helper_hpd_irq_event(dev); |
1086 | } | 1089 | } |
1087 | 1090 | ||
@@ -1092,8 +1095,22 @@ nv50_display_irq_handler(struct drm_device *dev) | |||
1092 | uint32_t delayed = 0; | 1095 | uint32_t delayed = 0; |
1093 | 1096 | ||
1094 | if (nv_rd32(dev, NV50_PMC_INTR_0) & NV50_PMC_INTR_0_HOTPLUG) { | 1097 | if (nv_rd32(dev, NV50_PMC_INTR_0) & NV50_PMC_INTR_0_HOTPLUG) { |
1095 | if (!work_pending(&dev_priv->hpd_work)) | 1098 | uint32_t hpd0_bits, hpd1_bits = 0; |
1096 | queue_work(dev_priv->wq, &dev_priv->hpd_work); | 1099 | |
1100 | hpd0_bits = nv_rd32(dev, 0xe054); | ||
1101 | nv_wr32(dev, 0xe054, hpd0_bits); | ||
1102 | |||
1103 | if (dev_priv->chipset >= 0x90) { | ||
1104 | hpd1_bits = nv_rd32(dev, 0xe074); | ||
1105 | nv_wr32(dev, 0xe074, hpd1_bits); | ||
1106 | } | ||
1107 | |||
1108 | spin_lock(&dev_priv->hpd_state.lock); | ||
1109 | dev_priv->hpd_state.hpd0_bits |= hpd0_bits; | ||
1110 | dev_priv->hpd_state.hpd1_bits |= hpd1_bits; | ||
1111 | spin_unlock(&dev_priv->hpd_state.lock); | ||
1112 | |||
1113 | queue_work(dev_priv->wq, &dev_priv->hpd_work); | ||
1097 | } | 1114 | } |
1098 | 1115 | ||
1099 | while (nv_rd32(dev, NV50_PMC_INTR_0) & NV50_PMC_INTR_0_DISPLAY) { | 1116 | while (nv_rd32(dev, NV50_PMC_INTR_0) & NV50_PMC_INTR_0_DISPLAY) { |