1 From: Eric Anholt <eric@anholt.net>
2 Date: Tue, 29 Jul 2008 19:10:39 +0000 (-0700)
3 Subject: i915: Add support for MSI and interrupt mitigation.
5 X-Git-Url: http://gitweb.freedesktop.org/?p=users/anholt/anholt/linux-2.6.git;a=commitdiff;h=aae4223e2fd3b29ae8e070b7a16d8cfc70c6a0c0
7 i915: Add support for MSI and interrupt mitigation.
9 Previous attempts at interrupt mitigation had been foiled by i915_wait_irq's
10 failure to update the sarea seqno value when the status page indicated that
11 the seqno had already been passed. MSI support has been seen to cut CPU
12 costs by up to 40% in some workloads by avoiding other expensive interrupt
13 handlers for frequent graphics interrupts.
15 Signed-off-by: Eric Anholt <eric@anholt.net>
18 --- a/drivers/gpu/drm/drm_irq.c
19 +++ b/drivers/gpu/drm/drm_irq.c
20 @@ -63,7 +63,7 @@ int drm_irq_by_busid(struct drm_device *
21 p->devnum != PCI_SLOT(dev->pdev->devfn) || p->funcnum != PCI_FUNC(dev->pdev->devfn))
25 + p->irq = dev->pdev->irq;
27 DRM_DEBUG("%d:%d:%d => IRQ %d\n", p->busnum, p->devnum, p->funcnum,
29 @@ -89,7 +89,7 @@ static int drm_irq_install(struct drm_de
30 if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
34 + if (dev->pdev->irq == 0)
37 mutex_lock(&dev->struct_mutex);
38 @@ -107,7 +107,7 @@ static int drm_irq_install(struct drm_de
40 mutex_unlock(&dev->struct_mutex);
42 - DRM_DEBUG("irq=%d\n", dev->irq);
43 + DRM_DEBUG("irq=%d\n", dev->pdev->irq);
45 if (drm_core_check_feature(dev, DRIVER_IRQ_VBL)) {
46 init_waitqueue_head(&dev->vbl_queue);
47 @@ -127,8 +127,12 @@ static int drm_irq_install(struct drm_de
48 if (drm_core_check_feature(dev, DRIVER_IRQ_SHARED))
49 sh_flags = IRQF_SHARED;
51 - ret = request_irq(dev->irq, dev->driver->irq_handler,
52 + ret = request_irq(dev->pdev->irq, dev->driver->irq_handler,
53 sh_flags, dev->devname, dev);
54 + /* Expose the device irq number to drivers that want to export it for
57 + dev->irq = dev->pdev->irq;
59 mutex_lock(&dev->struct_mutex);
61 @@ -164,11 +168,11 @@ int drm_irq_uninstall(struct drm_device
65 - DRM_DEBUG("irq=%d\n", dev->irq);
66 + DRM_DEBUG("irq=%d\n", dev->pdev->irq);
68 dev->driver->irq_uninstall(dev);
70 - free_irq(dev->irq, dev);
71 + free_irq(dev->pdev->irq, dev);
73 dev->locked_tasklet_func = NULL;
75 @@ -201,7 +205,7 @@ int drm_control(struct drm_device *dev,
76 if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
78 if (dev->if_version < DRM_IF_VERSION(1, 2) &&
79 - ctl->irq != dev->irq)
80 + ctl->irq != dev->pdev->irq)
82 return drm_irq_install(dev);
83 case DRM_UNINST_HANDLER:
84 @@ -239,7 +243,7 @@ int drm_wait_vblank(struct drm_device *d
86 unsigned int flags, seq;
88 - if ((!dev->irq) || (!dev->irq_enabled))
89 + if ((!dev->pdev->irq) || (!dev->irq_enabled))
92 if (vblwait->request.type &
93 --- a/drivers/gpu/drm/i915/i915_dma.c
94 +++ b/drivers/gpu/drm/i915/i915_dma.c
95 @@ -84,7 +84,7 @@ static int i915_dma_cleanup(struct drm_d
96 * may not have been called from userspace and after dev_private
97 * is freed, it's too late.
100 + if (dev->irq_enabled)
101 drm_irq_uninstall(dev);
103 if (dev_priv->ring.virtual_start) {
104 @@ -644,7 +644,7 @@ static int i915_getparam(struct drm_devi
106 switch (param->param) {
107 case I915_PARAM_IRQ_ACTIVE:
108 - value = dev->irq ? 1 : 0;
109 + value = dev->irq_enabled;
111 case I915_PARAM_ALLOW_BATCHBUFFER:
112 value = dev_priv->allow_batchbuffer ? 1 : 0;
113 @@ -763,6 +763,20 @@ int i915_driver_load(struct drm_device *
114 ret = drm_addmap(dev, base, size, _DRM_REGISTERS,
115 _DRM_KERNEL | _DRM_DRIVER,
116 &dev_priv->mmio_map);
119 + /* On the 945G/GM, the chipset reports the MSI capability on the
120 + * integrated graphics even though the support isn't actually there
121 + * according to the published specs. It doesn't appear to function
122 + * correctly in testing on 945G.
123 + * This may be a side effect of MSI having been made available for PEG
124 + * and the registers being closely associated.
126 + if (!IS_I945G(dev) && !IS_I945GM(dev))
127 + pci_enable_msi(dev->pdev);
129 + spin_lock_init(&dev_priv->user_irq_lock);
134 @@ -770,6 +784,9 @@ int i915_driver_unload(struct drm_device
136 struct drm_i915_private *dev_priv = dev->dev_private;
138 + if (dev->pdev->msi_enabled)
139 + pci_disable_msi(dev->pdev);
141 if (dev_priv->mmio_map)
142 drm_rmmap(dev, dev_priv->mmio_map);
144 --- a/drivers/gpu/drm/i915/i915_drv.h
145 +++ b/drivers/gpu/drm/i915/i915_drv.h
146 @@ -105,6 +105,12 @@ typedef struct drm_i915_private {
147 wait_queue_head_t irq_queue;
148 atomic_t irq_received;
149 atomic_t irq_emitted;
150 + /** Protects user_irq_refcount and irq_mask_reg */
151 + spinlock_t user_irq_lock;
152 + /** Refcount for i915_user_irq_get() versus i915_user_irq_put(). */
153 + int user_irq_refcount;
154 + /** Cached value of IMR to avoid reads in updating the bitfield */
157 int tex_lru_log_granularity;
158 int allow_batchbuffer;
159 --- a/drivers/gpu/drm/i915/i915_irq.c
160 +++ b/drivers/gpu/drm/i915/i915_irq.c
163 #define MAX_NOPID ((u32)~0)
165 +/** These are the interrupts used by the driver */
166 +#define I915_INTERRUPT_ENABLE_MASK (I915_USER_INTERRUPT | \
167 + I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT | \
168 + I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT)
171 +i915_enable_irq(drm_i915_private_t *dev_priv, u32 mask)
173 + if ((dev_priv->irq_mask_reg & mask) != 0) {
174 + dev_priv->irq_mask_reg &= ~mask;
175 + I915_WRITE(IMR, dev_priv->irq_mask_reg);
176 + (void) I915_READ(IMR);
181 +i915_disable_irq(drm_i915_private_t *dev_priv, u32 mask)
183 + if ((dev_priv->irq_mask_reg & mask) != mask) {
184 + dev_priv->irq_mask_reg |= mask;
185 + I915_WRITE(IMR, dev_priv->irq_mask_reg);
186 + (void) I915_READ(IMR);
191 * Emit blits for scheduled buffer swaps.
193 @@ -229,46 +254,50 @@ irqreturn_t i915_driver_irq_handler(DRM_
195 struct drm_device *dev = (struct drm_device *) arg;
196 drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
198 u32 pipea_stats, pipeb_stats;
201 pipea_stats = I915_READ(PIPEASTAT);
202 pipeb_stats = I915_READ(PIPEBSTAT);
204 - temp = I915_READ16(IIR);
206 - temp &= (I915_USER_INTERRUPT |
207 - I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT |
208 - I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT);
210 - DRM_DEBUG("%s flag=%08x\n", __FUNCTION__, temp);
213 + if (dev->pdev->msi_enabled)
214 + I915_WRITE(IMR, ~0);
215 + iir = I915_READ(IIR);
217 + DRM_DEBUG("iir=%08x\n", iir);
220 + if (dev->pdev->msi_enabled) {
221 + I915_WRITE(IMR, dev_priv->irq_mask_reg);
222 + (void) I915_READ(IMR);
227 - I915_WRITE16(IIR, temp);
228 - (void) I915_READ16(IIR);
229 - DRM_READMEMORYBARRIER();
230 + I915_WRITE(IIR, iir);
231 + if (dev->pdev->msi_enabled)
232 + I915_WRITE(IMR, dev_priv->irq_mask_reg);
233 + (void) I915_READ(IIR); /* Flush posted writes */
235 dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
237 - if (temp & I915_USER_INTERRUPT)
238 + if (iir & I915_USER_INTERRUPT)
239 DRM_WAKEUP(&dev_priv->irq_queue);
241 - if (temp & (I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT |
242 - I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT)) {
243 + if (iir & (I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT |
244 + I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT)) {
245 int vblank_pipe = dev_priv->vblank_pipe;
248 (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B))
249 == (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B)) {
250 - if (temp & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT)
251 + if (iir & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT)
252 atomic_inc(&dev->vbl_received);
253 - if (temp & I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT)
254 + if (iir & I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT)
255 atomic_inc(&dev->vbl_received2);
256 - } else if (((temp & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT) &&
257 + } else if (((iir & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT) &&
258 (vblank_pipe & DRM_I915_VBLANK_PIPE_A)) ||
259 - ((temp & I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT) &&
260 + ((iir & I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT) &&
261 (vblank_pipe & DRM_I915_VBLANK_PIPE_B)))
262 atomic_inc(&dev->vbl_received);
264 @@ -314,6 +343,27 @@ static int i915_emit_irq(struct drm_devi
265 return dev_priv->counter;
268 +static void i915_user_irq_get(struct drm_device *dev)
270 + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
272 + spin_lock(&dev_priv->user_irq_lock);
273 + if (dev->irq_enabled && (++dev_priv->user_irq_refcount == 1))
274 + i915_enable_irq(dev_priv, I915_USER_INTERRUPT);
275 + spin_unlock(&dev_priv->user_irq_lock);
278 +static void i915_user_irq_put(struct drm_device *dev)
280 + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
282 + spin_lock(&dev_priv->user_irq_lock);
283 + BUG_ON(dev->irq_enabled && dev_priv->user_irq_refcount <= 0);
284 + if (dev->irq_enabled && (--dev_priv->user_irq_refcount == 0))
285 + i915_disable_irq(dev_priv, I915_USER_INTERRUPT);
286 + spin_unlock(&dev_priv->user_irq_lock);
289 static int i915_wait_irq(struct drm_device * dev, int irq_nr)
291 drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
292 @@ -322,13 +372,17 @@ static int i915_wait_irq(struct drm_devi
293 DRM_DEBUG("irq_nr=%d breadcrumb=%d\n", irq_nr,
294 READ_BREADCRUMB(dev_priv));
296 - if (READ_BREADCRUMB(dev_priv) >= irq_nr)
297 + if (READ_BREADCRUMB(dev_priv) >= irq_nr) {
298 + dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
302 dev_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT;
304 + i915_user_irq_get(dev);
305 DRM_WAIT_ON(ret, dev_priv->irq_queue, 3 * DRM_HZ,
306 READ_BREADCRUMB(dev_priv) >= irq_nr);
307 + i915_user_irq_put(dev);
310 DRM_ERROR("EBUSY -- rec: %d emitted: %d\n",
311 @@ -413,20 +467,6 @@ int i915_irq_wait(struct drm_device *dev
312 return i915_wait_irq(dev, irqwait->irq_seq);
315 -static void i915_enable_interrupt (struct drm_device *dev)
317 - drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
321 - if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A)
322 - flag |= I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT;
323 - if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B)
324 - flag |= I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
326 - I915_WRITE16(IER, I915_USER_INTERRUPT | flag);
329 /* Set the vblank monitor pipe
331 int i915_vblank_pipe_set(struct drm_device *dev, void *data,
332 @@ -434,6 +474,7 @@ int i915_vblank_pipe_set(struct drm_devi
334 drm_i915_private_t *dev_priv = dev->dev_private;
335 drm_i915_vblank_pipe_t *pipe = data;
336 + u32 enable_mask = 0, disable_mask = 0;
339 DRM_ERROR("called with no initialization\n");
340 @@ -445,9 +486,20 @@ int i915_vblank_pipe_set(struct drm_devi
344 - dev_priv->vblank_pipe = pipe->pipe;
345 + if (pipe->pipe & DRM_I915_VBLANK_PIPE_A)
346 + enable_mask |= I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT;
348 + disable_mask |= I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT;
350 + if (pipe->pipe & DRM_I915_VBLANK_PIPE_B)
351 + enable_mask |= I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
353 + disable_mask |= I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
355 - i915_enable_interrupt (dev);
356 + i915_enable_irq(dev_priv, enable_mask);
357 + i915_disable_irq(dev_priv, disable_mask);
359 + dev_priv->vblank_pipe = pipe->pipe;
363 @@ -464,7 +516,7 @@ int i915_vblank_pipe_get(struct drm_devi
367 - flag = I915_READ(IER);
368 + flag = I915_READ(IMR);
370 if (flag & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT)
371 pipe->pipe |= DRM_I915_VBLANK_PIPE_A;
372 @@ -586,9 +638,9 @@ void i915_driver_irq_preinstall(struct d
374 drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
376 - I915_WRITE16(HWSTAM, 0xfffe);
377 - I915_WRITE16(IMR, 0x0);
378 - I915_WRITE16(IER, 0x0);
379 + I915_WRITE(HWSTAM, 0xfffe);
380 + I915_WRITE(IMR, 0x0);
381 + I915_WRITE(IER, 0x0);
384 void i915_driver_irq_postinstall(struct drm_device * dev)
385 @@ -601,7 +653,18 @@ void i915_driver_irq_postinstall(struct
387 if (!dev_priv->vblank_pipe)
388 dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A;
389 - i915_enable_interrupt(dev);
391 + /* Set initial unmasked IRQs to just the selected vblank pipes. */
392 + dev_priv->irq_mask_reg = ~0;
393 + if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A)
394 + dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT;
395 + if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B)
396 + dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
398 + I915_WRITE(IMR, dev_priv->irq_mask_reg);
399 + I915_WRITE(IER, I915_INTERRUPT_ENABLE_MASK);
400 + (void) I915_READ(IER);
402 DRM_INIT_WAITQUEUE(&dev_priv->irq_queue);
405 @@ -613,10 +676,10 @@ void i915_driver_irq_uninstall(struct dr
409 - I915_WRITE16(HWSTAM, 0xffff);
410 - I915_WRITE16(IMR, 0xffff);
411 - I915_WRITE16(IER, 0x0);
412 + I915_WRITE(HWSTAM, 0xffff);
413 + I915_WRITE(IMR, 0xffff);
414 + I915_WRITE(IER, 0x0);
416 - temp = I915_READ16(IIR);
417 - I915_WRITE16(IIR, temp);
418 + temp = I915_READ(IIR);
419 + I915_WRITE(IIR, temp);