From cd924de02927a091c517b0ac6b9cd8f065ce448c Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 7 Mar 2008 14:38:05 +1100 Subject: [PATCH 01/26] nouveau: don't touch NV_USER regs on channel destroy. Not only was this entirely pointless, it actually causes my NV30GL to die randomly when channels are destroyed. --- shared-core/nouveau_fifo.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/shared-core/nouveau_fifo.c b/shared-core/nouveau_fifo.c index 056cb6d0..8e93207e 100644 --- a/shared-core/nouveau_fifo.c +++ b/shared-core/nouveau_fifo.c @@ -433,13 +433,6 @@ void nouveau_fifo_free(struct nouveau_channel *chan) NV_WRITE(NV03_PFIFO_CACHE1_PUSH0, 0x00000000); NV_WRITE(NV04_PFIFO_CACHE1_PULL0, 0x00000000); - /* stop the fifo, otherwise it could be running and - * it will crash when removing gpu objects - *XXX: from real-world evidence, absolutely useless.. - */ - NV_WRITE(chan->get, chan->pushbuf_base); - NV_WRITE(chan->put, chan->pushbuf_base); - // FIXME XXX needs more code engine->fifo.destroy_context(chan); From 1ccccbd4ce3463edb459eb193feb572938fce19e Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 7 Mar 2008 15:08:59 +1100 Subject: [PATCH 02/26] nouveau: redo channel idle detection Will hopefully work a bit better than previous code, which depended on knowing the channel's most recent PUT value. Some chips always return 0 on reading these regs, and currently userspace is the only other entity which knows the value. --- shared-core/nouveau_fifo.c | 43 +++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/shared-core/nouveau_fifo.c b/shared-core/nouveau_fifo.c index 8e93207e..d8fda277 100644 --- a/shared-core/nouveau_fifo.c +++ b/shared-core/nouveau_fifo.c @@ -390,6 +390,34 @@ nouveau_fifo_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret, return 0; } +static int +nouveau_channel_idle(struct nouveau_channel *chan) +{ + struct drm_device *dev = chan->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_engine *engine = &dev_priv->Engine; + uint32_t caches; + int idle; + + caches = NV_READ(NV03_PFIFO_CACHES); + NV_WRITE(NV03_PFIFO_CACHES, caches & ~1); + + if (engine->fifo.channel_id(dev) != chan->id) { + struct nouveau_gpuobj *ramfc = chan->ramfc->gpuobj; + + if (INSTANCE_RD(ramfc, 0) != INSTANCE_RD(ramfc, 1)) + idle = 0; + else + idle = 1; + } else { + idle = (NV_READ(NV04_PFIFO_CACHE1_DMA_GET) == + NV_READ(NV04_PFIFO_CACHE1_DMA_PUT)); + } + + NV_WRITE(NV03_PFIFO_CACHES, caches); + return idle; +} + /* stops a fifo */ void nouveau_fifo_free(struct nouveau_channel *chan) { @@ -400,22 +428,9 @@ void nouveau_fifo_free(struct nouveau_channel *chan) DRM_INFO("%s: freeing fifo %d\n", __func__, chan->id); - /* Disable channel switching, if this channel isn't currenly - * active re-enable it if there's still pending commands. - * We really should do a manual context switch here, but I'm - * not sure I trust our ability to do this reliably yet.. - */ - NV_WRITE(NV03_PFIFO_CACHES, 0); - if (engine->fifo.channel_id(dev) != chan->id && - NV_READ(chan->get) != NV_READ(chan->put)) { - NV_WRITE(NV03_PFIFO_CACHES, 1); - } - /* Give the channel a chance to idle, wait 2s (hopefully) */ t_start = engine->timer.read(dev); - while (NV_READ(chan->get) != NV_READ(chan->put) || - NV_READ(NV03_PFIFO_CACHE1_GET) != - NV_READ(NV03_PFIFO_CACHE1_PUT)) { + while (!nouveau_channel_idle(chan)) { if (engine->timer.read(dev) - t_start > 2000000000ULL) { DRM_ERROR("Failed to idle channel %d before destroy." "Prepare for strangeness..\n", chan->id); From ce3733572e4eea6d9adb167d8fccac745455445b Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Sat, 8 Mar 2008 08:30:03 +1000 Subject: [PATCH 03/26] drm/radeon: check sarea_priv exists --- shared-core/radeon_state.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/shared-core/radeon_state.c b/shared-core/radeon_state.c index 70651d7f..8489549c 100644 --- a/shared-core/radeon_state.c +++ b/shared-core/radeon_state.c @@ -3126,12 +3126,14 @@ static int radeon_cp_setparam(struct drm_device *dev, void *data, struct drm_fil DRM_DEBUG("color tiling disabled\n"); dev_priv->front_pitch_offset &= ~RADEON_DST_TILE_MACRO; dev_priv->back_pitch_offset &= ~RADEON_DST_TILE_MACRO; - dev_priv->sarea_priv->tiling_enabled = 0; + if (dev_priv->sarea_priv) + dev_priv->sarea_priv->tiling_enabled = 0; } else if (sp->value == 1) { DRM_DEBUG("color tiling enabled\n"); dev_priv->front_pitch_offset |= RADEON_DST_TILE_MACRO; dev_priv->back_pitch_offset |= RADEON_DST_TILE_MACRO; - dev_priv->sarea_priv->tiling_enabled = 1; + if (dev_priv->sarea_priv) + dev_priv->sarea_priv->tiling_enabled = 1; } break; case RADEON_SETPARAM_PCIGART_LOCATION: From 2848f048616c2c97f02701386ee73137a1307e2c Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 24 Jan 2008 11:46:45 -0800 Subject: [PATCH 04/26] Switch from PIPE_VBLANK to PIPE_EVENT interrupts. My 965GM gets interrupts stuck when using the old PIPE_VBLANK interrupt. Switch to the PIPE_EVENT interrupt mechanism, and set the PIPE*STAT registers to use START_VBLANK on 965 and VBLANK on previous chips. --- shared-core/i915_drv.h | 48 +++++++++++-- shared-core/i915_irq.c | 148 +++++++++++++++++++++++++++-------------- 2 files changed, 139 insertions(+), 57 deletions(-) diff --git a/shared-core/i915_drv.h b/shared-core/i915_drv.h index 4d3ac0a5..570fc4d3 100644 --- a/shared-core/i915_drv.h +++ b/shared-core/i915_drv.h @@ -466,10 +466,23 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); /* Interrupt bits: */ -#define USER_INT_FLAG (1<<1) -#define VSYNC_PIPEB_FLAG (1<<5) -#define VSYNC_PIPEA_FLAG (1<<7) -#define HWB_OOM_FLAG (1<<13) /* binner out of memory */ +#define I915_PIPE_CONTROL_NOTIFY_INTERRUPT (1<<18) +#define I915_DISPLAY_PORT_INTERRUPT (1<<17) +#define I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT (1<<15) +#define I915_GMCH_THERMAL_SENSOR_EVENT_INTERRUPT (1<<14) +#define I915_HWB_OOM_INTERRUPT (1<<13) /* binner out of memory */ +#define I915_SYNC_STATUS_INTERRUPT (1<<12) +#define I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT (1<<11) +#define I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT (1<<10) +#define I915_OVERLAY_PLANE_FLIP_PENDING_INTERRUPT (1<<9) +#define I915_DISPLAY_PLANE_C_FLIP_PENDING_INTERRUPT (1<<8) +#define I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT (1<<7) +#define I915_DISPLAY_PIPE_A_EVENT_INTERRUPT (1<<6) +#define I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT (1<<5) +#define I915_DISPLAY_PIPE_B_EVENT_INTERRUPT (1<<4) +#define I915_DEBUG_INTERRUPT (1<<2) +#define I915_USER_INTERRUPT (1<<1) + #define I915REG_HWSTAM 0x02098 #define I915REG_INT_IDENTITY_R 0x020a4 @@ -513,8 +526,31 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); #define PIPE_PIXEL_MASK 0x00ffffff #define PIPE_PIXEL_SHIFT 0 -#define I915_VBLANK_INTERRUPT_ENABLE (1UL<<17) -#define I915_VBLANK_CLEAR (1UL<<1) +#define I915_FIFO_UNDERRUN_STATUS (1UL<<31) +#define I915_CRC_ERROR_ENABLE (1UL<<29) +#define I915_CRC_DONE_ENABLE (1UL<<28) +#define I915_GMBUS_EVENT_ENABLE (1UL<<27) +#define I915_VSYNC_INTERRUPT_ENABLE (1UL<<25) +#define I915_DISPLAY_LINE_COMPARE_ENABLE (1UL<<24) +#define I915_DPST_EVENT_ENABLE (1UL<<23) +#define I915_LEGACY_BLC_EVENT_ENABLE (1UL<<22) +#define I915_ODD_FIELD_INTERRUPT_ENABLE (1UL<<21) +#define I915_EVEN_FIELD_INTERRUPT_ENABLE (1UL<<20) +#define I915_START_VBLANK_INTERRUPT_ENABLE (1UL<<18) /* 965 or later */ +#define I915_VBLANK_INTERRUPT_ENABLE (1UL<<17) +#define I915_OVERLAY_UPDATED_ENABLE (1UL<<16) +#define I915_CRC_ERROR_INTERRUPT_STATUS (1UL<<13) +#define I915_CRC_DONE_INTERRUPT_STATUS (1UL<<12) +#define I915_GMBUS_INTERRUPT_STATUS (1UL<<11) +#define I915_VSYNC_INTERRUPT_STATUS (1UL<<9) +#define I915_DISPLAY_LINE_COMPARE_STATUS (1UL<<8) +#define I915_DPST_EVENT_STATUS (1UL<<7) +#define I915_LEGACY_BLC_EVENT_STATUS (1UL<<6) +#define I915_ODD_FIELD_INTERRUPT_STATUS (1UL<<5) +#define I915_EVEN_FIELD_INTERRUPT_STATUS (1UL<<4) +#define I915_START_VBLANK_INTERRUPT_STATUS (1UL<<2) /* 965 or later */ +#define I915_VBLANK_INTERRUPT_STATUS (1UL<<1) +#define I915_OVERLAY_UPDATED_STATUS (1UL<<0) #define SRX_INDEX 0x3c4 #define SRX_DATA 0x3c5 diff --git a/shared-core/i915_irq.c b/shared-core/i915_irq.c index 126f0379..a9b67a9c 100644 --- a/shared-core/i915_irq.c +++ b/shared-core/i915_irq.c @@ -31,10 +31,6 @@ #include "i915_drm.h" #include "i915_drv.h" -#define USER_INT_FLAG (1<<1) -#define VSYNC_PIPEB_FLAG (1<<5) -#define VSYNC_PIPEA_FLAG (1<<7) - #define MAX_NOPID ((u32)~0) /** @@ -420,55 +416,63 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) { struct drm_device *dev = (struct drm_device *) arg; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - u16 temp; + u32 iir; u32 pipea_stats, pipeb_stats; + int vblank = 0; - pipea_stats = I915_READ(I915REG_PIPEASTAT); - pipeb_stats = I915_READ(I915REG_PIPEBSTAT); - - temp = I915_READ16(I915REG_INT_IDENTITY_R); - + iir = I915_READ(I915REG_INT_IDENTITY_R); #if 0 - DRM_DEBUG("flag=%08x\n", temp); + DRM_DEBUG("flag=%08x\n", iir); #endif - if (temp == 0) + if (iir == 0) { + DRM_DEBUG ("iir 0x%08x im 0x%08x ie 0x%08x pipea 0x%08x pipeb 0x%08x\n", + iir, + I915_READ(I915REG_INT_MASK_R), + I915_READ(I915REG_INT_ENABLE_R), + I915_READ(I915REG_PIPEASTAT), + I915_READ(I915REG_PIPEBSTAT)); return IRQ_NONE; + } /* * Clear the PIPE(A|B)STAT regs before the IIR otherwise * we may get extra interrupts. */ - if (temp & VSYNC_PIPEA_FLAG) { - drm_handle_vblank(dev, i915_get_plane(dev, 0)); - I915_WRITE(I915REG_PIPEASTAT, - pipea_stats | I915_VBLANK_INTERRUPT_ENABLE | - I915_VBLANK_CLEAR); + if (iir & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) { + pipea_stats = I915_READ(I915REG_PIPEASTAT); + if (pipea_stats & (I915_START_VBLANK_INTERRUPT_STATUS| + I915_VBLANK_INTERRUPT_STATUS)) + { + vblank++; + drm_handle_vblank(dev, i915_get_plane(dev, 0)); + } + I915_WRITE(I915REG_PIPEASTAT, pipea_stats); } - if (temp & VSYNC_PIPEB_FLAG) { - drm_handle_vblank(dev, i915_get_plane(dev, 1)); - I915_WRITE(I915REG_PIPEBSTAT, - pipeb_stats | I915_VBLANK_INTERRUPT_ENABLE | - I915_VBLANK_CLEAR); + if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) { + pipeb_stats = I915_READ(I915REG_PIPEBSTAT); + if (pipeb_stats & (I915_START_VBLANK_INTERRUPT_STATUS| + I915_VBLANK_INTERRUPT_STATUS)) + { + vblank++; + drm_handle_vblank(dev, i915_get_plane(dev, 1)); + } + I915_WRITE(I915REG_PIPEBSTAT, pipeb_stats); } - I915_WRITE16(I915REG_INT_IDENTITY_R, temp); - (void) I915_READ16(I915REG_INT_IDENTITY_R); /* Flush posted write */ - - temp &= (dev_priv->irq_enable_reg | USER_INT_FLAG | VSYNC_PIPEA_FLAG | - VSYNC_PIPEB_FLAG); - if (dev_priv->sarea_priv) - dev_priv->sarea_priv->last_dispatch = - READ_BREADCRUMB(dev_priv); + dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); - if (temp & USER_INT_FLAG) { + I915_WRITE(I915REG_INT_IDENTITY_R, iir); + (void) I915_READ(I915REG_INT_IDENTITY_R); /* Flush posted write */ + + if (iir & I915_USER_INTERRUPT) { DRM_WAKEUP(&dev_priv->irq_queue); #ifdef I915_HAVE_FENCE i915_fence_handler(dev); #endif } - if (temp & (VSYNC_PIPEA_FLAG | VSYNC_PIPEB_FLAG)) { + if (vblank) { if (dev_priv->swaps_pending > 0) drm_locked_tasklet(dev, i915_vblank_tasklet); } @@ -499,8 +503,8 @@ void i915_user_irq_on(drm_i915_private_t *dev_priv) { DRM_SPINLOCK(&dev_priv->user_irq_lock); if (dev_priv->irq_enabled && (++dev_priv->user_irq_refcount == 1)){ - dev_priv->irq_enable_reg |= USER_INT_FLAG; - I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); + dev_priv->irq_enable_reg |= I915_USER_INTERRUPT; + I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); } DRM_SPINUNLOCK(&dev_priv->user_irq_lock); @@ -511,7 +515,7 @@ void i915_user_irq_off(drm_i915_private_t *dev_priv) DRM_SPINLOCK(&dev_priv->user_irq_lock); if (dev_priv->irq_enabled && (--dev_priv->user_irq_refcount == 0)) { // dev_priv->irq_enable_reg &= ~USER_INT_FLAG; - // I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); + // I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); } DRM_SPINUNLOCK(&dev_priv->user_irq_lock); } @@ -590,13 +594,17 @@ int i915_enable_vblank(struct drm_device *dev, int plane) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; int pipe = i915_get_pipe(dev, plane); + u32 pipestat_reg = 0; + u32 pipestat; switch (pipe) { case 0: - dev_priv->irq_enable_reg |= VSYNC_PIPEA_FLAG; + pipestat_reg = I915REG_PIPEASTAT; + dev_priv->irq_enable_reg |= I915_DISPLAY_PIPE_A_EVENT_INTERRUPT; break; case 1: - dev_priv->irq_enable_reg |= VSYNC_PIPEB_FLAG; + pipestat_reg = I915REG_PIPEBSTAT; + dev_priv->irq_enable_reg |= I915_DISPLAY_PIPE_B_EVENT_INTERRUPT; break; default: DRM_ERROR("tried to enable vblank on non-existent pipe %d\n", @@ -604,7 +612,25 @@ int i915_enable_vblank(struct drm_device *dev, int plane) break; } - I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); + if (pipestat_reg) + { + pipestat = I915_READ (pipestat_reg); + /* + * Older chips didn't have the start vblank interrupt, + * but + */ + if (IS_I965G (dev)) + pipestat |= I915_START_VBLANK_INTERRUPT_ENABLE; + else + pipestat |= I915_VBLANK_INTERRUPT_ENABLE; + /* + * Clear any pending status + */ + pipestat |= (I915_START_VBLANK_INTERRUPT_STATUS | + I915_VBLANK_INTERRUPT_STATUS); + I915_WRITE(pipestat_reg, pipestat); + } + I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); return 0; } @@ -613,13 +639,17 @@ void i915_disable_vblank(struct drm_device *dev, int plane) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; int pipe = i915_get_pipe(dev, plane); + u32 pipestat_reg = 0; + u32 pipestat; switch (pipe) { case 0: - dev_priv->irq_enable_reg &= ~VSYNC_PIPEA_FLAG; + pipestat_reg = I915REG_PIPEASTAT; + dev_priv->irq_enable_reg &= ~I915_DISPLAY_PIPE_A_EVENT_INTERRUPT; break; case 1: - dev_priv->irq_enable_reg &= ~VSYNC_PIPEB_FLAG; + pipestat_reg = I915REG_PIPEBSTAT; + dev_priv->irq_enable_reg &= ~I915_DISPLAY_PIPE_B_EVENT_INTERRUPT; break; default: DRM_ERROR("tried to disable vblank on non-existent pipe %d\n", @@ -627,16 +657,28 @@ void i915_disable_vblank(struct drm_device *dev, int plane) break; } - I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); + I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); + if (pipestat_reg) + { + pipestat = I915_READ (pipestat_reg); + pipestat &= ~(I915_START_VBLANK_INTERRUPT_ENABLE | + I915_VBLANK_INTERRUPT_ENABLE); + /* + * Clear any pending status + */ + pipestat |= (I915_START_VBLANK_INTERRUPT_STATUS | + I915_VBLANK_INTERRUPT_STATUS); + I915_WRITE(pipestat_reg, pipestat); + } } static void i915_enable_interrupt (struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - dev_priv->irq_enable_reg |= USER_INT_FLAG; + dev_priv->irq_enable_reg |= I915_USER_INTERRUPT; - I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); + I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); dev_priv->irq_enabled = 1; } @@ -677,9 +719,9 @@ int i915_vblank_pipe_get(struct drm_device *dev, void *data, flag = I915_READ(I915REG_INT_ENABLE_R); pipe->pipe = 0; - if (flag & VSYNC_PIPEA_FLAG) + if (flag & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) pipe->pipe |= DRM_I915_VBLANK_PIPE_A; - if (flag & VSYNC_PIPEB_FLAG) + if (flag & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) pipe->pipe |= DRM_I915_VBLANK_PIPE_B; return 0; @@ -883,16 +925,20 @@ int i915_driver_irq_postinstall(struct drm_device * dev) void i915_driver_irq_uninstall(struct drm_device * dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - u16 temp; + u32 temp; if (!dev_priv) return; dev_priv->irq_enabled = 0; - I915_WRITE16(I915REG_HWSTAM, 0xffff); - I915_WRITE16(I915REG_INT_MASK_R, 0xffff); - I915_WRITE16(I915REG_INT_ENABLE_R, 0x0); + I915_WRITE(I915REG_HWSTAM, 0xffffffff); + I915_WRITE(I915REG_INT_MASK_R, 0xffffffff); + I915_WRITE(I915REG_INT_ENABLE_R, 0x0); - temp = I915_READ16(I915REG_INT_IDENTITY_R); - I915_WRITE16(I915REG_INT_IDENTITY_R, temp); + temp = I915_READ(I915REG_PIPEASTAT); + I915_WRITE(I915REG_PIPEASTAT, temp); + temp = I915_READ(I915REG_PIPEBSTAT); + I915_WRITE(I915REG_PIPEBSTAT, temp); + temp = I915_READ(I915REG_INT_IDENTITY_R); + I915_WRITE(I915REG_INT_IDENTITY_R, temp); } From 07ba3b7193f1a50c3ef0509f9e37dab41457f81b Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Mon, 10 Mar 2008 18:30:17 +1000 Subject: [PATCH 05/26] remove unneeded load call --- linux-core/drm_stub.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/linux-core/drm_stub.c b/linux-core/drm_stub.c index 6584f51d..c68adbaf 100644 --- a/linux-core/drm_stub.c +++ b/linux-core/drm_stub.c @@ -157,11 +157,6 @@ static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev, } } - if (dev->driver->load) - if ((retcode = dev->driver->load(dev, ent->driver_data))) - goto error_out_unreg; - - retcode = drm_ctxbitmap_init(dev); if (retcode) { DRM_ERROR("Cannot allocate memory for context bitmap.\n"); From 52748d17923b7e501b707b950227864c0b64d8a1 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Tue, 11 Mar 2008 11:49:27 +1000 Subject: [PATCH 06/26] drm: hopefully fix cursors on 965 --- linux-core/intel_display.c | 18 +++++++++++------- linux-core/intel_drv.h | 2 +- shared-core/i915_drv.h | 3 ++- shared-core/i915_init.c | 8 ++++++++ 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/linux-core/intel_display.c b/linux-core/intel_display.c index 4b48a0b2..fa2b9bea 100644 --- a/linux-core/intel_display.c +++ b/linux-core/intel_display.c @@ -1007,7 +1007,7 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc, uint32_t control = (pipe == 0) ? CURSOR_A_CONTROL : CURSOR_B_CONTROL; uint32_t base = (pipe == 0) ? CURSOR_A_BASE : CURSOR_B_BASE; uint32_t temp; - size_t adder; + size_t addr; DRM_DEBUG("\n"); @@ -1039,17 +1039,21 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc, return -ENOMEM; } - adder = dev_priv->stolen_base + bo->offset; - intel_crtc->cursor_adder = adder; + if (dev_priv->cursor_needs_physical) + addr = dev_priv->stolen_base + bo->offset; + else + addr = bo->offset; + + intel_crtc->cursor_addr = addr; temp = 0; /* set the pipe for the cursor */ temp |= (pipe << 28); temp |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE; - DRM_DEBUG("cusror base %x\n", adder); + DRM_DEBUG("cusror base %x\n", addr); I915_WRITE(control, temp); - I915_WRITE(base, adder); + I915_WRITE(base, addr); return 0; } @@ -1075,7 +1079,7 @@ static int intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) temp |= ((x & CURSOR_POS_MASK) << CURSOR_X_SHIFT); temp |= ((y & CURSOR_POS_MASK) << CURSOR_Y_SHIFT); - adder = intel_crtc->cursor_adder; + adder = intel_crtc->cursor_addr; I915_WRITE((pipe == 0) ? CURSOR_A_POSITION : CURSOR_B_POSITION, temp); I915_WRITE((pipe == 0) ? CURSOR_A_BASE : CURSOR_B_BASE, adder); @@ -1241,7 +1245,7 @@ void intel_crtc_init(struct drm_device *dev, int pipe) intel_crtc->lut_b[i] = i; } - intel_crtc->cursor_adder = 0; + intel_crtc->cursor_addr = 0; crtc->driver_private = intel_crtc; } diff --git a/linux-core/intel_drv.h b/linux-core/intel_drv.h index 72ba01da..a36fd3f1 100644 --- a/linux-core/intel_drv.h +++ b/linux-core/intel_drv.h @@ -56,7 +56,7 @@ struct intel_output { struct intel_crtc { int pipe; int plane; - uint32_t cursor_adder; + uint32_t cursor_addr; u8 lut_r[256], lut_g[256], lut_b[256]; }; diff --git a/shared-core/i915_drv.h b/shared-core/i915_drv.h index f6c0005d..b8dfbc34 100644 --- a/shared-core/i915_drv.h +++ b/shared-core/i915_drv.h @@ -139,9 +139,10 @@ struct drm_i915_private { int fence_irq_on; uint32_t irq_enable_reg; int irq_enabled; - struct workqueue_struct *wq; + bool cursor_needs_physical; + #ifdef I915_HAVE_FENCE uint32_t flush_sequence; uint32_t flush_flags; diff --git a/shared-core/i915_init.c b/shared-core/i915_init.c index 3d8a1dca..274322e1 100644 --- a/shared-core/i915_init.c +++ b/shared-core/i915_init.c @@ -130,6 +130,14 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) dev->types[8] = _DRM_STAT_SECONDARY; dev->types[9] = _DRM_STAT_DMA; + if (IS_MOBILE(dev) || IS_I9XX(dev)) + dev_priv->cursor_needs_physical = true; + else + dev_priv->cursor_needs_physical = false; + + if (IS_I965G(dev) || IS_G33(dev)) + dev_priv->cursor_needs_physical = false; + if (IS_I9XX(dev)) { pci_read_config_dword(dev->pdev, 0x5C, &dev_priv->stolen_base); DRM_DEBUG("stolen base %p\n", (void*)dev_priv->stolen_base); From 5a7f4b3074d5cda909fc7329bc91da11d89181e1 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Tue, 11 Mar 2008 16:05:26 +1000 Subject: [PATCH 07/26] drm: fix oops on unload. if we are unloading the module, there is no master so therefore no lock --- shared-core/i915_init.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/shared-core/i915_init.c b/shared-core/i915_init.c index 274322e1..fa2fc3da 100644 --- a/shared-core/i915_init.c +++ b/shared-core/i915_init.c @@ -277,7 +277,6 @@ int i915_driver_unload(struct drm_device *dev) I915_WRITE(LP_RING + RING_LEN, 0); - if (drm_core_check_feature(dev, DRIVER_MODESET)) { drm_irq_uninstall(dev); intel_modeset_cleanup(dev); @@ -291,7 +290,6 @@ int i915_driver_unload(struct drm_device *dev) if (dev_priv->sarea_kmap.virtual) { drm_bo_kunmap(&dev_priv->sarea_kmap); dev_priv->sarea_kmap.virtual = NULL; - dev->primary->master->lock.hw_lock = NULL; dev->sigdata.lock = NULL; } From f13936f7fc4d4932d5c511ccec29f1c4d24dc2dc Mon Sep 17 00:00:00 2001 From: Stuart Bennett Date: Tue, 11 Mar 2008 00:33:58 +0000 Subject: [PATCH 08/26] nouveau: move AGP reset to mem_init_agp Also, power cycle PGRAPH when resetting AGP -- it seems to fix problems encountered by p0g on nv25 --- shared-core/nouveau_mem.c | 28 ++++++++++++++++++++++++++++ shared-core/nv04_mc.c | 13 ------------- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/shared-core/nouveau_mem.c b/shared-core/nouveau_mem.c index 80b2990d..4e80ca46 100644 --- a/shared-core/nouveau_mem.c +++ b/shared-core/nouveau_mem.c @@ -300,6 +300,32 @@ uint64_t nouveau_mem_fb_amount(struct drm_device *dev) return 0; } +static void nouveau_mem_reset_agp(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + uint32_t saved_pci_nv_1, saved_pci_nv_19, pmc_enable; + + saved_pci_nv_1 = NV_READ(NV04_PBUS_PCI_NV_1); + saved_pci_nv_19 = NV_READ(NV04_PBUS_PCI_NV_19); + + /* clear busmaster bit */ + NV_WRITE(NV04_PBUS_PCI_NV_1, saved_pci_nv_1 & ~0x4); + /* clear SBA and AGP bits */ + NV_WRITE(NV04_PBUS_PCI_NV_19, saved_pci_nv_19 & 0xfffff0ff); + + /* power cycle pgraph, if enabled */ + pmc_enable = NV_READ(NV03_PMC_ENABLE); + if (pmc_enable & NV_PMC_ENABLE_PGRAPH) { + NV_WRITE(NV03_PMC_ENABLE, pmc_enable & ~NV_PMC_ENABLE_PGRAPH); + NV_WRITE(NV03_PMC_ENABLE, NV_READ(NV03_PMC_ENABLE) | + NV_PMC_ENABLE_PGRAPH); + } + + /* and restore (gives effect of resetting AGP) */ + NV_WRITE(NV04_PBUS_PCI_NV_19, saved_pci_nv_19); + NV_WRITE(NV04_PBUS_PCI_NV_1, saved_pci_nv_1); +} + static int nouveau_mem_init_agp(struct drm_device *dev, int ttm) { @@ -308,6 +334,8 @@ nouveau_mem_init_agp(struct drm_device *dev, int ttm) struct drm_agp_mode mode; int ret; + nouveau_mem_reset_agp(dev); + ret = drm_agp_acquire(dev); if (ret) { DRM_ERROR("Unable to acquire AGP: %d\n", ret); diff --git a/shared-core/nv04_mc.c b/shared-core/nv04_mc.c index 766f3a33..24c1f7b3 100644 --- a/shared-core/nv04_mc.c +++ b/shared-core/nv04_mc.c @@ -7,25 +7,12 @@ int nv04_mc_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; - uint32_t saved_pci_nv_1, saved_pci_nv_19; - - saved_pci_nv_1 = NV_READ(NV04_PBUS_PCI_NV_1); - saved_pci_nv_19 = NV_READ(NV04_PBUS_PCI_NV_19); - - /* clear busmaster bit */ - NV_WRITE(NV04_PBUS_PCI_NV_1, saved_pci_nv_1 & ~(0x00000001 << 2)); - /* clear SBA and AGP bits */ - NV_WRITE(NV04_PBUS_PCI_NV_19, saved_pci_nv_19 & 0xfffff0ff); /* Power up everything, resetting each individual unit will * be done later if needed. */ NV_WRITE(NV03_PMC_ENABLE, 0xFFFFFFFF); - /* and restore (gives effect of resetting AGP) */ - NV_WRITE(NV04_PBUS_PCI_NV_19, saved_pci_nv_19); - NV_WRITE(NV04_PBUS_PCI_NV_1, saved_pci_nv_1); - return 0; } From 903d9231d6f998657cc80ee6f20ded4df68e691b Mon Sep 17 00:00:00 2001 From: Alan Hourihane Date: Tue, 11 Mar 2008 20:29:37 +0000 Subject: [PATCH 09/26] Add support for monitor hotplug signals/waits Also adjust i915 irq handling as it follows the 16bit'ism's of the i8xx series. --- linux-core/drmP.h | 20 ++++ linux-core/drm_crtc.c | 15 +-- linux-core/drm_drv.c | 1 + linux-core/drm_irq.c | 178 +++++++++++++++++++++++---- linux-core/i915_fence.c | 4 +- linux-core/intel_crt.c | 15 +-- linux-core/intel_sdvo.c | 14 --- shared-core/drm.h | 34 ++++++ shared-core/i915_drv.h | 17 +-- shared-core/i915_irq.c | 258 +++++++++++++++++++++++++--------------- 10 files changed, 396 insertions(+), 160 deletions(-) diff --git a/linux-core/drmP.h b/linux-core/drmP.h index 47974856..7b21f8a1 100644 --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -591,6 +591,13 @@ struct drm_vbl_sig { struct task_struct *task; }; +struct drm_hotplug_sig { + struct list_head head; + unsigned int counter; + struct siginfo info; + struct task_struct *task; +}; + /* location of GART table */ #define DRM_ATI_GART_MAIN 1 #define DRM_ATI_GART_FB 2 @@ -867,6 +874,15 @@ struct drm_device { struct work_struct work; + /** \name HOTPLUG IRQ support */ + /*@{ */ + wait_queue_head_t hotplug_queue; /**< HOTPLUG wait queue */ + spinlock_t hotplug_lock; + struct list_head *hotplug_sigs; /**< signal list to send on HOTPLUG */ + atomic_t hotplug_signal_pending; /* number of signals pending on all crtcs*/ + + /*@} */ + /** \name VBLANK IRQ support */ /*@{ */ @@ -1193,13 +1209,17 @@ extern void drm_driver_irq_preinstall(struct drm_device *dev); extern void drm_driver_irq_postinstall(struct drm_device *dev); extern void drm_driver_irq_uninstall(struct drm_device *dev); +extern int drm_hotplug_init(struct drm_device *dev); +extern int drm_wait_hotplug(struct drm_device *dev, void *data, struct drm_file *filp); extern int drm_vblank_init(struct drm_device *dev, int num_crtcs); extern int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *filp); +extern int drm_wait_hotplug(struct drm_device *dev, void *data, struct drm_file *filp); extern int drm_vblank_wait(struct drm_device * dev, unsigned int *vbl_seq); extern void drm_locked_tasklet(struct drm_device *dev, void(*func)(struct drm_device*)); extern u32 drm_vblank_count(struct drm_device *dev, int crtc); extern void drm_update_vblank_count(struct drm_device *dev, int crtc); extern void drm_handle_vblank(struct drm_device *dev, int crtc); +extern void drm_handle_hotplug(struct drm_device *dev); extern int drm_vblank_get(struct drm_device *dev, int crtc); extern void drm_vblank_put(struct drm_device *dev, int crtc); diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c index e0c85cef..0cd0ebd0 100644 --- a/linux-core/drm_crtc.c +++ b/linux-core/drm_crtc.c @@ -947,6 +947,7 @@ static void drm_pick_crtcs (struct drm_device *dev) if (drm_mode_equal (modes, modes_equal)) { if ((output->possible_clones & output_equal->possible_clones) && (output_equal->crtc == crtc)) { printk("Cloning %s (0x%lx) to %s (0x%lx)\n",drm_get_output_name(output),output->possible_clones,drm_get_output_name(output_equal),output_equal->possible_clones); + des_mode = modes; assigned = 0; goto clone; } @@ -1180,7 +1181,7 @@ int drm_crtc_set_config(struct drm_crtc *crtc, struct drm_mode_crtc *crtc_info, * @output hotpluged output * * LOCKING. - * Caller must hold mode config lock, function might grap struct lock. + * Caller must hold mode config lock, function might grab struct lock. * * Stage two of a hotplug. * @@ -1192,10 +1193,11 @@ int drm_hotplug_stage_two(struct drm_device *dev, struct drm_output *output, { int has_config = 0; + dev->mode_config.hotplug_counter++; + /* We might want to do something more here */ if (!connected) { DRM_DEBUG("not connected\n"); - dev->mode_config.hotplug_counter++; return 0; } @@ -1211,10 +1213,10 @@ int drm_hotplug_stage_two(struct drm_device *dev, struct drm_output *output, if (!output->crtc || !output->crtc->desired_mode) { DRM_DEBUG("could not find a desired mode or crtc for output\n"); - goto out_err; + return 1; } - /* We should realy check if there is a fb using this crtc */ + /* We should really check if there is a fb using this crtc */ if (!has_config) dev->driver->fb_probe(dev, output->crtc); else { @@ -1226,12 +1228,7 @@ int drm_hotplug_stage_two(struct drm_device *dev, struct drm_output *output, drm_disable_unused_functions(dev); - dev->mode_config.hotplug_counter++; return 0; - -out_err: - dev->mode_config.hotplug_counter++; - return 1; } EXPORT_SYMBOL(drm_hotplug_stage_two); diff --git a/linux-core/drm_drv.c b/linux-core/drm_drv.c index 434789dd..09372c71 100644 --- a/linux-core/drm_drv.c +++ b/linux-core/drm_drv.c @@ -136,6 +136,7 @@ static struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_MODE_DETACHMODE, drm_mode_detachmode_ioctl, DRM_MASTER|DRM_ROOT_ONLY|DRM_CONTROL_ALLOW), DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPERTY, drm_mode_getproperty_ioctl, DRM_MASTER | DRM_ROOT_ONLY | DRM_CONTROL_ALLOW), DRM_IOCTL_DEF(DRM_IOCTL_MODE_HOTPLUG, drm_mode_hotplug_ioctl, DRM_CONTROL_ALLOW), + DRM_IOCTL_DEF(DRM_IOCTL_WAIT_HOTPLUG, drm_wait_hotplug, 0), DRM_IOCTL_DEF(DRM_IOCTL_MM_INIT, drm_mm_init_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), diff --git a/linux-core/drm_irq.c b/linux-core/drm_irq.c index 6b740b18..230ef3cc 100644 --- a/linux-core/drm_irq.c +++ b/linux-core/drm_irq.c @@ -90,32 +90,41 @@ static void vblank_disable_fn(unsigned long arg) static void drm_vblank_cleanup(struct drm_device *dev) { - /* Bail if the driver didn't call drm_vblank_init() */ - if (dev->num_crtcs == 0) - return; - del_timer(&dev->vblank_disable_timer); vblank_disable_fn((unsigned long)dev); - drm_free(dev->vbl_queue, sizeof(*dev->vbl_queue) * dev->num_crtcs, - DRM_MEM_DRIVER); - drm_free(dev->vbl_sigs, sizeof(*dev->vbl_sigs) * dev->num_crtcs, - DRM_MEM_DRIVER); - drm_free(dev->_vblank_count, sizeof(*dev->_vblank_count) * - dev->num_crtcs, DRM_MEM_DRIVER); - drm_free(dev->vblank_refcount, sizeof(*dev->vblank_refcount) * - dev->num_crtcs, DRM_MEM_DRIVER); - drm_free(dev->vblank_enabled, sizeof(*dev->vblank_enabled) * - dev->num_crtcs, DRM_MEM_DRIVER); - drm_free(dev->last_vblank, sizeof(*dev->last_vblank) * dev->num_crtcs, - DRM_MEM_DRIVER); - drm_free(dev->vblank_premodeset, sizeof(*dev->vblank_premodeset) * - dev->num_crtcs, DRM_MEM_DRIVER); - drm_free(dev->vblank_offset, sizeof(*dev->vblank_offset) * dev->num_crtcs, + if (dev->vbl_queue) + drm_free(dev->vbl_queue, sizeof(*dev->vbl_queue) * dev->num_crtcs, DRM_MEM_DRIVER); - dev->num_crtcs = 0; + if (dev->vbl_sigs) + drm_free(dev->vbl_sigs, sizeof(*dev->vbl_sigs) * dev->num_crtcs, + DRM_MEM_DRIVER); + + if (dev->_vblank_count) + drm_free(dev->_vblank_count, sizeof(*dev->_vblank_count) * + dev->num_crtcs, DRM_MEM_DRIVER); + + if (dev->vblank_refcount) + drm_free(dev->vblank_refcount, sizeof(*dev->vblank_refcount) * + dev->num_crtcs, DRM_MEM_DRIVER); + + if (dev->vblank_enabled) + drm_free(dev->vblank_enabled, sizeof(*dev->vblank_enabled) * + dev->num_crtcs, DRM_MEM_DRIVER); + + if (dev->last_vblank) + drm_free(dev->last_vblank, sizeof(*dev->last_vblank) * dev->num_crtcs, + DRM_MEM_DRIVER); + + if (dev->vblank_premodeset) + drm_free(dev->vblank_premodeset, sizeof(*dev->vblank_premodeset) * + dev->num_crtcs, DRM_MEM_DRIVER); + + if (dev->vblank_offset) + drm_free(dev->vblank_offset, sizeof(*dev->vblank_offset) * dev->num_crtcs, + DRM_MEM_DRIVER); } int drm_vblank_init(struct drm_device *dev, int num_crtcs) @@ -182,6 +191,82 @@ err: } EXPORT_SYMBOL(drm_vblank_init); +int drm_wait_hotplug(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + union drm_wait_hotplug *hotplugwait = data; + struct timeval now; + int ret = 0; + unsigned int flags; + + if ((!dev->irq) || (!dev->irq_enabled)) + return -EINVAL; + + flags = hotplugwait->request.type; + + if (flags & _DRM_HOTPLUG_SIGNAL) { + unsigned long irqflags; + struct list_head *hotplug_sigs = dev->hotplug_sigs; + struct drm_hotplug_sig *hotplug_sig; + + hotplug_sig = drm_calloc(1, sizeof(struct drm_hotplug_sig), + DRM_MEM_DRIVER); + if (!hotplug_sig) + return -ENOMEM; + + atomic_inc(&dev->hotplug_signal_pending); + + hotplug_sig->info.si_signo = hotplugwait->request.signal; + hotplug_sig->task = current; + hotplug_sig->counter = + hotplugwait->reply.counter = + dev->mode_config.hotplug_counter; + + spin_lock_irqsave(&dev->hotplug_lock, irqflags); + + list_add_tail(&hotplug_sig->head, hotplug_sigs); + + spin_unlock_irqrestore(&dev->hotplug_lock, irqflags); + } else { + int cur_hotplug = dev->mode_config.hotplug_counter; + + DRM_WAIT_ON(ret, dev->hotplug_queue, 3 * DRM_HZ, + dev->mode_config.hotplug_counter > cur_hotplug); + + do_gettimeofday(&now); + + hotplugwait->reply.tval_sec = now.tv_sec; + hotplugwait->reply.tval_usec = now.tv_usec; + hotplugwait->reply.counter = dev->mode_config.hotplug_counter; + } + + return ret; +} + +static void drm_hotplug_cleanup(struct drm_device *dev) +{ + if (dev->hotplug_sigs) + drm_free(dev->hotplug_sigs, sizeof(*dev->hotplug_sigs), + DRM_MEM_DRIVER); +} +EXPORT_SYMBOL(drm_hotplug_cleanup); + +int drm_hotplug_init(struct drm_device *dev) +{ + spin_lock_init(&dev->hotplug_lock); + atomic_set(&dev->hotplug_signal_pending, 0); + + dev->hotplug_sigs = drm_alloc(sizeof(struct list_head), DRM_MEM_DRIVER); + if (!dev->hotplug_sigs) + return -ENOMEM; + + INIT_LIST_HEAD(dev->hotplug_sigs); + init_waitqueue_head(&dev->hotplug_queue); + + return 0; +} +EXPORT_SYMBOL(drm_hotplug_init); + /** * Install IRQ handler. * @@ -277,6 +362,8 @@ int drm_irq_uninstall(struct drm_device * dev) drm_vblank_cleanup(dev); + drm_hotplug_cleanup(dev); + dev->locked_tasklet_func = NULL; return 0; @@ -530,7 +617,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data, if (flags & _DRM_VBLANK_SIGNAL) { unsigned long irqflags; struct list_head *vbl_sigs = &dev->vbl_sigs[crtc]; - struct drm_vbl_sig *vbl_sig; + struct drm_vbl_sig *vbl_sig, *tmp; spin_lock_irqsave(&dev->vbl_lock, irqflags); @@ -538,7 +625,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data, * for the same vblank sequence number; nothing to be done in * that case */ - list_for_each_entry(vbl_sig, vbl_sigs, head) { + list_for_each_entry_safe(vbl_sig, tmp, vbl_sigs, head) { if (vbl_sig->sequence == vblwait->request.sequence && vbl_sig->info.si_signo == vblwait->request.signal @@ -659,6 +746,53 @@ void drm_handle_vblank(struct drm_device *dev, int crtc) } EXPORT_SYMBOL(drm_handle_vblank); +/** + * Send the HOTPLUG signals. + * + * \param dev DRM device. + * + * Sends a signal for each task in drm_device::hotplug_sigs and empties the list. + */ +static void drm_hotplug_send_signals(struct drm_device * dev) +{ + struct drm_hotplug_sig *hotplug_sig, *tmp; + struct list_head *hotplug_sigs; + unsigned long flags; + + spin_lock_irqsave(&dev->hotplug_lock, flags); + + hotplug_sigs = dev->hotplug_sigs; + + list_for_each_entry_safe(hotplug_sig, tmp, hotplug_sigs, head) { + hotplug_sig->info.si_code = hotplug_sig->counter; + + send_sig_info(hotplug_sig->info.si_signo, + &hotplug_sig->info, hotplug_sig->task); + + list_del(&hotplug_sig->head); + + drm_free(hotplug_sig, sizeof(*hotplug_sig), + DRM_MEM_DRIVER); + atomic_dec(&dev->hotplug_signal_pending); + } + + spin_unlock_irqrestore(&dev->hotplug_lock, flags); +} + +/** + * drm_handle_hotplug - handle a hotplug event + * @dev: DRM device + * @crtc: where this event occurred + * + * Drivers should call this routine in their hotplug interrupt handlers. + */ +void drm_handle_hotplug(struct drm_device *dev) +{ + DRM_WAKEUP(&dev->hotplug_queue); + drm_hotplug_send_signals(dev); +} +EXPORT_SYMBOL(drm_handle_hotplug); + /** * Tasklet wrapper function. * diff --git a/linux-core/i915_fence.c b/linux-core/i915_fence.c index 0f6cdeef..f392e8e6 100644 --- a/linux-core/i915_fence.c +++ b/linux-core/i915_fence.c @@ -120,11 +120,11 @@ static void i915_fence_poll(struct drm_device *dev, uint32_t fence_class, if (dev_priv->fence_irq_on && !(fc->waiting_types & DRM_FENCE_TYPE_EXE)) { - i915_user_irq_off(dev_priv); + i915_user_irq_off(dev); dev_priv->fence_irq_on = 0; } else if (!dev_priv->fence_irq_on && (fc->waiting_types & DRM_FENCE_TYPE_EXE)) { - i915_user_irq_on(dev_priv); + i915_user_irq_on(dev); dev_priv->fence_irq_on = 1; } } diff --git a/linux-core/intel_crt.c b/linux-core/intel_crt.c index a9fb50a3..915e430d 100644 --- a/linux-core/intel_crt.c +++ b/linux-core/intel_crt.c @@ -132,7 +132,7 @@ static void intel_crt_mode_set(struct drm_output *output, /** * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect CRT presence. * - * Only for I945G/GM. + * Not for i915G/i915GM * * \return TRUE if CRT is connected. * \return FALSE if CRT is disconnected. @@ -142,7 +142,7 @@ static bool intel_crt_detect_hotplug(struct drm_output *output) struct drm_device *dev = output->dev; struct drm_i915_private *dev_priv = dev->dev_private; u32 temp; -#if 1 + unsigned long timeout = jiffies + msecs_to_jiffies(1000); temp = I915_READ(PORT_HOTPLUG_EN); @@ -161,15 +161,6 @@ static bool intel_crt_detect_hotplug(struct drm_output *output) return true; return false; -#else - temp = I915_READ(PORT_HOTPLUG_STAT); - DRM_DEBUG("HST 0x%08x\n", temp); - - if (temp & (1 << 8) && temp & (1 << 9)) - return true; - - return false; -#endif } static bool intel_crt_detect_ddc(struct drm_output *output) @@ -187,7 +178,7 @@ static enum drm_output_status intel_crt_detect(struct drm_output *output) { struct drm_device *dev = output->dev; - if (IS_I945G(dev) || IS_I945GM(dev) || IS_I965G(dev)) { + if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) { if (intel_crt_detect_hotplug(output)) return output_status_connected; else diff --git a/linux-core/intel_sdvo.c b/linux-core/intel_sdvo.c index 3887df00..a8441d8f 100644 --- a/linux-core/intel_sdvo.c +++ b/linux-core/intel_sdvo.c @@ -53,7 +53,6 @@ struct intel_sdvo_priv { struct intel_sdvo_dtd save_input_dtd_1, save_input_dtd_2; struct intel_sdvo_dtd save_output_dtd[16]; u32 save_SDVOX; - int hotplug_enabled; }; /** @@ -72,14 +71,8 @@ void intel_sdvo_write_sdvox(struct drm_output *output, u32 val) if (sdvo_priv->output_device == SDVOB) { cval = I915_READ(SDVOC); - - if (sdvo_priv->hotplug_enabled) - bval = bval | (1 << 26); } else { bval = I915_READ(SDVOB); - - if (sdvo_priv->hotplug_enabled) - cval = cval | (1 << 26); } /* * Write the registers twice for luck. Sometimes, @@ -933,8 +926,6 @@ int intel_sdvo_supports_hotplug(struct drm_output *output) void intel_sdvo_set_hotplug(struct drm_output *output, int on) { - struct intel_output *intel_output = output->driver_private; - struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; u8 response[2]; u8 status; @@ -942,15 +933,11 @@ void intel_sdvo_set_hotplug(struct drm_output *output, int on) intel_sdvo_read_response(output, &response, 2); if (on) { - sdvo_priv->hotplug_enabled = 1; - intel_sdvo_write_cmd(output, SDVO_CMD_GET_HOT_PLUG_SUPPORT, NULL, 0); status = intel_sdvo_read_response(output, &response, 2); intel_sdvo_write_cmd(output, SDVO_CMD_SET_ACTIVE_HOT_PLUG, &response, 2); } else { - sdvo_priv->hotplug_enabled = 0; - response[0] = 0; response[1] = 0; intel_sdvo_write_cmd(output, SDVO_CMD_SET_ACTIVE_HOT_PLUG, &response, 2); @@ -1074,7 +1061,6 @@ void intel_sdvo_init(struct drm_device *dev, int output_device) } sdvo_priv->output_device = output_device; - sdvo_priv->hotplug_enabled = 0; intel_output->i2c_bus = i2cbus; intel_output->dev_priv = sdvo_priv; diff --git a/shared-core/drm.h b/shared-core/drm.h index 303a84b6..a1ebfb93 100644 --- a/shared-core/drm.h +++ b/shared-core/drm.h @@ -555,6 +555,39 @@ union drm_wait_vblank { struct drm_wait_vblank_reply reply; }; +/* Handle monitor hotplug. + * + * May want to extend this later to pass reply information which + * details the outputs which generated the hotplug event. + * Some chipsets can't determine that though, and we'd need to leave + * it to the higher levels to determine exactly what changed. + */ +enum drm_hotplug_seq_type { + _DRM_HOTPLUG_SIGNAL = 0x00000001, /**< Send signal instead of blocking */ +}; + +struct drm_wait_hotplug_request { + enum drm_hotplug_seq_type type; + unsigned long signal; +}; + +struct drm_wait_hotplug_reply { + enum drm_hotplug_seq_type type; + unsigned int counter; + long tval_sec; + long tval_usec; +}; + +/** + * DRM_IOCTL_WAIT_HOTPLUG ioctl argument type. + * + * \sa drmWaitHotplug(). + */ +union drm_wait_hotplug { + struct drm_wait_hotplug_request request; + struct drm_wait_hotplug_reply reply; +}; + enum drm_modeset_ctl_cmd { _DRM_PRE_MODESET = 1, _DRM_POST_MODESET = 2, @@ -1238,6 +1271,7 @@ struct drm_mode_hotplug { #define DRM_IOCTL_MODE_GETPROPERTY DRM_IOWR(0xAB, struct drm_mode_get_property) #define DRM_IOCTL_MODE_CURSOR DRM_IOWR(0xAC, struct drm_mode_cursor) #define DRM_IOCTL_MODE_HOTPLUG DRM_IOWR(0xAD, struct drm_mode_hotplug) +#define DRM_IOCTL_WAIT_HOTPLUG DRM_IOWR(0xAE, union drm_wait_hotplug) /*@}*/ diff --git a/shared-core/i915_drv.h b/shared-core/i915_drv.h index b8dfbc34..fb855933 100644 --- a/shared-core/i915_drv.h +++ b/shared-core/i915_drv.h @@ -307,8 +307,8 @@ extern void i915_disable_vblank(struct drm_device *dev, int crtc); extern u32 i915_get_vblank_counter(struct drm_device *dev, int crtc); extern int i915_vblank_swap(struct drm_device *dev, void *data, struct drm_file *file_priv); -extern void i915_user_irq_on(struct drm_i915_private *dev_priv); -extern void i915_user_irq_off(struct drm_i915_private *dev_priv); +extern void i915_user_irq_on(struct drm_device *dev); +extern void i915_user_irq_off(struct drm_device *dev); /* i915_mem.c */ extern int i915_mem_alloc(struct drm_device *dev, void *data, @@ -536,6 +536,12 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); #define I915REG_PIPEASTAT 0x70024 #define I915REG_PIPEBSTAT 0x71024 + +#define I915_VBLANK_INTERRUPT_ENABLE (1UL<<17) +#define I915_HOTPLUG_INTERRUPT_ENABLE (1UL<<26) +#define I915_HOTPLUG_CLEAR (1UL<<10) +#define I915_VBLANK_CLEAR (1UL<<1) + /* * The two pipe frame counter registers are not synchronized, so * reading a stable value is somewhat tricky. The following code @@ -567,9 +573,6 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); #define PIPE_PIXEL_MASK 0x00ffffff #define PIPE_PIXEL_SHIFT 0 -#define I915_VBLANK_INTERRUPT_ENABLE (1UL<<17) -#define I915_VBLANK_CLEAR (1UL<<1) - #define GPIOA 0x5010 #define GPIOB 0x5014 #define GPIOC 0x5018 @@ -1153,8 +1156,8 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); #define SDVOB_PCIE_CONCURRENCY (1 << 3) #define SDVO_DETECTED (1 << 2) /* Bits to be preserved when writing */ -#define SDVOB_PRESERVE_MASK ((1 << 17) | (1 << 16) | (1 << 14)) -#define SDVOC_PRESERVE_MASK (1 << 17) +#define SDVOB_PRESERVE_MASK ((1 << 17) | (1 << 16) | (1 << 14) | (1 << 26)) +#define SDVOC_PRESERVE_MASK ((1 << 17) | (1 << 26)) /** @defgroup LVDS * @{ diff --git a/shared-core/i915_irq.c b/shared-core/i915_irq.c index b9d137f4..4127383a 100644 --- a/shared-core/i915_irq.c +++ b/shared-core/i915_irq.c @@ -34,7 +34,9 @@ #include "intel_drv.h" #define USER_INT_FLAG (1<<1) +#define EVENT_PIPEB_FLAG (1<<4) #define VSYNC_PIPEB_FLAG (1<<5) +#define EVENT_PIPEA_FLAG (1<<6) #define VSYNC_PIPEA_FLAG (1<<7) #define HOTPLUG_FLAG (1 << 17) @@ -158,13 +160,14 @@ static void i915_vblank_tasklet(struct drm_device *dev) int nhits, nrects, slice[2], upper[2], lower[2], i, num_pages; unsigned counter[2]; struct drm_drawable_info *drw; - struct drm_i915_sarea *sarea_priv; + struct drm_i915_sarea *sarea_priv = master_priv->sarea_priv; u32 cpp = dev_priv->cpp, offsets[3]; u32 cmd = (cpp == 4) ? (XY_SRC_COPY_BLT_CMD | XY_SRC_COPY_BLT_WRITE_ALPHA | XY_SRC_COPY_BLT_WRITE_RGB) : XY_SRC_COPY_BLT_CMD; - u32 pitchropcpp; + u32 pitchropcpp = (sarea_priv->pitch * cpp) | (0xcc << 16) | + (cpp << 23) | (1 << 24); RING_LOCALS; counter[0] = drm_vblank_count(dev, 0); @@ -434,7 +437,7 @@ static struct drm_device *hotplug_dev; static int hotplug_cmd = 0; static spinlock_t hotplug_lock = SPIN_LOCK_UNLOCKED; -static void i915_hotplug_crt(struct drm_device *dev, bool connected) +static void i915_hotplug_crt(struct drm_device *dev, bool isconnected) { struct drm_output *output; struct intel_output *iout; @@ -453,7 +456,7 @@ static void i915_hotplug_crt(struct drm_device *dev, bool connected) if (iout == 0) goto unlock; - drm_hotplug_stage_two(dev, output, connected); + drm_hotplug_stage_two(dev, output, isconnected); unlock: mutex_unlock(&dev->mode_config.mutex); @@ -468,10 +471,8 @@ static void i915_hotplug_sdvo(struct drm_device *dev, int sdvoB) output = intel_sdvo_find(dev, sdvoB); - if (!output) { - DRM_ERROR("could not find sdvo%s output\n", sdvoB ? "B" : "C"); + if (!output) goto unlock; - } status = output->funcs->detect(output); @@ -480,7 +481,6 @@ static void i915_hotplug_sdvo(struct drm_device *dev, int sdvoB) else drm_hotplug_stage_two(dev, output, true); - /* wierd hw bug, sdvo stop sending interupts */ intel_sdvo_set_hotplug(output, 1); unlock: @@ -521,6 +521,7 @@ static void i915_hotplug_work_func(struct work_struct *work) if (sdvoC) i915_hotplug_sdvo(dev, 0); + drm_handle_hotplug(dev); } static int i915_run_hotplug_tasklet(struct drm_device *dev, uint32_t stat) @@ -575,45 +576,21 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) struct drm_i915_master_private *master_priv; struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private; u32 temp = 0; - u32 temp2; u32 pipea_stats, pipeb_stats; - pipea_stats = I915_READ(I915REG_PIPEASTAT); - pipeb_stats = I915_READ(I915REG_PIPEBSTAT); - - /* On i8xx hw the IIR and IER are 16bit on i9xx its 32bit */ - if (IS_I9XX(dev)) + /* On i8xx/i915 hw the IIR and IER are 16bit on i9xx its 32bit */ + if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) temp = I915_READ(I915REG_INT_IDENTITY_R); else temp = I915_READ16(I915REG_INT_IDENTITY_R); - temp2 = temp; temp &= (dev_priv->irq_enable_reg | USER_INT_FLAG); -#if 0 - /* ugly despamification of pipeb event irq */ - if (temp & (0xFFFFFFF ^ ((1 << 5) | (1 << 7)))) { - DRM_DEBUG("IIR %08x\n", temp2); - DRM_DEBUG("MSK %08x\n", dev_priv->irq_enable_reg | USER_INT_FLAG); - DRM_DEBUG("M&I %08x\n", temp); - DRM_DEBUG("HOT %08x\n", I915_READ(PORT_HOTPLUG_STAT)); - } -#else -#if 0 - DRM_DEBUG("flag=%08x\n", temp); -#endif -#endif - if (temp == 0) return IRQ_NONE; - if (IS_I9XX(dev)) { - I915_WRITE(I915REG_INT_IDENTITY_R, temp); - (void) I915_READ(I915REG_INT_IDENTITY_R); - } else { - I915_WRITE16(I915REG_INT_IDENTITY_R, temp); - (void) I915_READ16(I915REG_INT_IDENTITY_R); - } + pipea_stats = I915_READ(I915REG_PIPEASTAT); + pipeb_stats = I915_READ(I915REG_PIPEBSTAT); /* * Clear the PIPE(A|B)STAT regs before the IIR otherwise @@ -621,25 +598,40 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) */ if (temp & VSYNC_PIPEA_FLAG) { drm_handle_vblank(dev, i915_get_plane(dev, 0)); - I915_WRITE(I915REG_PIPEASTAT, - pipea_stats | I915_VBLANK_INTERRUPT_ENABLE | - I915_VBLANK_CLEAR); + + pipea_stats |= I915_VBLANK_INTERRUPT_ENABLE | + I915_VBLANK_CLEAR; } if (temp & VSYNC_PIPEB_FLAG) { drm_handle_vblank(dev, i915_get_plane(dev, 1)); - I915_WRITE(I915REG_PIPEBSTAT, - pipeb_stats | I915_VBLANK_INTERRUPT_ENABLE | - I915_VBLANK_CLEAR); + + pipeb_stats |= I915_VBLANK_INTERRUPT_ENABLE | + I915_VBLANK_CLEAR; } - I915_WRITE16(I915REG_INT_IDENTITY_R, temp); - (void) I915_READ16(I915REG_INT_IDENTITY_R); /* Flush posted write */ + if (temp & EVENT_PIPEA_FLAG) + pipea_stats |= I915_HOTPLUG_INTERRUPT_ENABLE | + I915_HOTPLUG_CLEAR; - DRM_READMEMORYBARRIER(); + if (temp & EVENT_PIPEB_FLAG) + pipeb_stats |= I915_HOTPLUG_INTERRUPT_ENABLE | + I915_HOTPLUG_CLEAR; + + I915_WRITE(I915REG_PIPEASTAT, pipea_stats); + (void) I915_READ(I915REG_PIPEASTAT); + I915_WRITE(I915REG_PIPEBSTAT, pipeb_stats); + (void) I915_READ(I915REG_PIPEBSTAT); + + /* Clear the generated interrupt */ + if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) { + I915_WRITE(I915REG_INT_IDENTITY_R, temp); + (void) I915_READ(I915REG_INT_IDENTITY_R); + } else { + I915_WRITE16(I915REG_INT_IDENTITY_R, temp); + (void) I915_READ16(I915REG_INT_IDENTITY_R); + } - temp &= (dev_priv->irq_enable_reg | USER_INT_FLAG | VSYNC_PIPEA_FLAG | - VSYNC_PIPEB_FLAG); if (dev->primary->master) { master_priv = dev->primary->master->driver_priv; @@ -658,15 +650,43 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) drm_locked_tasklet(dev, i915_vblank_tasklet); } - /* for now lest just ack it */ - if (temp & (1 << 17)) { - DRM_DEBUG("Hotplug event received\n"); + if (temp & (HOTPLUG_FLAG | EVENT_PIPEA_FLAG | EVENT_PIPEB_FLAG)) { + u32 temp2 = 0; - temp2 = I915_READ(PORT_HOTPLUG_STAT); + DRM_INFO("Hotplug event received\n"); + if (!IS_I9XX(dev) || IS_I915G(dev) || IS_I915GM(dev)) { +#if 0 + u32 b,c; + + b = I915_READ(SDVOB) & SDVO_PIPE_B_SELECT; + c = I915_READ(SDVOC) & SDVO_PIPE_B_SELECT; + + if (temp & EVENT_PIPEA_FLAG) { + if (!b) + temp2 |= SDVOB_HOTPLUG_INT_STATUS; + if (!c) + temp2 |= SDVOC_HOTPLUG_INT_STATUS; + + } + + if (temp & EVENT_PIPEB_FLAG) { + if (b) + temp2 |= SDVOB_HOTPLUG_INT_STATUS; + if (c) + temp2 |= SDVOC_HOTPLUG_INT_STATUS; + + } +#else + temp2 |= SDVOB_HOTPLUG_INT_STATUS | + SDVOC_HOTPLUG_INT_STATUS; +#endif + } else { + temp2 = I915_READ(PORT_HOTPLUG_STAT); + + I915_WRITE(PORT_HOTPLUG_STAT, temp2); + } i915_run_hotplug_tasklet(dev, temp2); - - I915_WRITE(PORT_HOTPLUG_STAT,temp2); } return IRQ_HANDLED; @@ -691,23 +711,33 @@ int i915_emit_irq(struct drm_device *dev) return dev_priv->counter; } -void i915_user_irq_on(struct drm_i915_private *dev_priv) +void i915_user_irq_on(struct drm_device *dev) { + struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private; + DRM_SPINLOCK(&dev_priv->user_irq_lock); if (dev_priv->irq_enabled && (++dev_priv->user_irq_refcount == 1)){ dev_priv->irq_enable_reg |= USER_INT_FLAG; - I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); + if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) + I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); + else + I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); } DRM_SPINUNLOCK(&dev_priv->user_irq_lock); } -void i915_user_irq_off(struct drm_i915_private *dev_priv) +void i915_user_irq_off(struct drm_device *dev) { + struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private; + DRM_SPINLOCK(&dev_priv->user_irq_lock); if (dev_priv->irq_enabled && (--dev_priv->user_irq_refcount == 0)) { // dev_priv->irq_enable_reg &= ~USER_INT_FLAG; - // I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); + // if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) + // I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); + // else + // I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); } DRM_SPINUNLOCK(&dev_priv->user_irq_lock); } @@ -725,10 +755,10 @@ static int i915_wait_irq(struct drm_device * dev, int irq_nr) if (READ_BREADCRUMB(dev_priv) >= irq_nr) return 0; - i915_user_irq_on(dev_priv); + i915_user_irq_on(dev); DRM_WAIT_ON(ret, dev_priv->irq_queue, 3 * DRM_HZ, READ_BREADCRUMB(dev_priv) >= irq_nr); - i915_user_irq_off(dev_priv); + i915_user_irq_off(dev); if (ret == -EBUSY) { DRM_ERROR("EBUSY -- rec: %d emitted: %d\n", @@ -803,7 +833,10 @@ int i915_enable_vblank(struct drm_device *dev, int plane) break; } - I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); + if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) + I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); + else + I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); return 0; } @@ -826,7 +859,10 @@ void i915_disable_vblank(struct drm_device *dev, int plane) break; } - I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); + if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) + I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); + else + I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); } void i915_enable_interrupt (struct drm_device *dev) @@ -836,40 +872,62 @@ void i915_enable_interrupt (struct drm_device *dev) dev_priv->irq_enable_reg |= USER_INT_FLAG; - if (IS_I9XX(dev) && dev->mode_config.num_output) { - dev_priv->irq_enable_reg |= HOTPLUG_FLAG; - - /* Activate the CRT */ - I915_WRITE(PORT_HOTPLUG_EN, CRT_HOTPLUG_INT_EN); - - /* SDVOB */ - o = intel_sdvo_find(dev, 1); - if (o && intel_sdvo_supports_hotplug(o)) { - intel_sdvo_set_hotplug(o, 1); - I915_WRITE(PORT_HOTPLUG_EN, SDVOB_HOTPLUG_INT_EN); - } - - /* SDVOC */ - o = intel_sdvo_find(dev, 0); - if (o && intel_sdvo_supports_hotplug(o)) { - intel_sdvo_set_hotplug(o, 1); - I915_WRITE(PORT_HOTPLUG_EN, SDVOC_HOTPLUG_INT_EN); - } - - } - - if (IS_I9XX(dev)) { - I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); + if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) { + if (dev->mode_config.num_output) + dev_priv->irq_enable_reg |= HOTPLUG_FLAG; } else { - I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); + if (dev->mode_config.num_output) + dev_priv->irq_enable_reg |= EVENT_PIPEA_FLAG | EVENT_PIPEB_FLAG; + + I915_WRITE(I915REG_PIPEASTAT, I915_READ(I915REG_PIPEASTAT) | I915_HOTPLUG_INTERRUPT_ENABLE | I915_HOTPLUG_CLEAR); + I915_WRITE(I915REG_PIPEBSTAT, I915_READ(I915REG_PIPEBSTAT) | I915_HOTPLUG_INTERRUPT_ENABLE | I915_HOTPLUG_CLEAR); } - DRM_DEBUG("HEN %08x\n",I915_READ(PORT_HOTPLUG_EN)); - DRM_DEBUG("HST %08x\n",I915_READ(PORT_HOTPLUG_STAT)); - DRM_DEBUG("IER %08x\n",I915_READ(I915REG_INT_ENABLE_R)); - DRM_DEBUG("SDB %08x\n",I915_READ(SDVOB)); + if (dev_priv->irq_enable_reg & (HOTPLUG_FLAG | EVENT_PIPEA_FLAG | EVENT_PIPEB_FLAG)) { + u32 temp = 0; - I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); + if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) { + temp = I915_READ(PORT_HOTPLUG_EN); + + /* Activate the CRT */ + temp |= CRT_HOTPLUG_INT_EN; + } + + if (IS_I9XX(dev)) { + /* SDVOB */ + o = intel_sdvo_find(dev, 1); + if (o && intel_sdvo_supports_hotplug(o)) { + intel_sdvo_set_hotplug(o, 1); + temp |= SDVOB_HOTPLUG_INT_EN; + } + + /* SDVOC */ + o = intel_sdvo_find(dev, 0); + if (o && intel_sdvo_supports_hotplug(o)) { + intel_sdvo_set_hotplug(o, 1); + temp |= SDVOC_HOTPLUG_INT_EN; + } + + I915_WRITE(SDVOB, I915_READ(SDVOB) | SDVO_INTERRUPT_ENABLE); + I915_WRITE(SDVOC, I915_READ(SDVOC) | SDVO_INTERRUPT_ENABLE); + } else { + /* DVO ???? */ + } + + if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) { + I915_WRITE(PORT_HOTPLUG_EN, temp); + + DRM_DEBUG("HEN %08x\n",I915_READ(PORT_HOTPLUG_EN)); + DRM_DEBUG("HST %08x\n",I915_READ(PORT_HOTPLUG_STAT)); + + I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); + } + } + + if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) + I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); + else + I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); dev_priv->irq_enabled = 1; } @@ -909,7 +967,11 @@ int i915_vblank_pipe_get(struct drm_device *dev, void *data, return -EINVAL; } - flag = I915_READ(I915REG_INT_ENABLE_R); + if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) + flag = I915_READ(I915REG_INT_ENABLE_R); + else + flag = I915_READ16(I915REG_INT_ENABLE_R); + pipe->pipe = 0; if (flag & VSYNC_PIPEA_FLAG) pipe->pipe |= DRM_I915_VBLANK_PIPE_A; @@ -1086,8 +1148,10 @@ void i915_driver_irq_preinstall(struct drm_device * dev) { struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private; + I915_WRITE(I915REG_PIPEASTAT, 0xffff); + I915_WRITE(I915REG_PIPEBSTAT, 0xffff); I915_WRITE16(I915REG_HWSTAM, 0xeffe); - if (IS_I9XX(dev)) { + if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) { I915_WRITE(I915REG_INT_MASK_R, 0x0); I915_WRITE(I915REG_INT_ENABLE_R, 0x0); } else { @@ -1114,6 +1178,10 @@ int i915_driver_irq_postinstall(struct drm_device * dev) if (ret) return ret; + ret = drm_hotplug_init(dev); + if (ret) + return ret; + dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ i915_enable_interrupt(dev); @@ -1138,7 +1206,9 @@ void i915_driver_irq_uninstall(struct drm_device * dev) dev_priv->irq_enabled = 0; - if(IS_I9XX(dev)) { + I915_WRITE(I915REG_PIPEASTAT, 0xffff); + I915_WRITE(I915REG_PIPEBSTAT, 0xffff); + if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) { I915_WRITE(I915REG_HWSTAM, 0xffffffff); I915_WRITE(I915REG_INT_MASK_R, 0xffffffff); I915_WRITE(I915REG_INT_ENABLE_R, 0x0); From cf1a2499ed9a0051bcd8627136fb53b496b6484c Mon Sep 17 00:00:00 2001 From: Alan Hourihane Date: Tue, 11 Mar 2008 21:24:29 +0000 Subject: [PATCH 10/26] global hotplug events happen in the pipe A stat register, they are not pipe A specific. Remove pipe B code. --- shared-core/i915_irq.c | 38 +++++++++----------------------------- 1 file changed, 9 insertions(+), 29 deletions(-) diff --git a/shared-core/i915_irq.c b/shared-core/i915_irq.c index 4127383a..558693f1 100644 --- a/shared-core/i915_irq.c +++ b/shared-core/i915_irq.c @@ -577,6 +577,7 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private; u32 temp = 0; u32 pipea_stats, pipeb_stats; + int hotplug = 0; /* On i8xx/i915 hw the IIR and IER are 16bit on i9xx its 32bit */ if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) @@ -610,13 +611,15 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) I915_VBLANK_CLEAR; } - if (temp & EVENT_PIPEA_FLAG) + /* This is a global event, and not a pipe A event */ + if (temp & EVENT_PIPEA_FLAG) { + if (pipea_stats & I915_HOTPLUG_CLEAR) + hotplug = 1; + pipea_stats |= I915_HOTPLUG_INTERRUPT_ENABLE | I915_HOTPLUG_CLEAR; - if (temp & EVENT_PIPEB_FLAG) - pipeb_stats |= I915_HOTPLUG_INTERRUPT_ENABLE | - I915_HOTPLUG_CLEAR; + } I915_WRITE(I915REG_PIPEASTAT, pipea_stats); (void) I915_READ(I915REG_PIPEASTAT); @@ -650,37 +653,14 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) drm_locked_tasklet(dev, i915_vblank_tasklet); } - if (temp & (HOTPLUG_FLAG | EVENT_PIPEA_FLAG | EVENT_PIPEB_FLAG)) { + if ((temp & HOTPLUG_FLAG) || hotplug) { u32 temp2 = 0; DRM_INFO("Hotplug event received\n"); if (!IS_I9XX(dev) || IS_I915G(dev) || IS_I915GM(dev)) { -#if 0 - u32 b,c; - - b = I915_READ(SDVOB) & SDVO_PIPE_B_SELECT; - c = I915_READ(SDVOC) & SDVO_PIPE_B_SELECT; - - if (temp & EVENT_PIPEA_FLAG) { - if (!b) - temp2 |= SDVOB_HOTPLUG_INT_STATUS; - if (!c) - temp2 |= SDVOC_HOTPLUG_INT_STATUS; - - } - - if (temp & EVENT_PIPEB_FLAG) { - if (b) - temp2 |= SDVOB_HOTPLUG_INT_STATUS; - if (c) - temp2 |= SDVOC_HOTPLUG_INT_STATUS; - - } -#else temp2 |= SDVOB_HOTPLUG_INT_STATUS | SDVOC_HOTPLUG_INT_STATUS; -#endif } else { temp2 = I915_READ(PORT_HOTPLUG_STAT); @@ -879,8 +859,8 @@ void i915_enable_interrupt (struct drm_device *dev) if (dev->mode_config.num_output) dev_priv->irq_enable_reg |= EVENT_PIPEA_FLAG | EVENT_PIPEB_FLAG; + /* Enable global interrupts for hotplug - not a pipeA event */ I915_WRITE(I915REG_PIPEASTAT, I915_READ(I915REG_PIPEASTAT) | I915_HOTPLUG_INTERRUPT_ENABLE | I915_HOTPLUG_CLEAR); - I915_WRITE(I915REG_PIPEBSTAT, I915_READ(I915REG_PIPEBSTAT) | I915_HOTPLUG_INTERRUPT_ENABLE | I915_HOTPLUG_CLEAR); } if (dev_priv->irq_enable_reg & (HOTPLUG_FLAG | EVENT_PIPEA_FLAG | EVENT_PIPEB_FLAG)) { From 981f515e2bd4d570ea33bb74ae82cd5b56cc9121 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Wed, 12 Mar 2008 14:48:01 +1000 Subject: [PATCH 11/26] drm: fix fd closing ordering. If the master fd goes away before the aiglx fd, we try and get a lock that actually doesn't exist. --- linux-core/drm_fops.c | 100 ++++++++++++++++++++++-------------------- 1 file changed, 52 insertions(+), 48 deletions(-) diff --git a/linux-core/drm_fops.c b/linux-core/drm_fops.c index fcadc544..0ca43824 100644 --- a/linux-core/drm_fops.c +++ b/linux-core/drm_fops.c @@ -402,59 +402,63 @@ int drm_release(struct inode *inode, struct file *filp) current->pid, (long)old_encode_dev(file_priv->minor->device), dev->open_count); - if (dev->driver->reclaim_buffers_locked && file_priv->master->lock.hw_lock) { - if (drm_i_have_hw_lock(dev, file_priv)) { - dev->driver->reclaim_buffers_locked(dev, file_priv); - } else { - unsigned long _end=jiffies + 3*DRM_HZ; - int locked = 0; - - drm_idlelock_take(&file_priv->master->lock); - - /* - * Wait for a while. - */ - - do{ - spin_lock(&file_priv->master->lock.spinlock); - locked = file_priv->master->lock.idle_has_lock; - spin_unlock(&file_priv->master->lock.spinlock); - if (locked) - break; - schedule(); - } while (!time_after_eq(jiffies, _end)); - - if (!locked) { - DRM_ERROR("reclaim_buffers_locked() deadlock. Please rework this\n" - "\tdriver to use reclaim_buffers_idlelocked() instead.\n" - "\tI will go on reclaiming the buffers anyway.\n"); + /* if the master has gone away we can't do anything with the lock */ + if (file_priv->minor->master) { + if (dev->driver->reclaim_buffers_locked && file_priv->master->lock.hw_lock) { + if (drm_i_have_hw_lock(dev, file_priv)) { + dev->driver->reclaim_buffers_locked(dev, file_priv); + } else { + unsigned long _end=jiffies + 3*DRM_HZ; + int locked = 0; + + drm_idlelock_take(&file_priv->master->lock); + + /* + * Wait for a while. + */ + + do{ + spin_lock(&file_priv->master->lock.spinlock); + locked = file_priv->master->lock.idle_has_lock; + spin_unlock(&file_priv->master->lock.spinlock); + if (locked) + break; + schedule(); + } while (!time_after_eq(jiffies, _end)); + + if (!locked) { + DRM_ERROR("reclaim_buffers_locked() deadlock. Please rework this\n" + "\tdriver to use reclaim_buffers_idlelocked() instead.\n" + "\tI will go on reclaiming the buffers anyway.\n"); + } + + dev->driver->reclaim_buffers_locked(dev, file_priv); + drm_idlelock_release(&file_priv->master->lock); } - - dev->driver->reclaim_buffers_locked(dev, file_priv); - drm_idlelock_release(&file_priv->master->lock); } - } - if (dev->driver->reclaim_buffers_idlelocked && file_priv->master->lock.hw_lock) { - - drm_idlelock_take(&file_priv->master->lock); - dev->driver->reclaim_buffers_idlelocked(dev, file_priv); - drm_idlelock_release(&file_priv->master->lock); - - } - - if (drm_i_have_hw_lock(dev, file_priv)) { - DRM_DEBUG("File %p released, freeing lock for context %d\n", - filp, _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock)); - - drm_lock_free(&file_priv->master->lock, - _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock)); - } + if (dev->driver->reclaim_buffers_idlelocked && file_priv->master->lock.hw_lock) { + + drm_idlelock_take(&file_priv->master->lock); + dev->driver->reclaim_buffers_idlelocked(dev, file_priv); + drm_idlelock_release(&file_priv->master->lock); + + } - if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) && - !dev->driver->reclaim_buffers_locked) { - dev->driver->reclaim_buffers(dev, file_priv); + if (drm_i_have_hw_lock(dev, file_priv)) { + DRM_DEBUG("File %p released, freeing lock for context %d\n", + filp, _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock)); + + drm_lock_free(&file_priv->master->lock, + _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock)); + } + + + if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) && + !dev->driver->reclaim_buffers_locked) { + dev->driver->reclaim_buffers(dev, file_priv); + } } drm_fasync(-1, filp, 0); From 8a18d123f55a7fb11ce333f0b1095020918b8616 Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Thu, 28 Feb 2008 09:08:52 +0100 Subject: [PATCH 12/26] Avoid large kmallocs. --- shared-core/i915_dma.c | 28 +++++++++++++++++----------- shared-core/i915_drv.h | 2 ++ 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/shared-core/i915_dma.c b/shared-core/i915_dma.c index 7d247f9c..f6df74a7 100644 --- a/shared-core/i915_dma.c +++ b/shared-core/i915_dma.c @@ -1181,7 +1181,6 @@ static int i915_handle_copyback(struct drm_device *dev, buffers++; } } - return err; } @@ -1272,7 +1271,6 @@ static int i915_execbuffer(struct drm_device *dev, void *data, struct drm_fence_arg *fence_arg = &exec_buf->fence_arg; int num_buffers; int ret; - struct drm_i915_validate_buffer *buffers; if (!dev_priv->allow_batchbuffer) { DRM_ERROR("Batchbuffer ioctl disabled\n"); @@ -1288,7 +1286,6 @@ static int i915_execbuffer(struct drm_device *dev, void *data, if (exec_buf->num_buffers > dev_priv->max_validate_buffers) return -EINVAL; - ret = drm_bo_read_lock(&dev->bm.bm_lock); if (ret) return ret; @@ -1306,8 +1303,12 @@ static int i915_execbuffer(struct drm_device *dev, void *data, num_buffers = exec_buf->num_buffers; - buffers = drm_calloc(num_buffers, sizeof(struct drm_i915_validate_buffer), DRM_MEM_DRIVER); - if (!buffers) { + if (!dev_priv->val_bufs) { + dev_priv->val_bufs = + vmalloc(sizeof(struct drm_i915_validate_buffer)* + dev_priv->max_validate_buffers); + } + if (!dev_priv->val_bufs) { drm_bo_read_unlock(&dev->bm.bm_lock); mutex_unlock(&dev_priv->cmdbuf_mutex); return -ENOMEM; @@ -1315,7 +1316,7 @@ static int i915_execbuffer(struct drm_device *dev, void *data, /* validate buffer list + fixup relocations */ ret = i915_validate_buffer_list(file_priv, 0, exec_buf->ops_list, - buffers, &num_buffers); + dev_priv->val_bufs, &num_buffers); if (ret) goto out_err0; @@ -1324,7 +1325,7 @@ static int i915_execbuffer(struct drm_device *dev, void *data, drm_agp_chipset_flush(dev); /* submit buffer */ - batch->start = buffers[num_buffers-1].buffer->offset; + batch->start = dev_priv->val_bufs[num_buffers-1].buffer->offset; DRM_DEBUG("i915 exec batchbuffer, start %x used %d cliprects %d\n", batch->start, batch->used, batch->num_cliprects); @@ -1341,13 +1342,11 @@ static int i915_execbuffer(struct drm_device *dev, void *data, out_err0: /* handle errors */ - ret = i915_handle_copyback(dev, buffers, num_buffers, ret); + ret = i915_handle_copyback(dev, dev_priv->val_bufs, num_buffers, ret); mutex_lock(&dev->struct_mutex); - i915_dereference_buffers_locked(buffers, num_buffers); + i915_dereference_buffers_locked(dev_priv->val_bufs, num_buffers); mutex_unlock(&dev->struct_mutex); - drm_free(buffers, (exec_buf->num_buffers * sizeof(struct drm_buffer_object *)), DRM_MEM_DRIVER); - mutex_unlock(&dev_priv->cmdbuf_mutex); drm_bo_read_unlock(&dev->bm.bm_lock); return ret; @@ -1618,6 +1617,13 @@ void i915_driver_lastclose(struct drm_device * dev) { drm_i915_private_t *dev_priv = dev->dev_private; +#ifdef I915_HAVE_BUFFER + if (dev_priv->val_bufs) { + vfree(dev_priv->val_bufs); + dev_priv->val_bufs = NULL; + } +#endif + if (drm_getsarea(dev) && dev_priv->sarea_priv) i915_do_cleanup_pageflip(dev); if (dev_priv->agp_heap) diff --git a/shared-core/i915_drv.h b/shared-core/i915_drv.h index 570fc4d3..3bc39e2a 100644 --- a/shared-core/i915_drv.h +++ b/shared-core/i915_drv.h @@ -69,6 +69,7 @@ #ifdef I915_HAVE_BUFFER #define I915_MAX_VALIDATE_BUFFERS 4096 +struct drm_i915_validate_buffer; #endif typedef struct _drm_i915_ring_buffer { @@ -141,6 +142,7 @@ typedef struct drm_i915_private { void *agp_iomap; unsigned int max_validate_buffers; struct mutex cmdbuf_mutex; + struct drm_i915_validate_buffer *val_bufs; #endif DRM_SPINTYPE swaps_lock; From fa1d88e3b2de843f33c9d77c9d95db762a950a14 Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Wed, 12 Mar 2008 09:56:06 +0100 Subject: [PATCH 13/26] Make sure other TTM memory types than TT is really unbound when evicted. --- linux-core/drm_bo_move.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/linux-core/drm_bo_move.c b/linux-core/drm_bo_move.c index 30e0f43f..21673daa 100644 --- a/linux-core/drm_bo_move.c +++ b/linux-core/drm_bo_move.c @@ -42,7 +42,6 @@ static void drm_bo_free_old_node(struct drm_buffer_object *bo) if (old_mem->mm_node && (old_mem->mm_node != bo->pinned_node)) { mutex_lock(&bo->dev->struct_mutex); drm_mm_put_block(old_mem->mm_node); - old_mem->mm_node = NULL; mutex_unlock(&bo->dev->struct_mutex); } old_mem->mm_node = NULL; @@ -57,7 +56,7 @@ int drm_bo_move_ttm(struct drm_buffer_object *bo, uint64_t save_proposed_flags = old_mem->proposed_flags; int ret; - if (old_mem->mem_type == DRM_BO_MEM_TT) { + if (old_mem->mem_type != DRM_BO_MEM_LOCAL) { if (evict) drm_ttm_evict(ttm); else From f1a681ebe5573c2ec7806ba4cb754314baef6935 Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Wed, 12 Mar 2008 10:02:09 +0100 Subject: [PATCH 14/26] Avoid duplicate calls to drm_ttm_bind in some cases. --- linux-core/drm_bo.c | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/linux-core/drm_bo.c b/linux-core/drm_bo.c index fd3cf9db..51cd1756 100644 --- a/linux-core/drm_bo.c +++ b/linux-core/drm_bo.c @@ -208,36 +208,35 @@ static int drm_bo_handle_move_mem(struct drm_buffer_object *bo, if (ret) goto out_err; } + + if (bo->mem.mem_type == DRM_BO_MEM_LOCAL) { + + struct drm_bo_mem_reg *old_mem = &bo->mem; + uint64_t save_flags = old_mem->flags; + uint64_t save_proposed_flags = old_mem->proposed_flags; + + *old_mem = *mem; + mem->mm_node = NULL; + old_mem->proposed_flags = save_proposed_flags; + DRM_FLAG_MASKED(save_flags, mem->flags, + DRM_BO_MASK_MEMTYPE); + goto moved; + } + } - if ((bo->mem.mem_type == DRM_BO_MEM_LOCAL) && bo->ttm == NULL) { - - struct drm_bo_mem_reg *old_mem = &bo->mem; - uint64_t save_flags = old_mem->flags; - uint64_t save_proposed_flags = old_mem->proposed_flags; - - *old_mem = *mem; - mem->mm_node = NULL; - old_mem->proposed_flags = save_proposed_flags; - DRM_FLAG_MASKED(save_flags, mem->flags, DRM_BO_MASK_MEMTYPE); - - } else if (!(old_man->flags & _DRM_FLAG_MEMTYPE_FIXED) && - !(new_man->flags & _DRM_FLAG_MEMTYPE_FIXED)) { - + if (!(old_man->flags & _DRM_FLAG_MEMTYPE_FIXED) && + !(new_man->flags & _DRM_FLAG_MEMTYPE_FIXED)) ret = drm_bo_move_ttm(bo, evict, no_wait, mem); - - } else if (dev->driver->bo_driver->move) { + else if (dev->driver->bo_driver->move) ret = dev->driver->bo_driver->move(bo, evict, no_wait, mem); - - } else { - + else ret = drm_bo_move_memcpy(bo, evict, no_wait, mem); - } - if (ret) goto out_err; +moved: if (old_is_pci || new_is_pci) drm_bo_vm_post_move(bo); From 7bcce66a1d5c93ff9b9f20d45d5b2c33c8ca8da9 Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Wed, 12 Mar 2008 10:07:56 +0100 Subject: [PATCH 15/26] Fix kernel crash when we hit OOM conditions. (Alan Hourihane) --- linux-core/drm_bo.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/linux-core/drm_bo.c b/linux-core/drm_bo.c index 51cd1756..2e210040 100644 --- a/linux-core/drm_bo.c +++ b/linux-core/drm_bo.c @@ -789,6 +789,11 @@ static int drm_bo_mem_force_space(struct drm_device *dev, } node = drm_mm_get_block(node, num_pages, mem->page_alignment); + if (unlikely(!node)) { + mutex_unlock(&dev->struct_mutex); + return -ENOMEM; + } + mutex_unlock(&dev->struct_mutex); mem->mm_node = node; mem->mem_type = mem_type; @@ -1482,6 +1487,9 @@ static int drm_buffer_object_validate(struct drm_buffer_object *bo, if (ret) { if (ret != -EAGAIN) DRM_ERROR("Failed moving buffer.\n"); + if (ret == -ENOMEM) + DRM_ERROR("Out of aperture space or " + "DRM memory quota.\n"); return ret; } } @@ -2747,7 +2755,7 @@ static int drm_bo_setup_vm_locked(struct drm_buffer_object *bo) list->file_offset_node = drm_mm_search_free(&dev->offset_manager, bo->mem.num_pages, 0, 0); - if (!list->file_offset_node) { + if (unlikely(!list->file_offset_node)) { drm_bo_takedown_vm_locked(bo); return -ENOMEM; } @@ -2755,6 +2763,11 @@ static int drm_bo_setup_vm_locked(struct drm_buffer_object *bo) list->file_offset_node = drm_mm_get_block(list->file_offset_node, bo->mem.num_pages, 0); + if (unlikely(!list->file_offset_node)) { + drm_bo_takedown_vm_locked(bo); + return -ENOMEM; + } + list->hash.key = list->file_offset_node->start; if (drm_ht_insert_item(&dev->map_hash, &list->hash)) { drm_bo_takedown_vm_locked(bo); From c0a1cd052ac44d1b342fa3f26afe1bd21d92b194 Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Thu, 17 Jan 2008 13:10:36 +0100 Subject: [PATCH 16/26] Add an emergency pinnable memory quota for root-only processes. --- linux-core/drmP.h | 4 +++- linux-core/drm_memory.c | 44 +++++++++++++++++++++++++++++++++++------ linux-core/drm_proc.c | 14 +++++++++++-- 3 files changed, 53 insertions(+), 9 deletions(-) diff --git a/linux-core/drmP.h b/linux-core/drmP.h index 1fea807b..2f76f3df 100644 --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -1001,8 +1001,10 @@ extern int drm_unbind_agp(DRM_AGP_MEM * handle); extern void drm_free_memctl(size_t size); extern int drm_alloc_memctl(size_t size); extern void drm_query_memctl(uint64_t *cur_used, + uint64_t *emer_used, uint64_t *low_threshold, - uint64_t *high_threshold); + uint64_t *high_threshold, + uint64_t *emer_threshold); extern void drm_init_memctl(size_t low_threshold, size_t high_threshold, size_t unit_size); diff --git a/linux-core/drm_memory.c b/linux-core/drm_memory.c index 402a680f..1a6c48df 100644 --- a/linux-core/drm_memory.c +++ b/linux-core/drm_memory.c @@ -39,8 +39,10 @@ static struct { spinlock_t lock; uint64_t cur_used; + uint64_t emer_used; uint64_t low_threshold; uint64_t high_threshold; + uint64_t emer_threshold; } drm_memctl = { .lock = SPIN_LOCK_UNLOCKED }; @@ -59,14 +61,30 @@ static inline size_t drm_size_align(size_t size) int drm_alloc_memctl(size_t size) { - int ret; + int ret = 0; unsigned long a_size = drm_size_align(size); + unsigned long new_used = drm_memctl.cur_used + a_size; spin_lock(&drm_memctl.lock); - ret = ((drm_memctl.cur_used + a_size) > drm_memctl.high_threshold) ? - -ENOMEM : 0; - if (!ret) - drm_memctl.cur_used += a_size; + if (unlikely(new_used > drm_memctl.high_threshold)) { + if (!DRM_SUSER(DRM_CURPROC) || + (new_used + drm_memctl.emer_used > drm_memctl.emer_threshold) || + (a_size > 2*PAGE_SIZE)) { + ret = -ENOMEM; + goto out; + } + + /* + * Allow small root-only allocations, even if the + * high threshold is exceeded. + */ + + new_used -= drm_memctl.high_threshold; + drm_memctl.emer_used += new_used; + a_size -= new_used; + } + drm_memctl.cur_used += a_size; +out: spin_unlock(&drm_memctl.lock); return ret; } @@ -77,19 +95,30 @@ void drm_free_memctl(size_t size) unsigned long a_size = drm_size_align(size); spin_lock(&drm_memctl.lock); + if (likely(a_size >= drm_memctl.emer_used)) { + a_size -= drm_memctl.emer_used; + drm_memctl.emer_used = 0; + } else { + drm_memctl.emer_used -= a_size; + a_size = 0; + } drm_memctl.cur_used -= a_size; spin_unlock(&drm_memctl.lock); } EXPORT_SYMBOL(drm_free_memctl); void drm_query_memctl(uint64_t *cur_used, + uint64_t *emer_used, uint64_t *low_threshold, - uint64_t *high_threshold) + uint64_t *high_threshold, + uint64_t *emer_threshold) { spin_lock(&drm_memctl.lock); *cur_used = drm_memctl.cur_used; + *emer_used = drm_memctl.emer_used; *low_threshold = drm_memctl.low_threshold; *high_threshold = drm_memctl.high_threshold; + *emer_threshold = drm_memctl.emer_threshold; spin_unlock(&drm_memctl.lock); } EXPORT_SYMBOL(drm_query_memctl); @@ -99,9 +128,12 @@ void drm_init_memctl(size_t p_low_threshold, size_t unit_size) { spin_lock(&drm_memctl.lock); + drm_memctl.emer_used = 0; drm_memctl.cur_used = 0; drm_memctl.low_threshold = p_low_threshold * unit_size; drm_memctl.high_threshold = p_high_threshold * unit_size; + drm_memctl.emer_threshold = (drm_memctl.high_threshold >> 4) + + drm_memctl.high_threshold; spin_unlock(&drm_memctl.lock); } diff --git a/linux-core/drm_proc.c b/linux-core/drm_proc.c index 67afee8e..42da5c69 100644 --- a/linux-core/drm_proc.c +++ b/linux-core/drm_proc.c @@ -445,9 +445,10 @@ static int drm__objects_info(char *buf, char **start, off_t offset, int request, struct drm_buffer_manager *bm = &dev->bm; struct drm_fence_manager *fm = &dev->fm; uint64_t used_mem; + uint64_t used_emer; uint64_t low_mem; uint64_t high_mem; - + uint64_t emer_mem; if (offset > DRM_PROC_LIMIT) { *eof = 1; @@ -476,7 +477,7 @@ static int drm__objects_info(char *buf, char **start, off_t offset, int request, DRM_PROC_PRINT("Buffer objects are not supported by this driver.\n"); } - drm_query_memctl(&used_mem, &low_mem, &high_mem); + drm_query_memctl(&used_mem, &used_emer, &low_mem, &high_mem, &emer_mem); if (used_mem > 16*PAGE_SIZE) { DRM_PROC_PRINT("Used object memory is %lu pages.\n", @@ -485,10 +486,19 @@ static int drm__objects_info(char *buf, char **start, off_t offset, int request, DRM_PROC_PRINT("Used object memory is %lu bytes.\n", (unsigned long) used_mem); } + if (used_emer > 16*PAGE_SIZE) { + DRM_PROC_PRINT("Used emergency memory is %lu pages.\n", + (unsigned long) (used_emer >> PAGE_SHIFT)); + } else { + DRM_PROC_PRINT("Used emergency memory is %lu bytes.\n\n", + (unsigned long) used_emer); + } DRM_PROC_PRINT("Soft object memory usage threshold is %lu pages.\n", (unsigned long) (low_mem >> PAGE_SHIFT)); DRM_PROC_PRINT("Hard object memory usage threshold is %lu pages.\n", (unsigned long) (high_mem >> PAGE_SHIFT)); + DRM_PROC_PRINT("Emergency root only memory usage threshold is %lu pages.\n", + (unsigned long) (emer_mem >> PAGE_SHIFT)); DRM_PROC_PRINT("\n"); From 32625774072f905d15024cc40ce7fd364d9ee4cd Mon Sep 17 00:00:00 2001 From: Alan Hourihane Date: Thu, 17 Jan 2008 13:04:23 +0000 Subject: [PATCH 17/26] Add error message --- linux-core/drm_fence.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/linux-core/drm_fence.c b/linux-core/drm_fence.c index 9d80327f..0ca0c408 100644 --- a/linux-core/drm_fence.c +++ b/linux-core/drm_fence.c @@ -520,8 +520,10 @@ int drm_fence_object_create(struct drm_device *dev, uint32_t fence_class, struct drm_fence_manager *fm = &dev->fm; fence = drm_ctl_calloc(1, sizeof(*fence), DRM_MEM_FENCE); - if (!fence) + if (!fence) { + DRM_ERROR("Out of memory creating fence object\n"); return -ENOMEM; + } ret = drm_fence_object_init(dev, fence_class, type, flags, fence); if (ret) { drm_fence_usage_deref_unlocked(&fence); From 88be276a427124cc545a7d89b137e4ae6dd79acb Mon Sep 17 00:00:00 2001 From: Alan Hourihane Date: Thu, 17 Jan 2008 13:04:42 +0000 Subject: [PATCH 18/26] Fix for debug memory routines --- linux-core/drm_memory.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/linux-core/drm_memory.c b/linux-core/drm_memory.c index 1a6c48df..12e01414 100644 --- a/linux-core/drm_memory.c +++ b/linux-core/drm_memory.c @@ -326,7 +326,12 @@ static void *agp_remap(unsigned long offset, unsigned long size, return NULL; } #endif /* agp */ - +#else +static void *agp_remap(unsigned long offset, unsigned long size, + struct drm_device * dev) +{ + return NULL; +} #endif /* debug_memory */ void drm_core_ioremap(struct drm_map *map, struct drm_device *dev) From 5bebcd7a0b548b29a9859b2949b06662968cc868 Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Wed, 12 Mar 2008 10:19:36 +0100 Subject: [PATCH 19/26] Dont allow !sysadmin clients to alter the memory type of NO_EVICT buffers. --- linux-core/drm_bo.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/linux-core/drm_bo.c b/linux-core/drm_bo.c index 2e210040..a94bd8a1 100644 --- a/linux-core/drm_bo.c +++ b/linux-core/drm_bo.c @@ -979,6 +979,20 @@ static int drm_bo_modify_proposed_flags (struct drm_buffer_object *bo, return -EPERM; } + if (likely(new_mask & DRM_BO_MASK_MEM) && + (bo->mem.flags & DRM_BO_FLAG_NO_EVICT) && + !DRM_SUSER(DRM_CURPROC)) { + if (likely(bo->mem.flags & new_flags & new_mask & + DRM_BO_MASK_MEM)) + new_flags = (new_flags & ~DRM_BO_MASK_MEM) | + (bo->mem.flags & DRM_BO_MASK_MEM); + else { + DRM_ERROR("Incompatible memory type specification " + "for NO_EVICT buffer.\n"); + return -EPERM; + } + } + if ((new_flags & DRM_BO_FLAG_NO_MOVE)) { DRM_ERROR("DRM_BO_FLAG_NO_MOVE is not properly implemented yet.\n"); return -EPERM; From 2a618e5a7f6d26fe85e7d931d0ef08d9f18b1b7c Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Wed, 12 Mar 2008 10:36:40 +0100 Subject: [PATCH 20/26] Bug # 14712 Disable page saving for GPU read-only TTMs. --- linux-core/drm_ttm.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/linux-core/drm_ttm.c b/linux-core/drm_ttm.c index cc80b132..e991254f 100644 --- a/linux-core/drm_ttm.c +++ b/linux-core/drm_ttm.c @@ -299,13 +299,13 @@ int drm_ttm_populate(struct drm_ttm *ttm) return 0; be = ttm->be; - if (ttm->page_flags & DRM_TTM_PAGE_WRITE) { - for (i = 0; i < ttm->num_pages; ++i) { - page = drm_ttm_get_page(ttm, i); - if (!page) - return -ENOMEM; - } + + for (i = 0; i < ttm->num_pages; ++i) { + page = drm_ttm_get_page(ttm, i); + if (!page) + return -ENOMEM; } + be->func->populate(be, ttm->num_pages, ttm->pages, ttm->dummy_read_page); ttm->state = ttm_unbound; return 0; From 7f04dd06e6003dd492ae5ddc876121a686f49157 Mon Sep 17 00:00:00 2001 From: Alan Hourihane Date: Wed, 12 Mar 2008 09:47:52 +0000 Subject: [PATCH 21/26] Add sample code to test hotplug events --- tests/modedemo/demo.c | 47 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/tests/modedemo/demo.c b/tests/modedemo/demo.c index 83a33aa6..3fad984d 100644 --- a/tests/modedemo/demo.c +++ b/tests/modedemo/demo.c @@ -22,6 +22,7 @@ #include #include #endif +#include #include "xf86drm.h" #include "xf86drmMode.h" @@ -387,9 +388,28 @@ drmModeCrtcPtr demoFindFreeCrtc(struct demo_driver *driver, drmModeOutputPtr out return crtc; } +static int driverfd; + +static void +hotplugSIGNAL(int sig, siginfo_t *si, void *d) +{ + union drm_wait_hotplug hw; + int ret; + + printf("GOT HOTPLUG EVENT!\n"); + + /* ask for another hotplug event ! */ + memset(&hw, 0, sizeof(hw)); + hw.request.type = _DRM_HOTPLUG_SIGNAL; + hw.request.signal = SIGUSR1; + ret = ioctl(driverfd, DRM_IOCTL_WAIT_HOTPLUG, &hw); +} + struct demo_driver* demoCreateDriver(void) { struct demo_driver* driver = malloc(sizeof(struct demo_driver)); + union drm_wait_hotplug hw; + int ret = 0; memset(driver, 0, sizeof(struct demo_driver)); @@ -400,6 +420,33 @@ struct demo_driver* demoCreateDriver(void) goto err_driver; } +#if 0 + /* ioctl wait for hotplug */ + do { + memset(&hw, 0, sizeof(hw)); + ret = ioctl(driver->fd, DRM_IOCTL_WAIT_HOTPLUG, &hw); + printf("HOTPLUG %d %d %d\n",ret,errno,hw.reply.counter); + } while (ret && errno == EBUSY); +#else + /* signal for hotplug */ + { + struct sigaction sa; + struct sigaction osa; + + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = hotplugSIGNAL; + sigaction(SIGUSR1, &sa, &osa); + + driverfd = driver->fd; + + memset(&hw, 0, sizeof(hw)); + hw.request.type = _DRM_HOTPLUG_SIGNAL; + hw.request.signal = SIGUSR1; + ret = ioctl(driver->fd, DRM_IOCTL_WAIT_HOTPLUG, &hw); + } +#endif + demoUpdateRes(driver); if (!driver->res) { printf("could not retrive resources\n"); From 631c6af4d926fd1fe73f017cfb032538cee7ea7d Mon Sep 17 00:00:00 2001 From: Alan Hourihane Date: Thu, 13 Mar 2008 11:47:37 +0000 Subject: [PATCH 22/26] Fix green offset --- linux-core/intel_fb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux-core/intel_fb.c b/linux-core/intel_fb.c index 52ff3a67..7c4b0632 100644 --- a/linux-core/intel_fb.c +++ b/linux-core/intel_fb.c @@ -157,7 +157,7 @@ static int intelfb_check_var(struct fb_var_screeninfo *var, break; case 16: var->red.offset = 11; - var->green.offset = 6; + var->green.offset = 5; var->blue.offset = 0; var->red.length = 5; var->green.length = 6; From d1513528d7e1a7bd119468087baa6839897627f4 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Mon, 17 Mar 2008 16:32:27 +1000 Subject: [PATCH 23/26] drm: select the correct master to attempt to remove the lock from. When destroying DRI sarea, make sure you use the master associated with the sarea and not the one currently in charge --- linux-core/drm_bufs.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/linux-core/drm_bufs.c b/linux-core/drm_bufs.c index 031f8ba0..f6ff75ab 100644 --- a/linux-core/drm_bufs.c +++ b/linux-core/drm_bufs.c @@ -380,10 +380,12 @@ int drm_rmmap_locked(struct drm_device *dev, drm_local_map_t *map) struct drm_map_list *r_list = NULL, *list_t; drm_dma_handle_t dmah; int found = 0; + struct drm_master *master; /* Find the list entry for the map and remove it */ list_for_each_entry_safe(r_list, list_t, &dev->maplist, head) { if (r_list->map == map) { + master = r_list->master; list_del(&r_list->head); drm_ht_remove_key(&dev->map_hash, r_list->user_token >> PAGE_SHIFT); @@ -413,9 +415,13 @@ int drm_rmmap_locked(struct drm_device *dev, drm_local_map_t *map) break; case _DRM_SHM: vfree(map->handle); - dev->sigdata.lock = dev->primary->master->lock.hw_lock = NULL; /* SHM removed */ - dev->primary->master->lock.file_priv = NULL; - wake_up_interruptible(&dev->primary->master->lock.lock_queue); + if (master) { + if (dev->sigdata.lock == master->lock.hw_lock) + dev->sigdata.lock = NULL; + master->lock.hw_lock = NULL; /* SHM removed */ + master->lock.file_priv = NULL; + wake_up_interruptible(&master->lock.lock_queue); + } break; case _DRM_AGP: case _DRM_SCATTER_GATHER: From e6be93b2a6f508b2284a6d352f5f0640ef1a542a Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Mon, 17 Mar 2008 16:33:15 +1000 Subject: [PATCH 24/26] drm: pick correct master for cleaning up When a master is exiting, make sure we clean it up and not the currently in charge master. --- linux-core/drm_fops.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/linux-core/drm_fops.c b/linux-core/drm_fops.c index 0ca43824..d5c59b19 100644 --- a/linux-core/drm_fops.c +++ b/linux-core/drm_fops.c @@ -488,13 +488,15 @@ int drm_release(struct inode *inode, struct file *filp) if (drm_core_check_feature(dev, DRIVER_MODESET)) drm_fb_release(filp); - file_priv->master = NULL; - if (file_priv->is_master) { - drm_put_master(file_priv->minor->master); - file_priv->minor->master = NULL; + if (file_priv->minor->master == file_priv->master) + file_priv->minor->master = NULL; + drm_put_master(file_priv->master); } + file_priv->master = NULL; + file_priv->is_master = 0; + mutex_lock(&dev->struct_mutex); drm_object_release(filp); if (file_priv->remove_auth_on_close == 1) { From 2d0411cb7544ea45b5879d4f454cb9ee3c9ff5fb Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Mon, 17 Mar 2008 16:34:15 +1000 Subject: [PATCH 25/26] i915: safety check the sarea map still exists --- shared-core/i915_init.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/shared-core/i915_init.c b/shared-core/i915_init.c index fa2fc3da..a76acb4e 100644 --- a/shared-core/i915_init.c +++ b/shared-core/i915_init.c @@ -383,7 +383,9 @@ void i915_master_destroy(struct drm_device *dev, struct drm_master *master) if (!master_priv) return; - drm_rmmap(dev, master_priv->sarea); + if (master_priv->sarea) + drm_rmmap(dev, master_priv->sarea); + drm_free(master_priv, sizeof(*master_priv), DRM_MEM_DRIVER); master->driver_priv = NULL; From 607964ed9e5f6d86a0960bef2341e7f5de9c71da Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Mon, 17 Mar 2008 16:37:46 +1000 Subject: [PATCH 26/26] drm: add master set/drop protocol this may not survive long - just need something for testing --- libdrm/xf86drm.c | 17 +++++++++++++++++ libdrm/xf86drm.h | 3 +++ linux-core/drmP.h | 4 ++++ linux-core/drm_drv.c | 3 +++ linux-core/drm_stub.c | 23 +++++++++++++++++++++++ shared-core/drm.h | 3 +++ 6 files changed, 53 insertions(+) diff --git a/libdrm/xf86drm.c b/libdrm/xf86drm.c index bcf562d5..53275f52 100644 --- a/libdrm/xf86drm.c +++ b/libdrm/xf86drm.c @@ -2977,3 +2977,20 @@ void drmCloseOnce(int fd) } } } + +int drmSetMaster(int fd) +{ + int ret; + + fprintf(stderr,"Setting master \n"); + ret = ioctl(fd, DRM_IOCTL_SET_MASTER, 0); + return ret; +} + +int drmDropMaster(int fd) +{ + int ret; + fprintf(stderr,"Dropping master \n"); + ret = ioctl(fd, DRM_IOCTL_DROP_MASTER, 0); + return ret; +} diff --git a/libdrm/xf86drm.h b/libdrm/xf86drm.h index 7b418604..35780aca 100644 --- a/libdrm/xf86drm.h +++ b/libdrm/xf86drm.h @@ -660,6 +660,9 @@ extern int drmSLLookupNeighbors(void *l, unsigned long key, extern int drmOpenOnce(void *unused, const char *BusID, int *newlyopened); extern void drmCloseOnce(int fd); +extern int drmSetMaster(int fd); +extern int drmDropMaster(int fd); + #include "xf86mm.h" #endif diff --git a/linux-core/drmP.h b/linux-core/drmP.h index 8273c879..c2c3cdde 100644 --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -1266,6 +1266,10 @@ extern int drm_agp_unbind_memory(DRM_AGP_MEM * handle); extern struct drm_ttm_backend *drm_agp_init_ttm(struct drm_device *dev); extern void drm_agp_chipset_flush(struct drm_device *dev); /* Stub support (drm_stub.h) */ +extern int drm_setmaster_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int drm_dropmaster_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); extern struct drm_master *drm_get_master(struct drm_minor *minor); extern void drm_put_master(struct drm_master *master); extern int drm_get_dev(struct pci_dev *pdev, const struct pci_device_id *ent, diff --git a/linux-core/drm_drv.c b/linux-core/drm_drv.c index 09372c71..649d4cae 100644 --- a/linux-core/drm_drv.c +++ b/linux-core/drm_drv.c @@ -76,6 +76,9 @@ static struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_SET_SAREA_CTX, drm_setsareactx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_GET_SAREA_CTX, drm_getsareactx, DRM_AUTH), + DRM_IOCTL_DEF(DRM_IOCTL_SET_MASTER, drm_setmaster_ioctl, DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_DROP_MASTER, drm_dropmaster_ioctl, DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_ADD_CTX, drm_addctx, DRM_AUTH|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_RM_CTX, drm_rmctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_MOD_CTX, drm_modctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), diff --git a/linux-core/drm_stub.c b/linux-core/drm_stub.c index 6856075b..f66dec07 100644 --- a/linux-core/drm_stub.c +++ b/linux-core/drm_stub.c @@ -88,6 +88,29 @@ again: return new_id; } +int drm_setmaster_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + if (file_priv->minor->master && file_priv->minor->master != file_priv->master) + return -EINVAL; + + if (!file_priv->master) + return -EINVAL; + + if (!file_priv->minor->master && file_priv->minor->master != file_priv->master) + file_priv->minor->master = file_priv->master; + return 0; +} + +int drm_dropmaster_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + if (!file_priv->master) + return -EINVAL; + file_priv->minor->master = NULL; + return 0; +} + struct drm_master *drm_get_master(struct drm_minor *minor) { struct drm_master *master; diff --git a/shared-core/drm.h b/shared-core/drm.h index a1ebfb93..a72263ae 100644 --- a/shared-core/drm.h +++ b/shared-core/drm.h @@ -1200,6 +1200,9 @@ struct drm_mode_hotplug { #define DRM_IOCTL_SET_SAREA_CTX DRM_IOW( 0x1c, struct drm_ctx_priv_map) #define DRM_IOCTL_GET_SAREA_CTX DRM_IOWR(0x1d, struct drm_ctx_priv_map) +#define DRM_IOCTL_SET_MASTER DRM_IO(0x1e) +#define DRM_IOCTL_DROP_MASTER DRM_IO(0x1f) + #define DRM_IOCTL_ADD_CTX DRM_IOWR(0x20, struct drm_ctx) #define DRM_IOCTL_RM_CTX DRM_IOWR(0x21, struct drm_ctx) #define DRM_IOCTL_MOD_CTX DRM_IOW( 0x22, struct drm_ctx)