Skip to content

Commit

Permalink
nouveau: Acknowledge HPD irq in handler, not bottom half
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Andy Lutomirski authored and Ben Skeggs committed Nov 18, 2010
1 parent c1b60ec commit ab83833
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 9 deletions.
6 changes: 6 additions & 0 deletions drivers/gpu/drm/nouveau/nouveau_drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,12 @@ struct drm_nouveau_private {
struct work_struct irq_work;
struct work_struct hpd_work;

struct {
spinlock_t lock;
uint32_t hpd0_bits;
uint32_t hpd1_bits;
} hpd_state;

struct list_head vbl_waiting;

struct {
Expand Down
1 change: 1 addition & 0 deletions drivers/gpu/drm/nouveau/nouveau_irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,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);
spin_lock_init(&dev_priv->hpd_state.lock);
INIT_LIST_HEAD(&dev_priv->vbl_waiting);
}
}
Expand Down
35 changes: 26 additions & 9 deletions drivers/gpu/drm/nouveau/nv50_display.c
Original file line number Diff line number Diff line change
Expand Up @@ -1032,11 +1032,18 @@ nv50_display_irq_hotplug_bh(struct work_struct *work)
struct drm_connector *connector;
const uint32_t gpio_reg[4] = { 0xe104, 0xe108, 0xe280, 0xe284 };
uint32_t unplug_mask, plug_mask, change_mask;
uint32_t hpd0, hpd1 = 0;
uint32_t hpd0, hpd1;

hpd0 = nv_rd32(dev, 0xe054) & nv_rd32(dev, 0xe050);
spin_lock_irq(&dev_priv->hpd_state.lock);
hpd0 = dev_priv->hpd_state.hpd0_bits;
dev_priv->hpd_state.hpd0_bits = 0;
hpd1 = dev_priv->hpd_state.hpd1_bits;
dev_priv->hpd_state.hpd1_bits = 0;
spin_unlock_irq(&dev_priv->hpd_state.lock);

hpd0 &= nv_rd32(dev, 0xe050);
if (dev_priv->chipset >= 0x90)
hpd1 = nv_rd32(dev, 0xe074) & nv_rd32(dev, 0xe070);
hpd1 &= nv_rd32(dev, 0xe070);

plug_mask = (hpd0 & 0x0000ffff) | (hpd1 << 16);
unplug_mask = (hpd0 >> 16) | (hpd1 & 0xffff0000);
Expand Down Expand Up @@ -1078,10 +1085,6 @@ nv50_display_irq_hotplug_bh(struct work_struct *work)
helper->dpms(connector->encoder, DRM_MODE_DPMS_OFF);
}

nv_wr32(dev, 0xe054, nv_rd32(dev, 0xe054));
if (dev_priv->chipset >= 0x90)
nv_wr32(dev, 0xe074, nv_rd32(dev, 0xe074));

drm_helper_hpd_irq_event(dev);
}

Expand All @@ -1092,8 +1095,22 @@ nv50_display_irq_handler(struct drm_device *dev)
uint32_t delayed = 0;

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);
uint32_t hpd0_bits, hpd1_bits = 0;

hpd0_bits = nv_rd32(dev, 0xe054);
nv_wr32(dev, 0xe054, hpd0_bits);

if (dev_priv->chipset >= 0x90) {
hpd1_bits = nv_rd32(dev, 0xe074);
nv_wr32(dev, 0xe074, hpd1_bits);
}

spin_lock(&dev_priv->hpd_state.lock);
dev_priv->hpd_state.hpd0_bits |= hpd0_bits;
dev_priv->hpd_state.hpd1_bits |= hpd1_bits;
spin_unlock(&dev_priv->hpd_state.lock);

queue_work(dev_priv->wq, &dev_priv->hpd_work);
}

while (nv_rd32(dev, NV50_PMC_INTR_0) & NV50_PMC_INTR_0_DISPLAY) {
Expand Down

0 comments on commit ab83833

Please sign in to comment.